CircuitPythonファームウエア
TRYGEARの各開発ボードには下記のCircuitPythonファームウェアを書き込んでご利用ください。
- TRYGEAR-DASH RP2350A
- Pico 2用のファームウェアをご利用ください
- https://circuitpython.org/board/raspberry_pi_pico2/
- TRYGEAR-VIZ RP2350B
- WeAct Studio RP2350B Core用のファームウェアをご利用ください
- https://circuitpython.org/board/weact_studio_rp2350b_core/
- TRYGEAR-NET ESP32-S3
- ESP32-S3-DevKitC-1用のファームウェアをご利用ください
- https://circuitpython.org/board/espressif_esp32s3_devkitc_1_n8r8/
また、ライブラリバンドルの/libフォルダの内容をボードの/libフォルダにコピーしてご利用ください。
フラッシュの容量は4MB以上あるので、必要なライブラリを選別することなく、すべてのライブラリをコピーできます。
端子割り当て
TRYGEARシリーズの開発ボードは、機能や性能により、いくつかのメーカーのMCUを採用しています。このため、使用しているMCUが異なる開発ボードごとに端子の呼び名や配置が異なっています。このため、特段の配慮がない場合、同じ機能のプログラムでも開発ボードごとに使用する端子名が異なってしまい、同じ機能のプログラムでも異なる開発ボードで共通に利用できないという問題があります。
TRYGEARシリーズの開発ボードでは、機能や配置が共通する端子の名前を異なる開発ボードでも同じ名前で使用できるようにライブラリファイル trygear.py を提供しています。trygear.pyを開発ボードの/libフォルダに格納し、プログラムの先頭にimport trygearと記述して使用してください。
from microcontroller import pin
import os
# 実行環境の自動判定
try:
_sysname = os.uname().sysname
except:
_sysname = "unknown"
if "rp2350b" in _sysname:
# --- Viz (RP2350B) Pin Definitions ---
BOARD_NAME = "VIZ RP2350B"
# Digital Pins (UNO Mapping)
D0, D1, D2, D3 = pin.GPIO1, pin.GPIO0, pin.GPIO2, pin.GPIO3
D4, D5, D6, D7 = pin.GPIO4, pin.GPIO5, pin.GPIO6, pin.GPIO7
D8, D9 = pin.GPIO22, pin.GPIO23
D10, D11, D12, D13 = pin.GPIO9, pin.GPIO11, pin.GPIO8, pin.GPIO10
# Analog Pins
A0, A1, A2, A3, A4, A5 = pin.GPIO40, pin.GPIO41, pin.GPIO42, pin.GPIO43, pin.GPIO46, pin.GPIO47
# Input Functions
SW1, SW2, SW3 = pin.GPIO29, pin.GPIO30, pin.GPIO31
# Output Functions
LED1 = D13 # SCK_S(GP13)と共用
LED2, LED3 = pin.GPIO32, pin.GPIO33
LED4 = pin.GPIO25
SRV1, SRV2 = LED2, LED3
RGB_LED = LED3
RGB_MX = pin.GPIO37
SPK = pin.GPIO26
# I2C Bus
SCL, SDA = pin.GPIO21, pin.GPIO20
SCL_A, SDA_A = A5, A4
# SPI Bus (Display: TFT_, Storage: SD_)
TFT_SCK, TFT_MOSI, TFT_MISO, TFT_CS = pin.GPIO38, pin.GPIO39, pin.GPIO36, pin.GPIO37
TFT_DC = TFT_MISO
SD_SCK, SD_MOSI, SD_MISO, SD_CS = D13, D11, D12, pin.GPIO24
# USB HOST
USBHP, USBHM = pin.GPIO34, pin.GPIO35
elif "ESP32S3" in _sysname:
# --- NET (ESP32-S3) Pin Definitions ---
BOARD_NAME = "NET ESP32-S3"
# Digital Pins (UNO Mapping)
D0, D1, D2, D3 = pin.GPIO44, pin.GPIO43, pin.GPIO1, pin.GPIO2
D4, D5, D6, D7 = pin.GPIO3, pin.GPIO38, pin.GPIO39, pin.GPIO40
D8, D9 = pin.GPIO41, pin.GPIO42
D10, D11, D12, D13 = pin.GPIO10, pin.GPIO11, pin.GPIO13, pin.GPIO12
# Analog Pins
A0, A1, A2, A3 = pin.GPIO4, pin.GPIO5, pin.GPIO6, pin.GPIO7
A4, A5 = pin.GPIO8, pin.GPIO9
# Input Functions
SW1, SW2, SW3 = pin.GPIO1, pin.GPIO47, pin.GPIO48
SW5 = pin.GPIO0
# Output Functions
LED1 = D13 # SCK_S(IO13)と共用
LED2, LED3 = pin.GPIO45, pin.GPIO46
SRV1, SRV2 = LED2, LED3
RGB_LED = LED3
RGB_MX = pin.GPIO18
SPK = pin.GPIO21
# I2C Bus
SCL = pin.GPIO9 # A5と共用
SDA = pin.GPIO8 # A4と共用
# SPI Bus (Display: TFT_, Storage: SD_)
TFT_SCK, TFT_MOSI, TFT_MISO, TFT_CS = pin.GPIO15, pin.GPIO16, pin.GPIO17, pin.GPIO18
TFT_DC = TFT_MISO
SD_SCK, SD_MOSI, SD_MISO, SD_CS = D13, D11, D12, pin.GPIO14
# CAN Bus
CAN_TX = pin.GPIO48
CAN_RX = pin.GPIO47
elif ("rp2350a" in _sysname) or ("rp2040" in _sysname):
# --- DASH (RP2350A) Pin Definitions ---
BOARD_NAME = "DASH/LITE/CORE RP2350A" if ("rp2350a" in _sysname) else "DASH/AXIS/LITE RP2040"
# Digital Pins (UNO Mapping)
D0, D1, D2, D3 = pin.GPIO1, pin.GPIO0, pin.GPIO2, pin.GPIO3
D4, D5, D6, D7 = pin.GPIO4, pin.GPIO5, pin.GPIO6, pin.GPIO7
D8, D9 = pin.GPIO8, pin.GPIO9
D10, D11, D12, D13 = pin.GPIO13, pin.GPIO11, pin.GPIO12, pin.GPIO10
# Analog Pins
A0, A1, A2, A3 = pin.GPIO26, pin.GPIO27, pin.GPIO28, pin.GPIO29
# Input Functions
SW1, SW2, SW3 = pin.GPIO14, pin.GPIO15, pin.GPIO22
# Output Functions
LED1 = D13 # SCK_S(GP13)と共用
LED2, LED3 = D0, D1
LED4 = pin.GPIO25
SRV1, SRV2 = LED2, LED3
RGB_LED = LED3
RGB_MX = pin.GPIO17
RGB_AXIS = pin.GPIO24
SPK = pin.GPIO23
# I2C Bus
SCL = pin.GPIO21 # A5と共用
SDA = pin.GPIO20 # S4と共用
# SPI Bus (Display: TFT_, Storage: SD_)
TFT_SCK, TFT_MOSI, TFT_MISO, TFT_CS = pin.GPIO18, pin.GPIO19, pin.GPIO16, pin.GPIO17
TFT_DC = TFT_MISO
SD_SCK, SD_MOSI, SD_MISO, SD_CS = D13, D11, D12, pin.GPIO24
else:
BOARD_NAME = "Unknown microcontroller"LED
LEDの点灯
TRYGEARには3,4個のLEDが装備されており、LED1,LED2,LED3などの名前が付けられています。それらのLEDをCircuitPythonで操作する方法を示します。
import trygear
import digitalio
import time
led = digitalio.DigitalInOut(trygear.LED1)
led.direction = digitalio.Direction.OUTPUT
led.value = True
time.sleep(1) # 1秒待つ
led.value = Falseこのプログラムは、単純に1秒だけLEDを点灯させています。
LEDの点滅
LED1を一定間隔で点滅するプログラムを示します。
import trygear
import digitalio
import time
led = digitalio.DigitalInOut(trygear.LED1)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(1) # 1秒待つ
led.value = False
time.sleep(1)LEDの明暗制御
LEDはPWMを使用して、単純に点灯、消灯だけでなく、明暗の度合いを制御できます。
PWMを使用してLED1の明暗を制御するプログラムを示します。
import trygear
import pwmio
import time
led = pwmio.PWMOut(trygear.LED1, frequency = 1000)
delta = 100
duty = 0
while True:
duty += delta
if duty == 65500:
delta = -100
elif duty == 0:
delta = 100
led.duty_cycle = duty # 0-65535
time.sleep(0.002)LEDをPWMで制御するように初期化し、Whileループの中で、PWMのデューティー比を増減させて、LEDの明暗を変化させています。
スイッチ
スイッチによるLEDの点灯
スイッチを押したときにLED1が点灯し、スイッチを離したときにLED1が消灯するプログラムを以下に示します。
import trygear
import digitalio
led = digitalio.DigitalInOut(trygear.LED1)
led.direction = digitalio.Direction.OUTPUT
sw1 = digitalio.DigitalInOut(trygear.SW1)
sw1.direction = digitalio.Direction.INPUT
while True:
led.value = not sw1.valueTRYGEARには、ユーザーがプログラムで使用できるスイッチ SW1, SW2, SW3が装備されており、それぞれtrygear.SW1,trygear.SW2, trygear.SW3で使用できます。
TRYGEARのSW1-SW3は負論理となっており、スイッチが押されたときにFalseもしくは0、離されている時にTrueもしくは1となります。このプログラムでは、正論理のLEDを接続している場合のもので、負論理のスイッチの読み取り結果を 'not' を付けて正論理に変換してLEDの引数としています。
なお、TRYGEAR-VIZとTRYGEAR-NETのSW1は、スイッチがプルアップされていない場合に何が起きるかを実験できるように、プルアップ用の抵抗が付けられていません。したがって、ふつうに使用する場合には、SW1だけは、MCUの内部プルアップを有効にする設定を行ってください。
圧電スピーカー
明るく光るLEDは出力装置として魅力的ですが、音を出す圧電スピーカーも電子工作では外せませんね。圧電スピーカーは圧電素子を使ったスピーカーで、TRYGEARに搭載されているものは直径が1センチ程度の小さなものです。
異なる周波数の音の出力
周波数が500Hzから1000Hzまで、10Hzごとに周波数を上げて音を出力するプログラム例を示します。(実際に出力される音(の高さ)は、圧電スピーカーの特性によるのか、ちょっと違和感があります。)
TRYGEARの圧電スピーカーを使用する場合のプログラム例を示します。TRYGEARで圧電スピーカーを使用する場合には、toneの引数として、圧電スピーカー接続した端子の端子名を指定してください。
import trygear
import simpleio
for hz in range(500,1001,10): # 100Hz -1000Hz
simpleio.tone(trygear.SPK, hz, 0.1)RP2040-SLIM-PLUS用の圧電スピーカーはGP23に接続されています。
スイッチによる音の出力
スイッチを押すと音が鳴るプログラム例を示します。
import trygear
import digitalio
import simpleio
sw1 = digitalio.DigitalInOut(trygear.SW1)
sw1.direction = digitalio.Direction.INPUT
while True:
if (sw1.value == 0): # スイッチが押されたとき
simpleio.tone(trygear.SPK, 400, 0.5)メロディーの出力
内臓タイマーを使用した音声出力例です。Arduinoでは、tone()関数の利用に相当します。音のPWM出力と書かれていることも多いのですが、基本的には周波数が重要でデューティーサイクルは問題ではないので、PWMというべきではないですね。
import trygear
import time
from simpleio import tone
# 音符の周波数 (単位: Hz)
NOTES = {
"C4": 261,
"D4": 294,
"E4": 329,
"F4": 349,
"G4": 392,
"A4": 440,
"B4": 493,
"C5": 523,
"REST": 0 # 休符
}
# 「カエルの歌」の楽譜 (音符と長さの組み合わせ)
# 長さは秒単位で指定
SONG = [
("C4", 0.5), ("D4", 0.5), ("E4", 0.5), ("F4", 0.5),
("E4", 0.5), ("D4", 0.5), ("C4", 0.5), ("REST", 0.5),
("E4", 0.5), ("F4", 0.5), ("G4", 0.5), ("A4", 0.5),
("G4", 0.5), ("F4", 0.5), ("E4", 0.5), ("REST", 0.5)
]
print("カエルの歌を演奏します。")
# 楽譜を演奏
while True:
for note, length in SONG:
if NOTES[note] == 0: # 休符の場合
print(f"休符: {length}s")
time.sleep(length) # 秒単位の休符
else:
print(f"再生中: {note}, 長さ: {length}s")
tone(trygear.SPK, NOTES[note], length) # 周波数と長さを指定 (秒単位)
time.sleep(1) # 演奏間に1秒の休止TFTディスプレイ
文字と基本図形の表示
import trygear
import busio
import displayio
from fourwire import FourWire
from adafruit_st7789 import ST7789
import terminalio
from adafruit_display_text import label
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.triangle import Triangle
# 以前のディスプレイリソースを解放
displayio.release_displays()
# SPIバスとピンの設定
spi = busio.SPI(clock=trygear.TFT_SCK, MOSI=trygear.TFT_MOSI)
display_bus = FourWire(spi, command=trygear.TFT_DC, chip_select=trygear.TFT_CS)
# ディスプレイの初期化(rowstart=80、rotation=270で画面を回転)
display = ST7789(display_bus, width=240, height=240, rotation=0, rowstart=80)
# メインの表示グループを作成
main_group = displayio.Group()
# 背景を黒に設定
background = Rect(0, 0, 240, 240, fill=0x000000) # 全体を黒背景に設定
main_group.append(background)
# 図形の表示
# 円(黄色)
circle = Circle(180, 50, 30, fill=0xFFFF00) # 中心座標(180,50), 半径30
main_group.append(circle)
# 四角形(シアン)
rectangle = Rect(100, 120, 60, 60, fill=0x00FFFF) # 左上座標(100,120), 幅60, 高さ60
main_group.append(rectangle)
# 三角形(マゼンタ)
triangle = Triangle(40, 200, 80, 240, 0, 240, fill=0xFF00FF) # 三角形の3頂点を指定
main_group.append(triangle)
# 文字列の表示(文字サイズを2-4倍に設定)
text_microfan = label.Label(terminalio.FONT, text="MicroFan", color=0xFF0000, x=20, y=30, scale=4) # 赤文字
text_rp2040 = label.Label(terminalio.FONT, text=trygear.BOARD_NAME, color=0x00FF00, x=0, y=90, scale=2) # 緑文字
text_circuitpython = label.Label(terminalio.FONT, text="CircuitPython", color=0x0000FF, x=5, y=150, scale=3) # 青文字
main_group.append(text_microfan)
main_group.append(text_rp2040)
main_group.append(text_circuitpython)
# ディスプレイに描画
display.root_group = main_group
# 無限ループで画面を維持
while True:
passボールが画面内を跳ね回るプログラム
import trygear
import busio
import displayio
from fourwire import FourWire
from adafruit_st7789 import ST7789
displayio.release_displays()
# SPIバスとピンの設定
spi = busio.SPI(clock=trygear.TFT_SCK, MOSI=trygear.TFT_MOSI)
display_bus = FourWire(spi, command=trygear.TFT_DC, chip_select=trygear.TFT_CS)
# display = ST7789(display_bus, width=320, height=170, colstart=35, rotation=90) # 1.9 inch 170x320
display = ST7789(display_bus, width=240, height=240, rowstart=80, rotation=0) # 1.54 inch 240x240
group = displayio.Group()
display.root_group = group
# ここまではグラフィックス利用の初期化で今後も共通
import simpleio
from adafruit_display_shapes.circle import Circle
class Ball(Circle):
# 初期座標、縦横速度、色を与える
def __init__(self, x0, y0, r, vx, vy, color):
super().__init__(x0, y0, r, fill=color, outline=0xFFFF00)
self.vx = vx
self.vy = vy
def tick(self):
# 縦横速度分移動して
self.x0 += self.vx
self.y0 += self.vy
# 画面の端に来たら速度を反転させて跳ね返る
if self.x0 <= self.r or self.x0 >= (display.width-1)-self.r:
simpleio.tone(trygear.SPK, 1000, 0.01)
self.vx = -self.vx
if self.y0 <= self.r or self.y0 >= (display.height-1)-self.r:
simpleio.tone(trygear.SPK, 500, 0.01)
self.vy = -self.vy
ball = Ball(100, 100, 10, 2, 5, 0x00FF00)
group.append(ball)
import time
while True:
group[0].tick()
time.sleep(0.02)HDMIコネクタを使用したTV/PCモニタ表示
TRYGEAR-VIZでは、HDMIコネクタが搭載されており、PicoDVIを使用して大画面表示を楽しむことができます。
文字と基本図形の表示
# 表示システムの初期化を行う
import displayio, picodvi, board, framebufferio
displayio.release_displays()
fb = picodvi.Framebuffer(
width=320, height=240, color_depth=8,
clk_dp=board.GP14, clk_dn=board.GP15,
red_dp=board.GP12, red_dn=board.GP13,
green_dp=board.GP18, green_dn=board.GP19,
blue_dp=board.GP16, blue_dn=board.GP17)
display = framebufferio.FramebufferDisplay(fb)
group = displayio.Group()
display.root_group = group
# 文字列の表示を行う
from adafruit_display_text import label
import terminalio
group.append(label.Label(terminalio.FONT, text="MicroFan", x=0, y=10, scale=2, color=0xFFFFFF))
group.append(label.Label(terminalio.FONT, text="TRYGEAR VIZ:PicoDVI", x=0, y=30, scale=2, color=0xFFFFFF))
# 図形の表示を行う
from adafruit_display_shapes.line import Line
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.triangle import Triangle
group.append(Line(0, 80, 320, 80, 0xFF00FF))
group.append(Rect(0, 120, 100, 100, fill=0xFF0000, outline=0xFFFF00))
group.append(Circle(160, 170, 50, fill=0x00FF00, outline=0xFFFF00))
group.append(Triangle(260, 120, 210, 220, 310, 220, fill=0x0000FF, outline=0xFFFF00))
ボールが画面内を跳ね回るプログラム
import displayio, picodvi, board, framebufferio
displayio.release_displays()
fb = picodvi.Framebuffer(
width=320, height=240, color_depth=8,
clk_dp=board.GP14, clk_dn=board.GP15, # 端子の配置はPICO-HDMI-PLUS用
red_dp=board.GP12, red_dn=board.GP13,
green_dp=board.GP18, green_dn=board.GP19,
blue_dp=board.GP16, blue_dn=board.GP17)
display = framebufferio.FramebufferDisplay(fb)
group = displayio.Group()
display.root_group = group
# ここまではグラフィックス利用の初期化で今後も共通
import simpleio
from adafruit_display_shapes.circle import Circle
class Ball(Circle):
# 初期座標、縦横速度、色を与える
def __init__(self, x0, y0, r, vx, vy, color):
super().__init__(x0, y0, r, fill=color, outline=0xFFFF00)
self.vx = vx
self.vy = vy
def tick(self):
# 縦横速度分移動して
self.x0 += self.vx
self.y0 += self.vy
# 画面の端に来たら速度を反転させて跳ね返る
if self.x0 <= self.r or self.x0 >= (display.width-1)-self.r:
simpleio.tone(board.GP26, 1000, 0.01)
self.vx = -self.vx
if self.y0 <= self.r or self.y0 >= (display.height-1)-self.r:
simpleio.tone(board.GP26, 500, 0.01)
self.vy = -self.vy
ball = Ball(100, 100, 10, 2, 5, 0x00FF00)
group.append(ball)
import time
while True:
group[0].tick()
time.sleep(0.02)OLEDディスプレイ
OLEDディスプレイへの文字表示
コントローラにSSD1306を使用した0.96インチのOLEDディスプレイに文字列を表示するプログラム例を示します。
import trygear
import board
import busio
import adafruit_ssd1306
i2c = busio.I2C(trygear.SCL, trygear.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
oled.text(trygear.BOARD_NAME, 0, 0, True) # 第二引数は文字を出力する横のピクセル座標、第三引数は縦のピクセル座標
oled.text('MicroFan', 32, 8, True)
oled.text('SW x3', 0, 16, True)
oled.text('LED x3 or x4', 0, 24, True)
oled.text('Piezo Speaker', 0, 32, True)
oled.text('RC Servo Conn. x2', 0, 40, True)
oled.text('MicroSD Socket', 0, 48, True)
oled.text('OLED Display', 0, 56, True)
oled.show()ssd1306での文字出力は、文字を出力する座標を毎回指定する必要があります。グラフィックス表示と合わせて文字表示をする場合には役立ちますが、文字だけを出力する場合には、少々面倒です。
また、文字もグラフィックスも、表示の指示をしただけではOLEDディスプレイの画面に表示されず、show()メソッドを呼び出した時点で初めてすべての表示指示が画面に反映されます。
OLEDディスプレイへのグラフィックス表示
OLEDディスプレイにグラフィックス表示するプログラム例です。
import trygear
import board
import busio
import adafruit_ssd1306
import time
i2c = busio.I2C(trygear.SCL, trygear.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
oled.fill(0) # 画面のクリア
for y in range(0,64,8):
oled.line(0,0,127,y,1) # 斜線
oled.show()
for x in range(127,0,-8):
oled.line(0,0,x,63,1) # 斜線
oled.show()
time.sleep(0.5)
oled.fill(0) # 画面のクリア
for n in range(0,32,4):
oled.rect(64-n*2,32-n,n*4,n*2,1) # 方形
oled.show()
time.sleep(0.5)
oled.fill(0) # 画面のクリア
for n in range(0,32,2):
oled.fill_rect(64-n*2,32-n,n*4,n*2,1) # 塗潰し方形
oled.show()
time.sleep(0.5)
oled.fill(0) # 画面のクリア
for n in range(2,32,2):
# oled.ellipse(64,32,n*4,n*2,1) # 楕円
oled.circle(64,32,n*2,1)
oled.show()RCサーボ
TRYGEARでは、基板左下の電源ソケットと電源関係のピンソケットの間に、CN2/D0, CN3/D1がRCサーボに接続しやすいようにそれぞれ3ピンのピンヘッダーで引き出されています。ここにRCサーボを接続して、簡単に実験を行うことができます。
CN2/D0はtrygear.SRV1, CN3/D1はtrygear.SRV2という端子名で使用します。ここでは、CN2にRCサーボを接続した例を示します。
RCサーボ用の電源は、USBもしくは電源ソケットから取得される5Vとなっています。開発ボードをUSBのみの接続で使用している場合には、RCサーボが大きめだと、大きな電流を使用してUSBに負荷がかかる場合があるので注意してください。
ここではRCサーボとして小型のSG90を使用します。
基本操作
import trygear
import time
import pwmio
from adafruit_motor import servo
pwm = pwmio.PWMOut(trygear.SRV1, duty_cycle=2 ** 15, frequency=50)
# actuation_range: int = 180, min_pulse: int = 750, max_pulse: int = 2250
my_servo = servo.Servo(pwm)
while True:
for angle in range(0, 180, 2): # 0 - 180 degrees, 5 degrees at a time.
my_servo.angle = angle
time.sleep(0.02)
for angle in range(180, 0, -2): # 180 - 0 degrees, 5 degrees at a time.
my_servo.angle = angle
time.sleep(0.02)使用しているRCサーボが上記のデフォルト設定のプログラムで、180度の回転を繰り返してくれればいいのですが、多くの場合、回転角が偏っていたり、回転角が180度に至らないのではないかと思います。
多くの場合、回転のどちらかの端でモーターの駆動音が少し詰まったような音がするのではないでしょうか?
パルス幅のキャリブレーション
RCサーボが適切に回転してくれるようにするには、それぞれのRCサーボに合わせてPWMの設定をキャリブレーション:調整してやる必要があります。
このため、RCサーボの回転角がライブラリのデフォルトの180度に合致する場合には、PWMのHIGHのパルス幅の範囲を調整する必要があります。
以下の例は、SG90というサーボに合わせたものです。3個のサーボで試してみましたが、同じ設定で問題なく動きました。ただ、SG90という名前で、どうやら複数のメーカーの同等品(信号等の規格が統一された互換品かどうかは不明)が何種類か流通しているようなので、メーカーが異なると、同じ型番でも設定を変更する必要があるかもしれません。
import board
import time
import pwmio
from adafruit_motor import servo
pwm = pwmio.PWMOut(board.GP0, duty_cycle=2 ** 15, frequency=50)
# SG90
my_servo = servo.Servo(pwm, min_pulse = 500, max_pulse = 2150)
while True:
for angle in range(0, 180, 5): # 0 - 180 degrees, 5 degrees at a time.
my_servo.angle = angle
time.sleep(0.05)
for angle in range(180, 0, -5): # 180 - 0 degrees, 5 degrees at a time.
my_servo.angle = angle
time.sleep(0.05)スイッチによる操作
RP2040-UNO-PLUSにはスイッチが2つ装備されているので、それを利用してRCサーボを操作できるようにプログラムを少し変更してみましょう。
import trygear
import digitalio
import time
import pwmio
from adafruit_motor import servo
sw1 = digitalio.DigitalInOut(trygear.SW1) # 初期状態は入力用
# sw1.pull = digitalio.Pull.UP # VIZ/NETではプルアップされてないので必要
sw3 = digitalio.DigitalInOut(trygear.SW3)
pwm = pwmio.PWMOut(trygear.SRV1, duty_cycle=2 ** 15, frequency=50)
# SG90
my_servo = servo.Servo(pwm, min_pulse = 500, max_pulse = 2100)
angle = 90
while True:
if sw1.value == False:
angle -= 2
if angle < 0:
angle = 0
if sw3.value == False:
angle += 2
if angle > 180:
angle = 180
my_servo.angle = angle
time.sleep(0.02)カラーLED
WS2812カラーLED
RGB表示
3個のWS2812タイプのカラーLEDに赤、緑、青色を出力するプログラム例です。
各LEDの明るさは、0-255の範囲で指定できますが、LEDの点灯確認目的であれば、10程度の明るさ指定でも十分な明るさで発光します。
# Ws2812 カラーLEDの点灯
import trygear
import board
import neopixel
rgb = neopixel.NeoPixel(trygear.RGB_LED, 3)
rgb[0] = (10,0,0) # 赤
rgb[1] = (0,10,0) # 緑
rgb[2] = (0,0,10) # 青import trygear
import digitalio
import board
import time
import neopixel
brt = 20
rgb = neopixel.NeoPixel(trygear.SD_CS, 4)
sw1 = digitalio.DigitalInOut(trygear.SW1)
sw1.direction = digitalio.Direction.INPUT
sw2 = digitalio.DigitalInOut(trygear.SW2)
sw2.direction = digitalio.Direction.INPUT
sw3 = digitalio.DigitalInOut(trygear.SW3)
sw3.direction = digitalio.Direction.INPUT
while True:
r, g, b = 0, 0, 0
if sw1.value == False:
r = brt
if sw2.value == False:
g = brt
if sw3.value == False:
b = brt
rgb[0] = (r, 0, 0)
rgb[1] = (0, g, 0)
rgb[2] = (0, 0, b)
rgb[3] = (r, g, b)
time.sleep(0.1)import trygear
import digitalio
import board
import time
import neopixel
brt = 10
rgb = neopixel.NeoPixel(trygear.RGB_LED, 13)
sw1 = digitalio.DigitalInOut(trygear.SW1)
sw1.direction = digitalio.Direction.INPUT
sw2 = digitalio.DigitalInOut(trygear.SW2)
sw2.direction = digitalio.Direction.INPUT
sw3 = digitalio.DigitalInOut(trygear.SW3)
sw3.direction = digitalio.Direction.INPUT
while True:
r, g, b = 0, 0, 0
if sw1.value == False:
r = brt
if sw2.value == False:
g = brt
if sw3.value == False:
b = brt
rgb[0:3] = [(r, 0, 0)] * 3
rgb[3:6] = [(0, g, 0)] * 3
rgb[6:9] = [(0, 0, b)] * 3
rgb[9:13] = [(r, g, b)] * 4
time.sleep(0.1)温度・湿度センサー
気温・湿度センサーAHT20のドライバーに関しては以下のページをご参照ください。
気温・湿度・明るさ表示
OLEDディスプレイに気温・湿度センサーAHT20で得られた気温と湿度、明るさセンサー(フォトトランジスタ)で得られた明るさを表示するプログラム例です。
AHT20はI2Cで接続されており、OLEDディスプレイと同じGP20,GP21にに接続されています。
明るさは、フォトトランジスタの出力電圧をAD変換で読み取って表示しています。明るさの範囲は0-1で、1に近い方が明るい状態を表します。
import trygear
import board
import busio
import analogio
import microcontroller
import adafruit_ahtx0
import adafruit_ssd1306
import time
i2c = busio.I2C(trygear.SCL, trygear.SDA)
aht20 = adafruit_ahtx0.AHTx0(i2c)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
brt = analogio.AnalogIn(board.A3)
while True:
oled.fill(0)
oled.text('CORE TEMP: {:.1f}\''.format(microcontroller.cpu.temperature),0,0,True)
oled.text('TEMP: {:.1f}\''.format(aht20.temperature),0,16,True)
oled.text('HUM: {:.1f}%'.format(aht20.relative_humidity),0,32,True)
oled.text('BRT: {:.3f}'.format(brt.value/65535),0,48,True)
oled.show()
time.sleep(0.1)気圧センサー
明るさセンサー
加速度センサー
MicroSD
使用するMicroSDはFATでフォーマットされている必要があります。
MicroSDのファイルリスト
MicroSDに記録されているファイルのリストをシリアルモニタに出力するスケッチです。フォルダがある場合には、その中身も再帰的に表示します。
import trygear
import os
import busio
import digitalio
import storage
import adafruit_sdcard
print("--- SD Card File Listing ---")
# 1. 指定されたピン配置でSPIバスとCS(チップセレクト)ピンを初期化
spi = busio.SPI(clock=trygear.SD_SCK, MOSI=trygear.SD_MOSI, MISO=trygear.SD_MISO)
cs = digitalio.DigitalInOut(trygear.SD_CS)
# 2. SDカードの初期化とマウント
try:
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
print("SD Card mounted successfully.")
except Exception as e:
print("Error: SD Card initialization failed!", e)
raise SystemExit
# 3. ファイルとフォルダを再帰的にリストアップする関数
def list_directory(path, depth=0):
try:
# 指定されたパス内のファイル・フォルダ一覧を取得
for entry in os.listdir(path):
full_path = path + "/" + entry
indent = " " * depth # 階層に応じたインデント
# エントリの情報を取得(フォルダかファイルかを判定するため)
stat = os.stat(full_path)
is_dir = (stat[0] & 0o170000) == 0o040000 # フォルダかどうかのビット判定
if is_dir:
print(f"{indent}[DIR] {entry}")
# フォルダの場合は、さらにその中身をスキャン(再帰呼び出し)
list_directory(full_path, depth + 1)
else:
size = stat[6] # ファイルサイズ(バイト)
print(f"{indent}- {entry} ({size} bytes)")
except Exception as e:
print(f"Error reading {path}: {e}")
# 4. ルート(/sd)からリストアップを開始
print("\n--- File List ---")
list_directory("/sd")
print("-----------------")MicroSDのファイルの読み書き
MicroSDにテキストファイルを作成し、その内容を読みだしてシリアルモニタに出力するスケッチです。
import trygear
import busio
import digitalio
import storage
import adafruit_sdcard
print("--- SD Card Text Writer & Reader ---")
# 1. 指定されたピン配置でSPIバスとCS(チップセレクト)ピンを初期化
spi = busio.SPI(clock=trygear.SD_SCK, MOSI=trygear.SD_MOSI, MISO=trygear.SD_MISO)
cs = digitalio.DigitalInOut(trygear.SD_CS)
# 2. SDカードの初期化とマウント
try:
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
print("SD Card mounted successfully at /sd")
except Exception as e:
print("Error: SD Card initialization failed!", e)
raise SystemExit
# 3. /sd/text.txt へ書き込み (モード 'w' で自動的に新規作成または上書き)
try:
with open("/sd/text.txt", "w", encoding="utf-8") as write_file:
# 1行目: 数字全て
write_file.write("0123456789\n")
# 2行目: 英小文字全て
write_file.write("abcdefghijklmnopqrstuvwxyz\n")
# 3行目: 英大文字全て
write_file.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n")
print("Saved text to '/sd/text.txt'.")
except Exception as e:
print("Error: Failed to write to file.", e)
# 4. /sd/text.txt から読み出してシリアルに出力
print("\n--- Reading from '/sd/text.txt' ---")
try:
with open("/sd/text.txt", "r", encoding="utf-8") as read_file:
for line in read_file:
# 各行の末尾の改行コードを二重に出力しないよう stripせずに printのendで制御
print(line, end="")
print("\n--------------------------------")
except Exception as e:
print("Error: Failed to read from file.", e)
USBデバイス
USBホスト
文字端末(のもと)の作成
TRYGEAR-VIZ-RP2350Bを使用すると、PCキーボードを入力とし、TV/PCモニターを出力とするスタンドアロンの超小型PCを作ることができます。出力はTFTディスプレイでも構いませんが。
まずはその入り口として、JISキーボードの入力内容をモニターに出力するプログラムを示します。
とても基本的な機能のみだけどね。
ここでは、下記の3つプログラムを組み合わせたプログラム例を紹介します。
- USBホスト機能を利用したPCキーボードの読み取りプログラム
- PicoDVIを使用した文字のTV/PCモニター出力プログラム
- 上記の2つを組み合わせた簡易端末
USBホストのPCキーボードの入力機能
読み取るのは、PCキーボード中央部のアルファベット数字キー周りのみで、今回はテンキーやファンクションキーは対象外。
シフトキーは使えます。
import board
import time
import usb.core
import usb_host
class JISKeyboard:
def __init__(self, dp=board.GP34, dm=board.GP35):
# USBホストの初期化
self.host_port = usb_host.Port(dp, dm)
self.keyboard = None
self.buffer = bytearray(8)
self.last_keycode = 0
# JIS配列マップ
self.HID_MAP = {
0x04: "a", 0x05: "b", 0x06: "c", 0x07: "d", 0x08: "e", 0x09: "f", 0x0a: "g",
0x0b: "h", 0x0c: "i", 0x0d: "j", 0x0e: "k", 0x0f: "l", 0x10: "m", 0x11: "n",
0x12: "o", 0x13: "p", 0x14: "q", 0x15: "r", 0x16: "s", 0x17: "t", 0x18: "u",
0x19: "v", 0x1a: "w", 0x1b: "x", 0x1c: "y", 0x1d: "z",
0x1e: "1", 0x1f: "2", 0x20: "3", 0x21: "4", 0x22: "5", 0x23: "6", 0x24: "7",
0x25: "8", 0x26: "9", 0x27: "0",
0x28: "\n", 0x2C: " ",
0x2d: "-", 0x2e: "^", 0x2f: "@", 0x30: "[",
0x32: "]", 0x33: ";", 0x34: ":", 0x35: "`",
0x36: ",", 0x37: ".", 0x38: "/", 0x87: "\\"
}
self.HID_SHIFT_MAP = {
0x04: "A", 0x05: "B", 0x06: "C", 0x07: "D", 0x08: "E", 0x09: "F", 0x0a: "G",
0x0b: "H", 0x0c: "I", 0x0d: "J", 0x0e: "K", 0x0f: "L", 0x10: "M", 0x11: "N",
0x12: "O", 0x13: "P", 0x14: "Q", 0x15: "R", 0x16: "S", 0x17: "T", 0x18: "U",
0x19: "V", 0x1a: "W", 0x1b: "X", 0x1c: "Y", 0x1d: "Z",
0x1e: "!", 0x1f: '"', 0x20: "#", 0x21: "$", 0x22: "%", 0x23: "&", 0x24: "'",
0x25: "(", 0x26: ")", 0x27: "~",
0x28: "\n", 0x2C: " ",
0x2d: "=", 0x2e: "~", 0x2f: "`", 0x30: "{",
0x32: "}", 0x33: "+", 0x34: "*", 0x35: "~",
0x36: "<", 0x37: ">", 0x38: "?", 0x87: "_"
}
def _find_keyboard(self):
devices = list(usb.core.find(find_all=True))
if not devices:
return None
dev = devices[-1]
keyboard = usb.core.find(idVendor=dev.idVendor, idProduct=dev.idProduct)
if keyboard is not None:
try:
keyboard.set_configuration()
except Exception:
pass
return keyboard
def read_char(self):
"""文字が入力されたらその文字を返す。入力がなければNoneを返す。"""
if self.keyboard is None:
self.keyboard = self._find_keyboard()
if self.keyboard is None:
return None
try:
num_bytes = self.keyboard.read(0x81, self.buffer)
if num_bytes > 0:
is_shift = (self.buffer[0] & 0x22) != 0
current_keycode = 0
for code in self.buffer[2:8]:
if code != 0:
current_keycode = code
break
# キーの変更検知(チャタリング防止)
if current_keycode != self.last_keycode:
self.last_keycode = current_keycode
if current_keycode != 0:
if is_shift and current_keycode in self.HID_SHIFT_MAP:
return self.HID_SHIFT_MAP[current_keycode]
elif not is_shift and current_keycode in self.HID_MAP:
return self.HID_MAP[current_keycode]
except Exception:
self.keyboard = None
self.last_keycode = 0
return NonePicoDVIのモニター出力機能
出力された文字をTV/PCモニターに表示します。
エンター(ここでは'\n')が入力されると、改行されます。
改行時に画面がスクロールアップします。
import board
import displayio
import framebufferio
import picodvi
import terminalio
class DVIConsole:
def __init__(self, width=640, height=480):
displayio.release_displays()
# 1. PicoDVIの初期化
self.fb = picodvi.Framebuffer(
width=width, height=height, color_depth=8,
clk_dp=board.GP14, clk_dn=board.GP15,
red_dp=board.GP12, red_dn=board.GP13,
green_dp=board.GP18, green_dn=board.GP19,
blue_dp=board.GP16, blue_dn=board.GP17
)
self.display = framebufferio.FramebufferDisplay(self.fb, rotation=0)
# 2. 画面に収まる文字の行列数を計算
font = terminalio.FONT
font_w, font_h = font.get_bounding_box()
grid_w = width // font_w
grid_h = height // font_h
# 3. ターミナル用のカラーパレットを作成(これが10.xの必須要素)
# [0]を背景色(黒)、[1]を文字色(白)にします
palette = displayio.Palette(2)
palette[0] = 0x000000
palette[1] = 0xFFFFFF
# 4. scroll_area となる TileGrid の生成
# これが Terminal クラスに要求されている最初の引数です
self.tile_grid = displayio.TileGrid(
font.bitmap,
pixel_shader=palette,
width=grid_w,
height=grid_h,
tile_width=font_w,
tile_height=font_h
)
# 5. Terminal を初期化
self.term = terminalio.Terminal(scroll_area=self.tile_grid, font=font)
# 6. グループへ追加して画面表示
self.group = displayio.Group()
self.group.append(self.tile_grid)
self.display.root_group = self.group
def write(self, text):
"""文字列をモニターに出力(改行やスクロールも自動処理)"""
self.term.write(text)統合コード
上記の2つのモジュールをインポートし、PCキーボードから読み込んだ文字をTV/PCモニターに出力するようにしてやると出来上がり。
合計3個のプログラムファイルを開発ボードのファイルシステムにコピーして利用してください。
動いて当たり前だけどちょっと感動。まだ穴だらけですが。。。
画面の解像度を上げると文字が小さくなってインパクトが弱かったので、下記の統合コードでは、画面の解像度を低くしています。
入力と出力をUSBシリアルやUARTに接続すると、簡易端末の出来上がり。目指せVT100(古いですね。)
入力と出力を開発ボード内部のREPLループに接続してやれば、超小型Python PCの出来上がり。
import time
import sys
from jis_keyboard import JISKeyboard
from dvi_console import DVIConsole
print("=== システム起動 ===")
# 各モジュール(部品)をインスタンス化
# これにより、内部の複雑な初期化やピン配置は隠蔽されます
console = DVIConsole(360,200)
keyboard = JISKeyboard()
console.write("JIS Keyboard Test Console\n")
console.write("Ready to Type...\n\n")
while True:
# キーボードモジュールから文字を1文字取得
char = keyboard.read_char()
if char is not None:
# 1. PicoDVIモニターに出力
console.write(char)
# 2. REPL(PC側のシリアルコンソール)にも同時に出力
sys.stdout.write(char)
time.sleep(0.01)