https://github.com/Tamakichi/pico_MicroPython_misakifont
からcodeボタン→Download ZIPでダウンロードした後、
misakifontフォルダをCIRCUITPYドライブのlibにコピーした後、
sample_misaki.pyをSSD1302に表示させるため以下のようにちょこっと修正して、
from misakifont import MisakiFont
import busio
import adafruit_ssd1306
from board import *
i2c = busio.I2C(GP15, GP14)
display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)
display.fill(0) # 表示内容消去
"""
フォントのビットマップ表示
"""
def show_bitmap(x,y,fd):
for row in range(0,7):
for col in range(0,7):
display.pixel(x+col,y+row,1 if (0x80>>col) & fd[row] else 0)
str="こんにちは世界!"
mf = MisakiFont()
i=0
for c in str:
d = mf.font(ord(c))
show_bitmap(10+i,30,d)
i+=8
display.show()
表示させるとこんな感じ。
フォントの大きさの自由度はあまりないけど、これでいいんじゃないかな?
2025年2月23日日曜日
Raspberry Pi Picoに接続したSSD1302に美咲フォントで表示
raspberry pi picoでSSD1302に日本語を表示させてみた。
raspberry pi picoでSSD1302に漢字を表示させたい。
というわけで以下の順でやってみた。
1.フォントを探す
2.イメージのバイナリデータをファイル化
3.漢字コードからバイナリファイルの場所を算出するインデックスを作成
4.文字コード変換1(ShiftJIS→JIS変換)
5.文字コード変換2(UTF8→ShiftJIS変換)
6.文字画像確認その1(コンソール出力)
7.文字画像確認その2(SSD1302への出力)
8.文字列を表示する。
1.フォントを探す
https://www.mgo-tec.com/blog-entry-ssd1306-shinonome.html を見てみると東雲フォント
というものがあるみたいなので、今回はこれを(shnmk16.bdf)を使ってみる。
2.イメージのバイナリデータをファイル化
どうせメモリ上にフォントイメージを載せられないので、一字毎イメージファイルを読むことにする。
バイナリデータは1文字32バイトで保存。
そのためのshnmk16.bdfからバイナリファイルを抽出するスクリプトを作って流してみる。
def readandwritefontsfile():
isImageArea=False
code=0
image=b''
with open('shnmk16.bdf') as f:
with open('fontImage.bin','wb') as wf:
for str in f:
if str[:9]=="STARTCHAR" :
code=int(str[10:14],16)
elif str[:6]=="BITMAP" :
image=b''
isImageArea=True
elif str[:7]=="ENDCHAR" :
wf.write(image)
isImageArea=False
elif isImageArea:
image+=bytes.fromhex(str[:4])
readandwritefontsfile()
で作成されたfontImage.binのファイルサイズが、220,128バイト。
CircuitPyドライブには載せられるが、メモリ上にこのイメージを載せるのは容量的に少し不安がある。
なので、表示の都度このファイルから1文字ずつ読み出すことにする。
3.漢字コードからバイナリファイルの場所を算出するインデックスを作成
shnmk16.bdf内で定義しているコードはJISのよう。
最初の文字コード(10進数)8481からで、使われていない文字はそのコードを飛ばしていて、
フォントコードが飛び番になっている。
確認方法として、以下を実施
・「shnmk16.bdf」をキーワードENCODINGでgrepし、
・出てきた行をExcelに貼り付け、
・区切り位置ウィザードで半角スペースを区切り
・E2セルに=D2-D1-1の計算式を入れ、E列末までコピーし、値貼り付けにより計算式を数値にして
・オートフィルタでE列として、マイナス値が1件を確認したのち、値0のもののみ表示し、表示された行を行削除し、
・オートフィルタを解除して、F1セルに=E1、F2セルに=F1+E2の計算式を入れ、F2セルをF列末までコピーして
・D列(コード)、E列(飛び番)、F列(累積)を抽出して確認
確認したところ、以下のコード値で直前のコードから+1より大きい数字に飛んでいる。コード値と飛び番数は以下。
コード 飛び番 累積
8737 162 162
8762 11 173
8778 8 181
8796 11 192
8818 7 199
8830 4 203
9008 177 380
9025 7 387
9057 6 393
9249 166 559
9505 173 732
9761 170 902
9793 8 910
10017 200 1110
10065 15 1125
10273 175 1300
12321 2016 3316
12577 162 3478
12833 162 3640
13089 162 3802
13345 162 3964
13601 162 4126
13857 162 4288
14113 162 4450
14369 162 4612
14625 162 4774
14881 162 4936
15137 162 5098
15393 162 5260
15649 162 5422
15905 162 5584
16161 162 5746
16417 162 5908
16673 162 6070
16929 162 6232
17185 162 6394
17441 162 6556
17697 162 6718
17953 162 6880
18209 162 7042
18465 162 7204
18721 162 7366
18977 162 7528
19233 162 7690
19489 162 7852
19745 162 8014
20001 162 8176
20257 162 8338
20513 205 8543
20769 162 8705
21025 162 8867
21281 162 9029
21537 162 9191
21793 162 9353
22049 162 9515
22305 162 9677
22561 162 9839
22817 162 10001
23073 162 10163
23329 162 10325
23585 162 10487
23841 162 10649
24097 162 10811
24353 162 10973
24609 162 11135
24865 162 11297
25121 162 11459
25377 162 11621
25633 162 11783
25889 162 11945
26145 162 12107
26401 162 12269
26657 162 12431
26913 162 12593
27169 162 12755
27425 162 12917
27681 162 13079
27937 162 13241
28193 162 13403
28449 162 13565
28705 162 13727
28961 162 13889
29217 162 14051
29473 162 14213
29729 162 14375
これをもとに以下の関数で入力code(SJISコード)を、その値により補正値を変え、
最終的にイメージファイルの読み込みアドレスを算出する
具体的には、
・G2セルに="elif code <"&D2&" :(Alt+Enter) code-="&E1 計算式を入れG列末までコピーして、
・G列をコピーし、テキストエディタに貼り付け、正規表現置換で\r?\nを\r\nに置換。
あとは前後を整備して、以下の関数を作る。
def binfileindex(code):
if code < 8737 :
pass
elif code <8762 :
code-=162
elif code <8778 :
code-=173
elif code <8796 :
code-=181
elif code <8818 :
code-=192
elif code <8830 :
code-=199
elif code <9008 :
code-=203
elif code <9025 :
code-=380
elif code <9057 :
code-=387
elif code <9249 :
code-=393
elif code <9505 :
code-=559
elif code <9761 :
code-=732
elif code <9793 :
code-=902
elif code <10017 :
code-=910
elif code <10065 :
code-=1110
elif code <10273 :
code-=1125
elif code <12321 :
code-=1300
elif code <12577 :
code-=3316
elif code <12833 :
code-=3478
elif code <13089 :
code-=3640
elif code <13345 :
code-=3802
elif code <13601 :
code-=3964
elif code <13857 :
code-=4126
elif code <14113 :
code-=4288
elif code <14369 :
code-=4450
elif code <14625 :
code-=4612
elif code <14881 :
code-=4774
elif code <15137 :
code-=4936
elif code <15393 :
code-=5098
elif code <15649 :
code-=5260
elif code <15905 :
code-=5422
elif code <16161 :
code-=5584
elif code <16417 :
code-=5746
elif code <16673 :
code-=5908
elif code <16929 :
code-=6070
elif code <17185 :
code-=6232
elif code <17441 :
code-=6394
elif code <17697 :
code-=6556
elif code <17953 :
code-=6718
elif code <18209 :
code-=6880
elif code <18465 :
code-=7042
elif code <18721 :
code-=7204
elif code <18977 :
code-=7366
elif code <19233 :
code-=7528
elif code <19489 :
code-=7690
elif code <19745 :
code-=7852
elif code <20001 :
code-=8014
elif code <20257 :
code-=8176
elif code <20513 :
code-=8338
elif code <20769 :
code-=8543
elif code <21025 :
code-=8705
elif code <21281 :
code-=8867
elif code <21537 :
code-=9029
elif code <21793 :
code-=9191
elif code <22049 :
code-=9353
elif code <22305 :
code-=9515
elif code <22561 :
code-=9677
elif code <22817 :
code-=9839
elif code <23073 :
code-=10001
elif code <23329 :
code-=10163
elif code <23585 :
code-=10325
elif code <23841 :
code-=10487
elif code <24097 :
code-=10649
elif code <24353 :
code-=10811
elif code <24609 :
code-=10973
elif code <24865 :
code-=11135
elif code <25121 :
code-=11297
elif code <25377 :
code-=11459
elif code <25633 :
code-=11621
elif code <25889 :
code-=11783
elif code <26145 :
code-=11945
elif code <26401 :
code-=12107
elif code <26657 :
code-=12269
elif code <26913 :
code-=12431
elif code <27169 :
code-=12593
elif code <27425 :
code-=12755
elif code <27681 :
code-=12917
elif code <27937 :
code-=13079
elif code <28193 :
code-=13241
elif code <28449 :
code-=13403
elif code <28705 :
code-=13565
elif code <28961 :
code-=13727
elif code <29217 :
code-=13889
elif code <29473 :
code-=14051
elif code <29729 :
code-=14213
else :
code-=14375
return (code-8481)*32
d=binfileindex(29400)
4.文字コード変換1(ShiftJIS→JIS変換)
pythonの標準でのJISコード変換は見つからなかったので、SJIS→JISの変換関数を作る。
def sjis2jis(indata):
h_data=int.from_bytes(indata)>>8
l_data=int.from_bytes(indata) & 0xff
if h_data<=0x9f :
if l_data < 0x9f :
h_data=(h_data<<1) - 0xe1
else :
h_data=(h_data<<1) - 0xe0
else :
if l_data < 0x9f :
h_data =(h_data << 1 ) -0x161
else:
h_data =(h_data << 1 ) -0x160
if l_data < 0x7f :
l_data -= 0x1f
elif l_data < 0x9f:
l_data -=0x20
else:
l_data -=0x7e
return h_data << 8 | l_data
5.文字コード変換2(UTF8→ShiftJIS変換)
circuitpythonでは、「'あ'.encode('shift_jis')」が使えずUTF-8で表示されてしまう。
なので自前で変換テーブルを作成する。元ネタとして、以下に簡易的なUTF-8→ShiftJISの変換の情報があるので
https://ameblo.jp/fc2miha/entry-12876272336.html
上の情報から、以下の情報を辿って、
https://drive.google.com/file/d/1dMNG8bcM58w7uHRdBbqsP2R1HGAU9K2z/view?usp=sharing
UTF-8→SJISの変換テーブル(list.dat)をダウンロードする。
このlist.datを入力にした、3バイトのUTF-8コードと2バイトのShiftJISのペアの5バイト*変換文字分のデータを作る。
次のプログラムでutf8tosjis.binを作り、
import json
def readandwriteconvfile():
flg_firstline=True
dic=dict()
with open('utf8tosjis.bin','wb') as wf:
with open('list.dat','r',encoding='utf-8') as f:
for str in f:
if flg_firstline :
flg_firstline=False
continue
str_utf8code,str_sjiscode,_=str.split(',')
wf.write(int(str_utf8code,16).to_bytes(3,byteorder='big'))
wf.write(int(str_sjiscode,16).to_bytes(2,byteorder='big'))
readandwriteconvfile()
出来上がった変換テーブル(utf8tosjis.bin)ファイルをcircuitpyドライブ直下におく。
サイズは、46,795バイト。どうせイメージを読み込むのにファイルアクセスがあるので
こちらを高速化してもあまり意味がないが、今回は変換テーブルを起動時にファイルから読み込み
メモリ上に載せておくことにする。
(ただ辞書にしてjson.dumpやjson.loadをすると、すごく時間がかかるのでメモリ一括読み込みし、
キーをハッシュアクセスではなく、ループで検索することにする。)
dic=dict()
allbyte=b''
with open('utf8tosjis.bin','rb') as f:
allbyte=f.read()
で、変換表を使ったutf8文字列→sjis文字列への変換関数を作る。
def utf8str2sjisstr(utf8str):
retval=b''
for utf8char in utf8str:
utf8=utf8char.encode()
i=0
while i>8
l_data=indata & 0xff
if h_data<=0x9f :
if l_data < 0x9f :
h_data=(h_data<<1) - 0xe1
else :
h_data=(h_data<<1) - 0xe0
else :
if l_data < 0x9f :
h_data =(h_data << 1 ) -0x161
else:
h_data =(h_data << 1 ) -0x160
if l_data < 0x7f :
l_data -= 0x1f
elif l_data < 0x9f:
l_data -=0x20
else:
l_data -=0x7e
return h_data << 8 | l_data
str_mainte='メンテナンス中'
putString(utf8str2sjisstr(str_mainte),28,8)
表示はこんな感じ。
utf8→sjis変換部分やユーザから見えなくてもよいので、putStringの中から呼べばよかったかも。
ま、泥臭いし半角対応はできていないので、今回作ってみたものの使わないかな?
8x8の美咲フォントを使う方は、いろいろ整備されているみたいだし。
2025年2月16日日曜日
CircuitPythonにロータリーエンコーダー用のモジュールがあった。
https://docs.circuitpython.org/en/latest/shared-bindings/rotaryio/index.html circuit pythonにロータリーエンコーダー専用のモジュールがあるのなら自作しないで使うべきでした。 で、このページをみたらクリックタイプの動きがわかりました。 秋月で買った100円のクリックタイプのロータリーエンコーダーは1クリック進むとOFF,OFF,ON,ONの1サイクル分進んでいた、と。 rotaryio.IncrementalEncoderを呼び出すときに指定する。divisorがデフォルトで4で、 これはdedents(引っ掛かり)が、(ON,ON,OFF,OFFの)1サイクルあたりに1回しかこない場合に指定するようです。 ということは、1クリック分動かすと1サイクル分うごくものがある、と、 ロータリーエンコーダをGPIO1,GND,GPIO2に接続し、 上記のcircuitpythonのページのサンプルをraspberrypipico用にピンを少し書き換えて、以下のプログラムを動かしてみました。 import rotaryio import time from board import * enc = rotaryio.IncrementalEncoder(GP1, GP2) last_position = None while True: position = enc.position if last_position == None or position != last_position: print(position) last_position = position 結果、ロータリーエンコーダを回すと+1もしくは-1をした値を返してくれた。 もう簡単に試せるクリックなしのロータリーエンコーダを持っていないので試していないのだけど、 クリックなしのロータリーエンコーダーの場合は、IncrementalEncoderの第三引数に1を入れればよいのだと思う。 2025/2/20 追記: クリックなしロータリーエンコーダーでIncrementalEncoderモジュールを使ったところ、第三引数なし(divisor=4)だと誤りなく拾えるが、 回す量に対する読み取り頻度が体感1/4になってしまい、第三引数に1を設定すると読み取り頻度が向上するが、逆方向読み取りも多々発生する。 1周期1カウントや2カウントを拾うのなら使えそうだが、1周期4カウント拾うのは汎用的なものだと無理があるのかな?
2025年2月9日日曜日
ロータリーエンコーダの読み取り改善を目指して
ロータリーエンコーダは、回したときに状態誤検知が発生することがある。
原因としては以下が考えられる。
①:読み込み間隔が長いため状態変化の取りこぼしが発生していること
②:状態変化時のバタつきを検知していること
前回は、以下のように状態読み取り間隔を十分長くとる(0.01秒間隔)ことにより、②の誤検知頻度を低くすることにしてみた。
import digitalio
import board
import time
s1 = digitalio.DigitalInOut(board.GP17)
s1.direction = digitalio.Direction.INPUT
s1.pull = digitalio.Pull.UP
s2 = digitalio.DigitalInOut(board.GP16)
s2.direction = digitalio.Direction.INPUT
s2.pull = digitalio.Pull.UP
n_s1=o_s1=s1.value
n_s2=o_s2=s2.value
while True:
n_s1 , n_s2= s1.value , s2.value
if(n_s1 != o_s1 or n_s2 != o_s2) :
if n_s1 ^ o_s2 == 0 :
print("UP")
else:
print("DOWN")
o_s1,o_s2=n_s1,n_s2
time.sleep(0.01)
このとき、低速で動かすとほとんど誤検知が発生しないが、高速で動かすと誤検知が複数回連続で見られる。
これは、高速操作時の誤検知は①で低速時の誤検知は②で、②は読み取り間隔が長いためほとんど遭遇しないのだと思う。
上のプログラムのtime.sleep行をコメントアウトすると、ゆっくり動かしても高速に動かしても誤検知が見られる。
連続した誤検知はたまにしか発生しないが、3行同時に変化が表示されることがある。
こちらは、①は発生していないが、たまに②が3回連続で発生していると思う。
まずは、timesleepを小さい量から徐々に大きくし、低速回転時にたましか誤検知が発生せず連続誤検知が1回のみのtimesleep値を探す。
0.00001とした場合、操作途中に12回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが12回分以上は発生していることがある。
0.0001とした場合、操作途中に 4回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが4回以上は発生していることがある。
0.001とした場合、操作途中に 2回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが2回以上は発生していることがある。
0.002とした場合、操作途中に 1回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが1回以上は発生していることがある。
というわけで、0.002秒を採用し、運悪く誤検知のタイミングで読み取ってしまった場合に備え、最初のプログラムを以下のように少し加工してみた。
import digitalio
import board
import time
s1 = digitalio.DigitalInOut(board.GP17)
s1.direction = digitalio.Direction.INPUT
s1.pull = digitalio.Pull.UP
s2 = digitalio.DigitalInOut(board.GP16)
s2.direction = digitalio.Direction.INPUT
s2.pull = digitalio.Pull.UP
n_s1=o_s1=s1.value
n_s2=o_s2=s2.value
while True:
nn_s1 , nn_s2= s1.value , s2.value
if (nn_s1 != n_s1 or nn_s2 != n_s2) :
n_s1,n_s2 =nn_s1,nn_s2
elif(n_s1 != o_s1 or n_s2 != o_s2) :
if n_s1 ^ o_s2 == 0 :
print("UP")
else:
print("DOWN")
o_s1,o_s2=n_s1,n_s2
time.sleep(0.002)
加工した部分time.sleepの値の他に、以下の部分を
n_s1 , n_s2= s1.value , s2.value
if(n_s1 != o_s1 or n_s2 != o_s2) :
を以下に変更したこと。
nn_s1 , nn_s2= s1.value , s2.value
if (nn_s1 != n_s1 or nn_s2 != n_s2) :
n_s1,n_s2 =nn_s1,nn_s2
elif(n_s1 != o_s1 or n_s2 != o_s2) :
変更の狙いは以下が成り立つとして、
・0.002秒の間隔なら2回目の読み取り時には正しい読み取りができているはず。
・人の操作は、0.002x2よりも十分遅い。(間隔の2回分の時間が開いたとしても、人の次の操作と誤検知期間よりは短い。)
変更内容は、
0.002秒間隔でロータリーエンコーダを読み取るが、状態変更後の初回の読み取り時には状態変更を受け入れず、
次の読み取りで初回読み取りと同じ値であれば、先の読み取りは正常として状態変更を受け入れる。
次の読み取りで初回読み取りと違う値であれば、先の読み取りは誤検知として読み飛ばしたうえで今回の読み取りを初回読み取りとする。
ただ、ごくたまに誤検知をする。
原因はわからないが、以下かなぁ。
・状態変更後の誤検知期間の時間長は一定(0.002秒)ではないのか
・ダイヤルを次の目盛りまで回す操作が0.004秒よりも速いのか
・読み取り頻度が短すぎるのか
・そのほかの要因なのか
やってないのでわからないのだけど、0.01μFのセラミックコンデンサを並列つなぎすると劇的に改善するものなのかな?
circuitpythonでストレージ非表示、データ永続化、
boot.pyをこんな感じにすると、GPIO6につけたスイッチを押してUSB接続すると、switch.valueがFalseとなり通常のドライブが見れる状況。
スイッチを押さずに起動すると、PCにpicoのドライブが表示されない。
import os
import storage
import board
import digitalio
switch = digitalio.DigitalInOut(board.GP6)
switch.direction = digitalio.Direction.INPUT
switch.pull = digitalio.Pull.UP
if switch.value:
#NormalMode
storage.disable_usb_drive()
else :
#Maintance
pass
ドライブが表示されないなどPCからPICOのストレージに書き込みができない場合には、circuitpythonからストレージに書き込み可能となる。
これを使って状態の永続化をする。
pythonはjson形式文字列をそのまま辞書として保持できるみたいなので、こんな感じで辞書変数のdataをdata.jsonのファイルで出力して、
import json
# 永続化したいデータ
data = {
"name": "Taro",
"age": 30,
"city": "Tokyo"
}
# JSONファイルにデータを書き込む
with open('data.json', 'w', encoding='utf-8') as file:
json.dump(data, file)
data.jsonからデータを読み取り、更新してファイルに出力するのはこんな感じ。
import json
# JSONファイルからデータを読み込む
with open('data.json', 'r', encoding='utf-8') as file:
data = json.load(file)
print(data)
data['age']+=1
# JSONファイルにデータを書き込む
with open('data.json', 'w', encoding='utf-8') as file:
json.dump(data, file)
boot.pyからcode.pyの情報連携のやり方がわからず、PCから書き込み可能モードだとboot.pyからもファイル出力ができなさそう。
なので、data書き込みをしてみて、エラーになったらメンテナンスモードと判断しようかな。
2025/3/2 追記:JSONのやり取りはすごく遅いので、普通のファイル入出力の方がよさそう。
raspberry pi pico2を使ったUSB入力デバイス、部品配置
HIDの記事について、ユニバーサル基板に配置してみた。 ついでにディスプレイをつけてみた。 裏面はこんな感じ 立体交差、曲線配線、冗長な配線距離、はんだの痕など、指さして笑われそう。 今回もいろいろありました。 ロータリーエンコーダの固定足2個用にユニバーサル基板の3穴分を切らなければいけない(両足分の2か所)が、切ってロータリーエンコーダを配置してみたところ、間隔が狭すぎて使いづらい。 上の方のロータリーエンコーダの上の方の足を基板外に出すことにより距離しつつ再度の基板切りを回避。 裏面配線のことを忘れてピン配置図と同じようにしてくみ上げ終わってから、PCで確認したところ動作せず、結構途方にくれる。 GPIOの番号を0番から順に利用しようと思って、立体交差を受け入れていたのなのだけれど、もうそんなことを言ってられない。 ほとんどGPIOなので、GPIO番号の変更で逃げられない部分の再配線。 最初はロータリーエンコーダをクリックありとクリックなしを配置していたのだけど、動かしてみたらクリックありのロータリーエンコーダーの挙動がよくわからない。 結局2つともクリックなしロータリーエンコーダにしてみた。 ロータリーエンコーダを外すときに基板の穴が再利用できなくなったので、仕方なく配置場所を変更。(再度基板切りが発生) カッターナイフで削って加工しており、1か所切るのに30分ぐらいかかっているので、やり直しは結構ショック。 この削り作業で、カッターナイフの刃を2個分消費。 使った部品は、秋月で買ったものは [104398]細ピンヘッダー 1×20:¥30 2個 合計:¥60 [129604]Raspberry Pi Pico:¥880 [112031]0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色:1個:¥580 [129599]ロータリーエンコーダー(ノンクリックタイプ):¥100 2個 :¥200 [116278]ツマミ(ノブ) Dカット 指標付き S-42:¥30 2個:¥60 [102561]タクトスイッチ(大)10個セット:1パック:¥300 [103230]片面ガラスコンポジット・ユニバーサル基板 Bタイプ めっき仕上げ (95×72mm) 日本製:1枚:¥180 その他、配線用の線は買っていなかったので、 ホームセンターの園芸用の一番細い被膜付きの針金を買ってみた。10メートル250円。ただ、針金直径1.2mmは太すぎて加工困難なため使用できず。 結局ブレッドボード用のジャンパーワイヤーを5本ぐらい切って使ったのと、転がっているケーブル結束の針金入りビニル被膜から被膜をはいで利用。 配置について、raspberry pi picoやディスプレイが手の甲に隠れてしまう。 配置場所を変えたほうが良かったかも。ま、上下逆にして使っても、画面と、usbの面とつまみの位置が逆になるだけだから、気にならないといえば気ならない。 部品面では、安定させるために基板の裏5ミリぐらいスペーサーを入れて、同じ大きさの基板を底面にした方がよかったかも。 あと、精神安定上、ロータリーエンコーダ用にコンデンサはあった方がよいと思う。 さて、次はソフト部分。 今回も結局コンデンサーを使っていないので、ロータリーエンコーダーのバタつきをソフトウェア的にもう少し真面目に対応することと、 デバイスをPCにつないだ時に記憶媒体として表示されなくするのとデバイスに永続的なモードを持ちたいので、あるボタンを押しながら接続しないとcircuitpython側で マウントしないように加工することと、 操作時に、「メッセージ出力?」、「マウスホイール操作?」、「Shift,Ctrl,Altを押しながら?」などPCにどんな挙動をさせるかを決めることと モード変更時のインタフェースを決めることかな?
2025年2月2日日曜日
raspberry pi zero 2wの初期設定メモ
1.初期設定:
1.1.初期イメージ作成:
以下からPiImagerをダウンロードし、PCにインストール
https://www.raspberrypi.com/software/
PiImagerで、以下を設定しmicrosdカードに書き込む
・RASPBERRY PI ZERO 2 W,RASPBERRY PI OS(64-BIT),SDカードストレージを選択し、
・カスタマイズ設定で編集を選択し、
・一般タブで、ホスト名、ユーザ名、WIFI設定、ロケール設定を設定し
・サービスでSSHを有効にし、パスワード認証を使うを選択
イメージを書き込んだSDカードをraspberrypizero2wに刺して、raspberrypizero2wのマイクロUSBに通電。
・PCのコマンドプロンプトから、さっき設定したユーザ名とホスト名を使って"ssh [ユーザ名]@[ホスト名]"で接続。パスワードで接続。
sudo apt update
sudo apt full-upgrade
sudo rpi-update
sudo reboot
PCから"ssh [ユーザ名]@[ホスト名]"で接続。
sudo raspi-config を実行し、3 Interface Option から I2CとSPIを有効にしてリブート。
1.2.dockerインストール
http://docker.jp/PDF/docker-getting-started-guide-for-linux.pdf
と、
https://www.cqpub.co.jp/interface/download/2025/2/IF2502T2%E3%83%AA%E3%82%B9%E3%83%88.zip
のS3_list.txtを見ながら実施。
PCから"ssh [ユーザ名]@[ホスト名]"で接続。
curl -fsSL https://get.docker.com/ | sh
sudo usermod -aG docker $USER
exit でいったんログオフし、もう一度PCから"ssh [ユーザ名]@[ホスト名]"で接続。
id でdockerグループに所属を確認。
インストール結果確認。以下コマンドで「Hello from Docker!」が表示されることを確認。
docker run hello-world
メモリが少なくてchromiumもfirefoxも起動できないのであまり意味がないかもしれないけど、VNCサーバを起動するとするなら、
sudo raspi-config を実行し、3 Interface Option から VNCを有効にする。
そのほか、ssh接続にパスワードじゃなくて鍵を使うとすると、キーペアを作って公開キーをraspberry pi側に、秘密鍵をPC側に置いておく
キー作成
ssh-keygen -f keypair
公開鍵登録
umask 0077
mkdir ~/.ssh
cat keypair.pub >> ~/.ssh/authorized_keys
umask 0022
rm keypair.pub
PCから、"sftp [ユーザ名]@[ホスト名]"で接続。
以下のsftpコマンドで秘密鍵を取得
get keypair
rm keypair
exit
getした秘密鍵(keypair)をPCの%USERPROFILE%\.sshフォルダ(無ければ作る)にファイル移動。
%USERPROFILE%\.ssh\config ファイルに以下を追記
===
Host [raspberrypizero2wのホスト名]
HostName [raspberrypizero2wのホスト名]
User [raspberrypizero2wの接続ユーザー名]
IdentityFile "[%USERPROFILE%を展開したフォルダ名]\.ssh\keypair"
===
インタフェース2月号によると、コンテナからGPIOなどを使うには、
1.ホスト側でGPIOを使っているユーザのID、グループIDを合わせる必要があることと
2.コンテナの/devをホスト側の/devにアクセスできるようにマウントする必要があることと
3.サービスの権限をつける必要がある
ようです。
1.は、Dockerfileで、イメージを作る時にイメージ側にgroupadd,useradd,をして、
ホスト側のpiユーザである、ユーザID:1000,グループID:1000を設定して、
USER命令で実行ユーザをUID,GIDが共に1000のユーザで起動するようにすること
2.は、compose.ymlで、services:(サービス名):の下のvolumes:で配列の一つに/dev:/devを指定すること
3.は、compose.ymlで、services:(サービス名):の下でprivileged: true を指定する必要があることです。
確かに、これをやってP.37のリスト3のDockerfile(ただしADDの行を除く)とリスト4のcompose.ymlを作成し、
[touch .env_file-develop]した後、docker compose up -d を実行して、
docker exec -it [コンテナハッシュ] /bin/bash
で入ると、プロンプトでログインユーザ名がrootではなくDockerfileでUSERで指定したユーザとなっていて、
pip install gpiozero
pip install Rpi
pip install lgpio
pip install pigpio
をやった後、以下のpythonスクリプトを実行したらGPIO18に接続したLEDが点滅した
===
from gpiozero import LED
from time import sleep
led = LED(18)
while True:
led.on()
sleep(0.5)
led.off()
sleep(0.5)
===
追記:imageにPI OS Liteを使っても同じように設定できた。
PI OS はrunlevel 5 (X環境)で動いているけど、PI OS Liteはrunlevel 3 (CUIのマルチユーザーモード)で動いているので、
Liteの方が消費リソースが少なくていい感じかも。
登録:
投稿 (Atom)




