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を入れればよいのだと思う。
PC備忘録
コンピュータを中心ですが、日常のこともいっぱい取り混ぜていきます。 なお自分のメモみたいなものなので、文句は受け付けないですよ~。自己責任でお願いします。
2025年2月16日日曜日
CircuitPythonにロータリーエンコーダー用のモジュールがあった。
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書き込みをしてみて、エラーになったらメンテナンスモードと判断しようかな。
raspberry pi pico2を使ったUSB入力デバイス、部品配置
HIDの記事について、ユニバーサル基板に配置してみた。 ついでにディスプレイをつけてみた。 裏面はこんな感じ 立体交差、曲線配線、冗長な配線距離、はんだの痕など、指さして笑われそう。 今回もいろいろありました。 ロータリーエンコーダの固定足2個用にユニバーサル基板の3穴分を切らなければいけない(両足分の2か所)が、切ってロータリーエンコーダを配置してみたところ、間隔が狭すぎて使いづらい。 上の方のロータリーエンコーダの上の方の足を基板外に出すことにより距離しつつ再度の基板切りを回避。 裏面配線のことを忘れてピン配置図と同じようにしてくみ上げ終わってから、PCで確認したところ動作せず、結構途方にくれる。 GPIOの番号を0番から順に利用しようと思って、立体交差を受け入れていたのなのだけれど、もうそんなことを言ってられない。 ほとんどGPIOなので、GPIO番号の変更で逃げられない部分の再配線。 最初はロータリーエンコーダをクリックありとクリックなしを配置していたのだけど、動かしてみたらクリックありのロータリーエンコーダーの挙動がよくわからない。 結局2つともクリックなしロータリーエンコーダにしてみた。 ロータリーエンコーダを外すときに基板の穴が再利用できなくなったので、仕方なく配置場所を変更。(再度基板切りが発生) カッターナイフで削って加工しており、1か所切るのに30分ぐらいかかっているので、やり直しは結構ショック。 使った部品は、秋月で買ったものは [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の方が消費リソースが少なくていい感じかも。
2025年2月1日土曜日
pipicoで可変抵抗器2
そういえばアナログスティックは2方向の可変抵抗器でした。 これを使ってUSBマウスのようにマウスカーソル操作してみます。 接続はこんな感じ アナログスティックのGND,+5V,VRx,VRyをpicoのGND(38),3.3V(36),ADC1(32),ADC0(31)に接続して circuitpythonのコードはこんな感じ import time import usb_hid from adafruit_hid.mouse import Mouse from analogio import AnalogIn from board import * potentiometer1=AnalogIn(A0) potentiometer2=AnalogIn(A1) mouse = Mouse(usb_hid.devices) while True: x=-int((potentiometer1.value-65536/2)/65536*50) y=int((potentiometer2.value-65536/2)/65536*50) if abs(x)<2 : x=0 if abs(y)<2 : y=0 mouse.move(x,y,0) time.sleep(0.01)
最近のユニバーサル基板って切って小さくできるものなのかな?
久しぶり(約40年ぶり)にユニバーサル基板を買ってみた。 100円の小さ目のもの。 今住んでいるところの近くで電子工作用部品を扱っているところを知らないので、ネット通販。 raspberry pi picoやzero,ロータリーエンコーダやタクトスイッチを買ったときに一緒に購入。 (秋月で購入。全部で11,000円に届かなかったので送料が500円。picoは2つかったのだけど、zeroも2つ買っておけば、送料無料になっていたなぁ。) で、納品書をよく読むと、片面ガラスコンポジット・ユニバーサル基板って書いてある。 これって、基板を切ったりできるものなのかな?
登録:
投稿 (Atom)