UMEHOSHI ITA TOP PAGE
[Raspberry Pi 3 Model A+]と[UMEHOSHI ITA]を乗せたモータ付き台車
このページで示した[Raspberry Pi]のインストールと[UMEHOSHI ITA]を制御できた後のページです。
Raspberry Pi 3 Model A+]に接続した[UMEHOSHI ITA]基板を乗せるモータ付き台車の制作
[Raspberry Pi 3 Model A+]と[UMEHOSHI ITA]を乗せたモータ付き台車の
拡張ボードの回路結線図&配置図
(上の基板写真のクリックで裏面も見ることができます。)
上記では、BNO055使用の下記の9軸センサーフュージョンモジュールキット(秋月電子さんから購入)を、さかさまに取り付けています。
上記の配置図内で示したように、さかさまに取り付けることによってX軸がロボット前方に向く方向になります。

一般にロボット工学系・ROS(Robot Operating System)系では、ロボット座標が次のように合わせます。
上記の配置図は、下位のロボット座標に方向が合うようにセンサーを配置した結果です。
| 方向 | 意味 | 推奨センサー軸 |
| 前方(前進方向) | ロボットが進む方向 | +X軸 |
| 左方向 | ロボットの左側 | +Y軸 |
| 上方向 | 地面から上(重力に逆らう方向) | +Z軸 |
また、VL53L1X使用 レーザー測距センサーモジュール(秋月電子さんから購入)を、手持ちパーツを加工したLアングルで基板に取り付けしています。
接続は、3.3VをAE-VL53L1Xに供給しています。
AE-VL53L1X内部では、LP5907MFX-2.8/NOPBで2.8vにしてVL53L1Xに電源供給しており、
SDA/SCLラインはFXMA2102L8Xのレベルシフターを使って3.3vのRaspberry Piと安全接続できますが、
SHUTピンは2.8vにプルアップされているだけなので、Raspberry PiとSHUTは接続しませんでした。
(Raspberry Piの3.3V出力をVL53L1Xチップの2.8Vロジックレベル入力に接続すると、定格を超える電圧が入力され最悪損傷の可能性がある?)
また、0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色(秋月電子さんから購入)を取り付けしています。

(このモジュールで使われている制御チップはSSD1306です)
GPIO5, 6, 16, 17 をタクトスイッチに繋げる
GPIO5, 6, 16, 17にタクトスイッチSW1, SW2, SW3, SW4に接続しました。
(GPIO6, GPIO16, GPIO17はプルアップ付き入力に設定して使う予定です。)
GPIO5の接続しているSW1(ダイダイ)を次のように設定してシャットダウン用にしました。
SSHでログイン後、sudo nano /boot/config.txt の操作で編集状態にします。
そして、このテキストの最後に「dtoverlay=gpio-shutdown,gpio_pin=5」の行を追加します。
nanoの保存、終了の操作後に、sudo rebootで再起動します。
以上で、GPIO5のSW1(ダイダイ)スイッチ操作でシャットダウンできるようしました。
上記の回路結線図で示しているように、40ピンのGPIO21にLEDを付けています。
これを出力ピンにして、Hiに設定すれば点灯します。
次のプログラム(swtest.py)は、これら検証用で起動時にLEDを点灯させ、
SW2、SW3、SW4 と順番に押して、LEDを消灯、点灯、消灯させます。
#!/usr/bin/python3
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT) # GPIO21を出力に設定
GPIO.output(21, GPIO.HIGH) # ON(3.3V)
for no in [6, 16, 17]:
GPIO.setup(no, GPIO.IN, pull_up_down=GPIO.PUD_UP) # プルアップ付き入力
while True:
if GPIO.input(6) == GPIO.LOW: break # SW2スイッチが押されるのを待つ
GPIO.output(21, GPIO.LOW) # OFF
while True:
if GPIO.input(16) == GPIO.LOW: break # SW3スイッチが押されるのを待つ
GPIO.output(21, GPIO.HIGH) # ON(3.3V)
while True:
if GPIO.input(17) == GPIO.LOW: break # SW4スイッチが押されるのを待つ
GPIO.cleanup() # ピンの初期化
(Python 3.9.2 (default, Mar 12 2021, 04:06:34)[GCC 10.2.1 20210110] on linux)で使っています
9軸センサーBNO055モジュールキット使用のフュージョンモードの動作検証プログラム
Raspbarry PIのGPIO2/SDAとGPIO3/SCLを使ってBNO055とI2Cの接続を行っています。
Raspbarry で、sudo raspi-configのコマンドで、次の操作で、I2Cを使えるようにします。
→ 「Interface Options」 → 「I2C」 → 「Enable」 変更した後、再起動するとよいでしょう。
次の操作で、I2Cが動作しているか、検証できます。
suzuki@raspberrypi:~ $ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- 28 29 -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
これで、センサーのアドレスの0x28が見えればOKです。
(後から取り付けた距離センサー用のアドレスが29で、
SSD1306使用の128×64ドット有機ELディスプレイのアドレスが3cです。)
取り付けた9軸センサーのBNO055は、内部MPUを使って各センサー情報を統合して簡単に高度な処理結果を得ることができるFusionモードと、
そうでない素データを取得するモードがある。
今回はFusionモードを次のプログラム(bno055_test.py)で試した。
#!/usr/bin/python3
import smbus # I2C通信をPythonから簡単に扱うためのモジュール
import time
BNO055_ADDRESS = 0x28 # BNO055のI2Cアドレス(ADRピンがGNDなら0x28、VDDなら0x29になります)
BNO055_OPR_MODE = 0x3D # 動作モードを設定するためのレジスタ
BNO055_EULER_H_LSB = 0x1A # オイラー角(方位・ロール・ピッチ)のデータが始まるアドレス
bus = smbus.SMBus(1)# 引数の1でRaspberry PiのボードGPIO2: SDA、GPIO3: SCLを指定
bus.write_byte_data(BNO055_ADDRESS, BNO055_OPR_MODE, 0x00) # 設定変更(OPR_MODE)でCONFIGモードに切り替える
time.sleep(0.05)
# センサーをリセット(0x3FのSYS_TRIGGERレジスタのビット7をセット)
bus.write_byte_data(BNO055_ADDRESS,0x3F, 0x20)
time.sleep(0.7) # リセット後は再起動まで時間がかかる
# 出力単位(UNIT_SEL)を設定(0x00で「角度=度(°)」単位)
bus.write_byte_data(BNO055_ADDRESS,0x3B, 0x00)
# センサーフュージョンを有効にするNDOFモードに変更
bus.write_byte_data(BNO055_ADDRESS,BNO055_OPR_MODE, 0x0C) # NDOFモードへ
time.sleep(0.05)
print("BNO055をNDOF(Fusion)モードで初期化しました。(自動的にキャリブレーションが実行)")
def to_signed(val):
"""16ビット値を符号付き整数に変換"""
if val >= 0x8000:
val -= 0x10000
return val
def read_euler():
data = bus.read_i2c_block_data(BNO055_ADDRESS, BNO055_EULER_H_LSB, 6)
# 各要素が 1 バイト(0?255)の整数を6個のリストで得られる。(1 LSB = 1/16 度)
# データはリトルエンディアン形式(下位→上位の順)
heading = (data[1] << 8) | data[0] # 方位角(北基準のYAW)
roll = (data[3] << 8) | data[2] # ロール角(左右の傾き)
pitch = (data[5] << 8) | data[4] # ピッチ角(前後の傾き)
# ロールとピッチは符号付き
roll = to_signed(roll)
pitch = to_signed(pitch)
# スケーリング(1 LSB = 1/16 度)
heading = heading / 16.0 #
roll = roll / 16.0
pitch = pitch / 16.0
return heading, roll, pitch
try:
while True:
h, r, p = read_euler()
print(f"Heading: {h:7.2f}°, Roll: {r:7.2f}°, Pitch: {p:7.2f}°")
time.sleep(0.2)
except KeyboardInterrupt:
print("\n終了しました。")
Heading: 189.81°, Roll: -1.38°, Pitch: -176.75°
Heading: 189.81°, Roll: -1.38°, Pitch: -176.75°
この実行結果は、キャリブレーション操作後にロボットの前方を北に向けて、ロボットを水平に置いた状態のセンサー取得情報です。
(ロボットの前方はセンサーのX軸方向に一致するように取り付けています。)
キャリブレーションとは、測定機器やセンサーなどの誤差を修正し、正確な値を出力するように調整する作業で、その操作は後述します。
ロボットが右旋回すると Heading が増加し、左旋回すると Heading が減少する結果が得られた。
これからBNO055 の +X軸 はロボットの前方を正しく向いている。
その後下記表に示すように、各回転操作からセンサーの座標軸がロボット座標軸と在って正しい取り付けができたと判断している。
BNO055 の標準オイラー角定義とNDOFモードに於ける上記プログラムの出力値の意味
| 角度 | 回転軸 | 回転方向(+) | 説明 |
| Heading (Yaw) | Z軸まわり | 反時計回り(上から見て右回転) | ロボットが右旋回で値が増えるならOK(右が+Z) |
| Roll | X軸まわり | 前方に向かって右手の親指を伸ばした向き | 左に傾けると+、右に傾けるとーが正しい |
| Pitch | Y軸まわり | ロボットの前方を下げる方向が+ | 前のめりで+、上向きでー |
さて、Heading (Yaw)、Roll、Pitchを得るレジスタと、目標となる出力範囲は、次の通りです。
| 項目 | レジスタ | 出力範囲 |
| ヘディング (Heading) | 0x1A〜0x1B | 0〜360°(X軸を北に合わせる時に、0又は360度) |
| ロール (Roll) | 0x1C〜0x1D | -90〜+90°(水平時は0度) |
| ピッチ (Pitch) | 0x1E〜0x1F | -180〜+180°(水平時は0度) |
対して、実験するとX軸を北に合わせた時に、Headingが189.81でした。
これが0になるようにオフセット調整します。次のように調整です。
heading = heading - 189.81 # headingズレ補正
heading = heading if heading >= 0 else heading + 360
同様に、水平時にPitchが -176.75°という実験結果なので、これが0になるように次のオフセット調整を行います。
pitch = pitch - -176.75 # pitchズレ補正
if pitch > 180:
pitch = -(360 - pitch)
elif pitch < -180:
pitch = -(-360 - pitch)
return heading, roll, pitch
以上の調整で、次のようにほぼ希望の結果が得られました。
Heading: 0.00°, Roll: -1.38°, Pitch: 0.00°
Heading: 0.00°, Roll: -1.38°, Pitch: 0.00°
前述のオフセット調整は、実験値で得られた調整値を埋め込みましたが、
将来的に初期起動には調整値を記録し、次回からそれを使って調整する仕組みにすべきと考えます。
前述で示したプログラムは、
BNO055をFusionモード(内部MPUを使って各センサー情報を統合して処理結果を得るモード)に属するNDOFモードで起動しています。
NDOFモードは、初期化時にOPR_MODE レジスタ (0x3D) に 0x0Cを設定することで行っていますが、
このモードでは、自動キャリブレーションを実行する状態になります。
(キャリブレーションとは、測定誤差を修正し、正しい測定できるように調整する校正の挙動です。)
「NDOFモードにしただけ」では自動で完了はしません。ユーザーが次の3項目でセンサーを動かすことにより完了します。
- MAG(磁気):8の字運動を行う必要があります。
- ACC(加速度):6方向で静止状態にする。
- GYR(ジャイロ):数秒間静止。
NDOFモードでは、自動キャリブレーションが行われます。そしてこのデータは電源投入後に常にリセットされます。
それで、すでに正しくキャリブレーション済みの値を保存しておき、起動時にそれを BNO055に再書き込みして使い回すことにします。
起動時のキャリブレーション実行で、それが完了したら その情報を保存する以下のプログラム(bno055_calib_write.py)を作りました。
(時々必要と思われる時に実行させる予定のコードです。)
#!/usr/bin/python3
import smbus # I2C通信をPythonから簡単に扱うためのモジュール
import time
BNO055_ADDRESS = 0x28 # BNO055のI2Cアドレス(ADRピンがGNDなら0x28、VDDなら0x29になります)
BNO055_OPR_MODE = 0x3D # 動作モードを設定するためのレジスタ
BNO055_EULER_H_LSB = 0x1A # オイラー角(方位・ロール・ピッチ)のデータが始まるアドレス
bus = smbus.SMBus(1)# 引数の1でRaspberry PiのボードGPIO2: SDA、GPIO3: SCLを指定
bus.write_byte_data(BNO055_ADDRESS, BNO055_OPR_MODE, 0x00) # 設定変更(OPR_MODE)でCONFIGモードに切り替える
time.sleep(0.05)
# センサーをリセット(0x3FのSYS_TRIGGERレジスタのビット7をセット)
bus.write_byte_data(BNO055_ADDRESS,0x3F, 0x20)
time.sleep(0.7) # リセット後は再起動まで時間がかかる
# 出力単位(UNIT_SEL)を設定(0x00で「角度=度(°)」単位)
bus.write_byte_data(BNO055_ADDRESS,0x3B, 0x00)
# センサーフュージョンを有効にするNDOFモードに変更
bus.write_byte_data(BNO055_ADDRESS,BNO055_OPR_MODE, 0x0C) # NDOFモードへ
time.sleep(0.05)
print("BNO055をNDOF(Fusion)モードで初期化しました。(自動的にキャリブレーションが実行)")
while True: # NDOFモードで全てのセンサー(SYS, GYR, ACC, MAG)が 3 になるまで動かす。
cal = bus.read_byte_data(BNO055_ADDRESS, 0x35)
sys = (cal >> 6) & 0x03
gyr = (cal >> 4) & 0x03
acc = (cal >> 2) & 0x03
mag = (cal >> 0) & 0x03
print(f"SYS:{sys}, GYR:{gyr}, ACC:{acc}, MAG:{mag}")
time.sleep(0.5)
if sys == 3 and gyr == 3 and acc == 3 and mag == 3 : break # キャリブレーション完了?
# 各値が 3 になれば完全キャリブレーション完了です
calib_data = bus.read_i2c_block_data(BNO055_ADDRESS, 0x55, 22)
with open("bno055_calib.bin", "wb") as f:
f.write(bytearray(calib_data)) # オフセット値を読み出して保存
キャリブレーション情報は、RAM上のアドレス範囲:0x55 〜 0x6Aに保持されて、それを保存しています
実行例(ユーザーがMAG:磁気、ACC:加速度)、GYR:ジャイロの3項目の校正のためセンサーを動かす操作が必要で数十秒かかります。)
suzuki@raspberrypi:~/py $ python bno055_calib_write.py
BNO055をNDOF(Fusion)モードで初期化しました。(自動的にキャリブレーションが実行)
SYS:0, GYR:0, ACC:0, MAG:0
・・・省略・・・
SYS:0, GYR:3, ACC:0, MAG:0
SYS:2, GYR:3, ACC:0, MAG:0
・・・省略・・・
SYS:2, GYR:3, ACC:0, MAG:0
・・・省略・・・
SYS:0, GYR:3, ACC:0, MAG:0
・・・省略・・・
SYS:0, GYR:3, ACC:0, MAG:0
SYS:2, GYR:3, ACC:0, MAG:0
SYS:3, GYR:3, ACC:0, MAG:0
SYS:2, GYR:3, ACC:0, MAG:0
・・・省略・・・
SYS:2, GYR:3, ACC:0, MAG:0
・・・省略・・・
SYS:0, GYR:3, ACC:0, MAG:1
SYS:2, GYR:3, ACC:0, MAG:1
SYS:1, GYR:3, ACC:0, MAG:1
SYS:2, GYR:3, ACC:0, MAG:2
SYS:3, GYR:3, ACC:0, MAG:2
SYS:3, GYR:3, ACC:0, MAG:3
・・・省略・・・
SYS:3, GYR:3, ACC:1, MAG:3
SYS:3, GYR:3, ACC:3, MAG:3
suzuki@raspberrypi:~/py $
上記実行で、各校正判定値が 3 になればキャリブレーションを完了したとして、
そのキャリブレーションデータを"bno055_calib.bin"に保存します。
次に、
キャリブレーションデータの"bno055_calib.bin"をBNO055に再書き込みして
補正する最終板のコード(bno055_M.py)を示します。
なお、このコードではX軸が北を向く時にHeadingが0、水平に置いた時にRollと Pitchが0になるオフセット調整のコードも付加しています。
この調整に使うデータは、"bno055_offset.txt"に調整無し時の各値を記憶しておいて、起動時にこれをロードして調整を行っています。
"bno055_offset.txt"の内容例:189.81,-1.38,-176.75
上記のデータは、ロボットを水平にしてはX軸が北を向く時のオフセット調整無し時のHeading,Roll,Pitchの値です。
"bno055_offset.txt"のファイルが存在しない時、このファイルを作るモードで起動する仕様にしました。
この"bno055_offset.txt"ファイル作成モード(make_offset_mode)では、起動時に20秒程度で終了し、終了直前の無調整のHeading,Roll,Pitchの値を保存します。
新たな調整データを設定したい場合は、"bno055_offset.txt"を削除して、起動時に20秒以内でロボットを水平でX軸が北を向くように置いて、
"bno055_offset.txt"が作られるプログラムの終了を待つ必要があります。
(一度"bno055_offset.txt"を作成後、再び作成が必要なる頻度は少ないと考えています。)
#!/usr/bin/python3
import smbus # I2C通信をPythonから簡単に扱うためのモジュール
import time
make_offset_mode=False # "bno055_offset.txt"オフセット調整ファイル作成モード
try:
with open("bno055_offset.txt", "r") as fr:
s = fr.readline() # 一行読み取り(改行を含めて)
a = s.split(",")
heading_offset = float(a[0]) # オフセット調整値取得
roll_offset = float(a[1])
pitch_offset = float(a[2])
print(f"offset value heading:{heading_offset}, roll:{roll_offset}, pitch:{pitch_offset}")
except:
make_offset_mode = True # 作成モード
# BNO055 の初期化
BNO055_ADDRESS = 0x28 # BNO055のI2Cアドレス(ADRピンがGNDなら0x28、VDDなら0x29になります)
BNO055_OPR_MODE = 0x3D # 動作モードを設定するためのレジスタ
BNO055_EULER_H_LSB = 0x1A # オイラー角(方位・ロール・ピッチ)のデータが始まるアドレス
bus = smbus.SMBus(1)# 引数の1でRaspberry PiのボードGPIO2: SDA、GPIO3: SCLを指定
bus.write_byte_data(BNO055_ADDRESS, BNO055_OPR_MODE, 0x00) # 設定変更(OPR_MODE)でCONFIGモードに切り替える
time.sleep(0.05)
with open("bno055_calib.bin", "rb") as f:
calib_data = list(f.read(22))
# 設定モードへ
bus.write_byte_data(BNO055_ADDRESS, 0x3D, 0x00)
time.sleep(0.025)
# キャリブレーションデータを書き込んで、調整情報を復元
bus.write_i2c_block_data(BNO055_ADDRESS, 0x55, calib_data)
# NDOFモードに戻す
bus.write_byte_data(BNO055_ADDRESS, 0x3D, 0x0C)
time.sleep(0.05)
# 出力単位(UNIT_SEL)を設定(0x00で「角度=度(°)」単位)
bus.write_byte_data(BNO055_ADDRESS,0x3B, 0x00)
def to_signed(val):
"""16ビット値を符号付き整数に変換"""
if val >= 0x8000:
val -= 0x10000
return val
def read_euler():
data = bus.read_i2c_block_data(BNO055_ADDRESS, BNO055_EULER_H_LSB, 6)
# 各要素が 1 バイト(0?255)の整数を6個のリストで得られる。(1 LSB = 1/16 度)
# データはリトルエンディアン形式(下位→上位の順)
heading = (data[1] << 8) | data[0] # 方位角(北基準のYAW)
roll = (data[3] << 8) | data[2] # ロール角(左右の傾き)
pitch = (data[5] << 8) | data[4] # ピッチ角(前後の傾き)
# ロールとピッチは符号付き
roll = to_signed(roll)
pitch = to_signed(pitch)
# スケーリング(1 LSB = 1/16 度)
heading = heading / 16.0 #
roll = roll / 16.0
pitch = pitch / 16.0
if make_offset_mode == False:
# X軸が北を向く時にHeadingが0、水平に置いた時にRollと Pitchが0になるオフセット調整
heading = heading - heading_offset # headingズレ補正
heading = heading if heading >= 0 else heading + 360
roll = roll - roll_offset # rollズレ補正
if roll > 90:
roll = -(90 - roll)
elif roll < -90:
roll = -(-90 - roll)
pitch = pitch - pitch_offset # pitchズレ補正
if pitch > 180:
pitch = -(360 - pitch)
elif pitch < -180:
pitch = -(-360 - pitch)
#
return heading, roll, pitch
try:
count = 100
while True:
h, r, p = read_euler()
print(f"Heading: {h:7.2f}°, Roll: {r:7.2f}°, Pitch: {p:7.2f}°")
if make_offset_mode:
with open("bno055_offset.txt", "w") as fw:
s = fw.write(f"{h},{r},{p}\n") # オフセット調整データ書き込み
count -= 1
print(f" {count}が0になるまでに、ロボットを水平にしてはX軸が北を向くように置いてください。")
if count <= 0:
print("\n終了しました。")
break
#
time.sleep(0.2)
except KeyboardInterrupt:
print("\n終了しました。")
VL53L1X使用 レーザー測距センサーモジュール(ToF)の動作検証プログラム
距離計測の以下の検証コードをAIより入手しました。
これには、Adafruit(エイダフルート:アメリカの企業で、マイコンボード、センサー、DIYキット、電子部品などを幅広く提供)のモジュールを使っており、
それを次のコマンドで追加しました。
sudo pip3 install --upgrade adafruit-circuitpython-vl53l1x
このバージョンは現時点(2525年10月)で、 1.2.6でした。
import time
import board
import busio
import adafruit_vl53l1x
# オープンソースハードウェアの設計・製造・販売を行うアメリカの企業のAdafruit(エイダフルート)モジュール利用
# I2C初期化
i2c = busio.I2C(board.SCL, board.SDA)
# センサー初期化
vl53 = adafruit_vl53l1x.VL53L1X(i2c)
print("VL53L1X Start measuring...")
vl53.start_ranging()
while True:
distance = vl53.distance
if distance is not None:
print(f"Distance: {distance} mm")
time.sleep(0.5)
下記が実行例で、Ctrl+Cで強制終了しています。
suzuki@raspberrypi:~/py $ python VL53L1X_Adafruit.py
VL53L1X Start measuring...
Distance: 88.6 mm
Distance: 88.5 mm
Distance: 88.4 mm
^CTraceback (most recent call last):
File "/home/suzuki/py/VL53L1X_Adafruit.py", line 21, in
time.sleep(0.5)
KeyboardInterrupt
suzuki@raspberrypi:~/py $
以上のように、比較的簡単に動作できましたが、これをI2Cを直接に使うおうとすると、かなり難しいようです。
9軸センサーBNO055モジュールでは、smbusモジュールで制御していましたが、
VL53L1X のレジスタはすべて 16ビットアドレス形式(上位バイト → 下位バイト)で、
16ビット指定が容易なsmbus2モジュールを使う方が扱いやすいと思われます。
SSD1306使用の128×64ドット有機ELディスプレイの動作検証プログラム
SSD1306使用モジュールの文字コードをAIより入手し、少し変更して確認しました。
これも、Adafruit(エイダフルート)のモジュールを利用しており、それは次のコマンド操作でインストールしています。
sudo apt update
sudo apt install -y python3-pip python3-pil python3-numpy
pip3 install adafruit-circuitpython-ssd1306
このadafruit-circuitpython-ssd1306バージョンは現時点(2525年11月)で、2.12.22でした。
確認したコードは次の内容です。
import board
import busio
from adafruit_ssd1306 import SSD1306_I2C
from PIL import Image, ImageDraw, ImageFont
import time
i2c = busio.I2C(board.SCL, board.SDA)# --- I2C初期化 ---
# --- SSD1306ディスプレイ初期化 (128x64の場合) ---
oled = SSD1306_I2C(128, 64, i2c)
# --- 画面をクリア ---
oled.fill(0)
oled.show()
# --- Pillowで描画領域を作成 ---
image = Image.new("1", (oled.width, oled.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()# --- フォント設定 ---
# --- テキスト描画 (0=黒、255=白)上記設定で、横21文字---
draw.text((0, 0), "Test adafruit_ssd1306", font=font, fill=255)
draw.text((0, 20), "1234567890123456789012345", font=font, fill=255)
draw.text((0, 40), "RasPi3A+,UMEHOSHI ITA", font=font, fill=255)
# --- 画面に表示 ---
oled.image(image)
oled.show()
time.sleep(10)# --- 10秒間表示 ---
# --- クリア ---
oled.fill(0)
oled.show()
draw.text() は、Pillow(PIL) ライブラリの ImageDraw クラスに含まれるメソッドです。
これで、128×64ドットの画像上にテキストを描画しています。(右のイメージが実行例)
SSD1306の横幅128ピクセルに対して「何文字表示できるか」は、使用しているフォントの幅 によって決まります。
上記のImageFont.load_default() で読み込まれるフォントは、固定幅 6×8 ピクセルの英数字フォントです。
128 ÷ 6 ≒ 21文字 より、1行で表示可能な文字数はおよそ21文字です。
128ピクセルを超える部分の文字は 右端で切れて見えなくなります。(見えない だけ)
また、上記テキスト表示のdraw.textメソッドを使っているが、他に次のような代表的メソッドがあります。
| 用途 | 使用例 |
| 点を打つ | draw.point((x, y), fill=255) |
| 線を引く | raw.line((x1, y1, x2, y2), fill=255, width=1) |
| 四角形や枠を描く | draw.rectangle((x1, y1, x2, y2), outline=255, fill=0) |
| 円・楕円を描く | draw.ellipse((x1, y1, x2, y2), outline=255, fill=0) |
| 多角形を描く | draw.polygon([(x1,y1),(x2,y2),(x3,y3)], outline=255, fill=0) |
| 弧(円弧・楕円弧)を描く | draw.arc((x1, y1, x2, y2), start=0, end=180, fill=255) |
4Bit DIPスイッチ情報を、128×64ドット有機ELディスプレイに表示する検証プログラム
#!/usr/bin/python3
import RPi.GPIO as GPIO
import board
import busio
from adafruit_ssd1306 import SSD1306_I2C
from PIL import Image, ImageDraw, ImageFont
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT) # GPIO21を出力に設定
GPIO.output(21, GPIO.HIGH) # ON(3.3V)
for no in [6, 16, 17, 22, 23, 24, 25]:
GPIO.setup(no, GPIO.IN, pull_up_down=GPIO.PUD_UP) # プルアップ付き入力
sw1 = "OFF" if GPIO.input(22) == GPIO.HIGH else "ON"
sw2 = "OFF" if GPIO.input(23) == GPIO.HIGH else "ON"
sw3 = "OFF" if GPIO.input(24) == GPIO.HIGH else "ON"
sw4 = "OFF" if GPIO.input(25) == GPIO.HIGH else "ON"
i2c = busio.I2C(board.SCL, board.SDA)# --- I2C初期化 ---
# --- SSD1306ディスプレイ初期化 (128x64の場合) ---
oled = SSD1306_I2C(128, 64, i2c)
# --- 画面をクリア ---
oled.fill(0)
oled.show()
# --- Pillowで描画領域を作成 ---
image = Image.new("1", (oled.width, oled.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()# --- フォント設定 ---
# --- テキスト描画 (0=黒、255=白)上記設定で、横21文字---
draw.text((0, 0), f"DIP SW1: {sw1}", font=font, fill=255)
draw.text((0, 20), f"DIP SW2: {sw2}", font=font, fill=255)
draw.text((0, 40), f"DIP SW3: {sw3}", font=font, fill=255)
draw.text((0, 80), f"DIP SW4: {sw4}", font=font, fill=255)
# --- 画面に表示 ---
oled.image(image)
oled.show()
time.sleep(10)# --- 10秒間表示 ---
# --- クリア ---
oled.fill(0)
oled.show()