2025年3月20日木曜日

Powershellの配列アクセス

powershellの変数アクセスについてのメモ
定義されている構造とは違う構造でアクセスした場合、読み取りはできるけど、書き込みは有効にならない感じがすることのメモ




要素3つの配列、その要素に要素3つのハッシュがあるものを定義する。
> $person=@(@{id=1;name="Taro";height=175},@{id=2;name="Jaro";height=173},@{id=1;name="Saburo";height=176})


> $person[0]

Name                           Value                                                                                 
----                           -----                                                                                 
name                           Taro                                                                                  
id                             1                                                                                     
height                         175                                                                                   



> $person[0].GetType()

IsPublic IsSerial Name                                     BaseType                                                  
-------- -------- ----                                     --------                                                  
True     True     Hashtable                                System.Object                                             




> $person[0].name
Taro




> $person[0].name.GetType()

IsPublic IsSerial Name                                     BaseType                                                  
-------- -------- ----                                     --------                                                  
True     True     String                                   System.Object                                             



構造通りのアクセスについては予想通り。
配列を無視して1つのハッシュっぽく読み取りアクセスしてみると、


> $person.name
Taro
Jaro
Saburo

> $person.name.GetType()

IsPublic IsSerial Name                                     BaseType                                                  
-------- -------- ----                                     --------                                                  
True     True     Object[]                                 System.Array                                              



> $person.name[1]
Jaro

> $person.name[1].GetType()

IsPublic IsSerial Name                                     BaseType                                                  
-------- -------- ----                                     --------                                                  
True     True     String                                   System.Object                                             

配列を単体のようにアクセスすると、その結果が配列として返却される。
便利な気がするが、(適当に作っても動いてしまって、あとでメンテが辛そうといった)危なっかしい気もする。

次は書き込み。
まずは、構造通りのアクセスで変更してみると、


> $person[0].height
175

> $person[0].height=180

> $person[0].height
180



予想通り変更された。
配列を無視して1つのハッシュっぽく書き込みアクセスしてみると、


> $person.height[0]
180

> $person.height[0]=175

> $person.height[0]
180

エラーは発生せず値は書き換わらない。

> $person

Name                           Value                                                                                 
----                           -----                                                                                 
name                           Taro                                                                                  
id                             1                                                                                     
height                         180                                                                                   
name                           Jaro                                                                                  
id                             2                                                                                     
height                         173                                                                                   
name                           Saburo                                                                                
id                             1                                                                                     
height                         176           

一旦180に書き換えた後に入力した175の値はどこに行っちゃったんだろう。

2025年3月19日水曜日

インタフェース2025年2月号追実行断念。

私にはハードルが高すぎた。
MIKE環境の作成には、・・・を展開します。とあるが、どこからnormal_mike_xxx.tar.gzをダウンロードすればよいかわからない。
第3部4章では、リスト1から14まで参照しているがそのリストがどこにあるかわからない。などなど。
関わっていても時間が溶けるだけで身になることはなさそうなので諦めよう。

2025年3月2日日曜日

マイコン内蔵LEDの接続

インタフェース2月号のラズパイで作り学ぶDockerコンテナの再開。
ページ内で、マイコン内蔵RGBLEDテープを使うところがあり、

秋月電子で、探していたところ、それっぽいものがあった。
https://akizukidenshi.com/catalog/g/g116029/
で、物が届いた後に気が付いた。あれ、これって表面実装部品じゃないですか?
ヒートガンはあるけど、ペーストはんだもない。 というわけで、2.5ミリ間隔の部分にはんだごてでのはんだ付け。 ワイヤストリッパーもないので、被覆をはさみで切りこみを入れたあとねじって引き抜いて、
作業シートの上にセロハンテープで固定して、
老眼鏡をかけてはんだづけ。
以下のページをみて、ピンアサインと配線例を確認し、 https://www.peace-corp.co.jp/data/WS2812B-V5_V1.0_EN.pdf
このページのコードを流用して、 https://howtojp.com/a/sensors/Raspberry-Pi-WS2812B-control import time import board import neopixel # WS2812Bの設定 pixel_pin = board.GP18 # デジタルピン18に接続した場合 num_pixels = 2 # LEDの数 ORDER = neopixel.GRB # カラーオーダー pixels = neopixel.NeoPixel( pixel_pin, num_pixels, brightness=0.2, auto_write=False, pixel_order=ORDER ) # 色の定義 red = (255, 0, 0) green = (0, 255, 0) blue = (0, 0, 255) # 全てのLEDを順番に点灯 for i in range(num_pixels): pixels[i] = red pixels.show() time.sleep(1) pixels[i] = green pixels.show() time.sleep(1) pixels[i] = blue pixels.show() time.sleep(1) で、動かした結果こんな感じ。
このとき2番目のLEDがなぜか暗かったけど、配線をし直したら明るくなった。 そのほか、BME280もピンヘッダとジャンパをはんだ付けしなきゃならない。インタフェースの特集記事への着手への道のりは先は長そう?

2025年3月1日土曜日

picoのHIDデバイス化備忘録

いろいろ至らない部分があるが、ほかにやりたいことがあるので、こちらはいったん終了。
作業メモだけ残す。

配線:
ボタン1~10をGNDと、GP0,GP1,GP2,GP3,GP4,GP28,GP27,GP26,GP5,GP6に接続
ロータリーエンコーダ1を、GP17,GND,GP16に接続。
ロータリーエンコーダ2を、GP19,GND,GP18に接続。
SSD1302の、GND,VCC,SCL,SDAを、GND,3.3V,GP15,GP14に接続。

ライブラリファイル配置。
PIのボタンを押しながらPCに接続する。
PIのドライブにflash_nuke.uf2をコピーしてストレージを初期化。
PIのドライブにadafruit-circuitpython-raspberry_pi_pico2-ja-9.2.3.uf2をコピーしてcircuitpy環境にする。
adafruit-circuitpython-bundle-py-20250201\examples\framebuf\font5x8.bin をcircuitpyドライブ直下にコピー。
adafruit-circuitpython-bundle-py-20250201\libの
adafruit_framebuf.mpy,adafruit_ssd1306.mpy,adafruit_hid/をcircuitpyのlibフォルダにコピー。
pico_MicroPython_misakifont-main.zip\pico_MicroPython_misakifont-main\の
misakifontフォルダをcircuitpyのlibフォルダにコピー。
mode.iniをcircuitpyドライブ直下に作成。内容は0の1バイトのみ。

circuitpyドライブ直下のboot.pyを以下の内容に差し替える。

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()
    usb_cdc.disable()
else :
  #Maintenance
    pass            


circuitpyドライブ直下のcode.pyを以下の内容に差し替える。


import digitalio
import busio
import adafruit_ssd1306
import time
import usb_hid
import json
from board import *
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard import Keycode
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode
from adafruit_hid.mouse import Mouse
from misakifont import MisakiFont
i2c = busio.I2C(GP15, GP14)
display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)
mode=0
try:
    with open('mode.ini', 'r') as file:
        mode=int(file.read())
    with open('mode.ini', 'w') as file:
        file.write("{}".format(mode))
except Exception as e:
    display.text("MAINTENANCE MODE", 16, 30, True, font_name="font5x8.bin", size=1)
    display.show()
#    raise e


cc = ConsumerControl(usb_hid.devices)
keyboard = Keyboard(usb_hid.devices)
layout=KeyboardLayoutUS(keyboard)
mouse = Mouse(usb_hid.devices)

s1 = digitalio.DigitalInOut(GP17)
s1.direction = digitalio.Direction.INPUT
s1.pull = digitalio.Pull.UP
s2 = digitalio.DigitalInOut(GP16)
s2.direction = digitalio.Direction.INPUT
s2.pull = digitalio.Pull.UP
n_s1=o_s1=s1.value
n_s2=o_s2=s2.value

s3 = digitalio.DigitalInOut(GP19)
s3.direction = digitalio.Direction.INPUT
s3.pull = digitalio.Pull.UP
s4 = digitalio.DigitalInOut(GP18)
s4.direction = digitalio.Direction.INPUT
s4.pull = digitalio.Pull.UP
n_s3=o_s3=s3.value
n_s4=o_s4=s4.value
mf = MisakiFont()
sw=[]
n_sws=[True,True,True,True,True,True,True,True,True,True]
o_sws=[True,True,True,True,True,True,True,True,True,True]

isDisplay=False

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)


def DisplayMenu(menuNo):
    global isDisplay,strs,i2c,display
    i2c.deinit()
    i2c = busio.I2C(GP15, GP14)
    display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3C)

    display.fill(0) # 表示内容消去
    y=1
    for str in strs[menuNo]:
        i=0
        for c in str:
            d = mf.font(ord(c))
            show_bitmap(i,y,d)
            i+=8
        y+=10
    display.show()
    isDisplay=True


strs=(
    ("――モード1 スタンダード――",
     "1.コピー 2.カット",         #Ctrl+C,Ctrl+X
     "3.はりつけ4.すべてせんたく",   #Ctrl+V,Ctrl+A
     "5.やり直し6.再実行",       #Ctrl+Z,Ctrl+Y
     "7.デスク 8.ウィンドウ閉じる", #Win+D,Ctrl+W
     "上ボリューム下Ctrl+ホイール",),
    ("――モード2 YouTube――",
     "1.停止再生 2.ミュート",      #k,m
     "3.10秒進む4.10秒もどる",  #l,j
     "5.前フレーム 6.次フレーム",    #.,,
     "7.全画面8.小ウィンドウ",      #f,i
     "上.ボリューム 下.明るさ",),
    ("――モード3 Winキー ――",
     "1.ゲームバー 2.文字起こし", #G,H
     "3.設定画面 4.Winロック",   #I,L
     "5.画面設定6.クリップりれき",       #P,V
     "7.拡大表示 8.縮小表示",   #+,-
     "上マウス左右 下マウス上下",),
    ("――モード4 メッセージ送信 ――",
     "1.Hello 2.Bye", 
     "3.OK    4.NG", 
     "5.login 6.logout",
     "7.prev  8.next",
     "上マウス左右 下マウス上下",),
    )
#command 1:kbd.press&release,2:cc.press&release,3:layout.write,4:Mouse.X,5:Mouse.Y,6:MouseWheel,7:Ctrl+MouseWheel,8:MenuChange,9:MenuDisplayOn/OFF
btn_cmds=((
    (1,(Keycode.LEFT_CONTROL,Keycode.C,)),(1,(Keycode.LEFT_CONTROL,Keycode.X,)),
    (1,(Keycode.LEFT_CONTROL,Keycode.V,)),(1,(Keycode.LEFT_CONTROL,Keycode.A,)),
    (1,(Keycode.LEFT_CONTROL,Keycode.Z,)),(1,(Keycode.LEFT_CONTROL,Keycode.Y,)),
    (1,(Keycode.WINDOWS,Keycode.D,)),(1,(Keycode.LEFT_CONTROL,Keycode.W,)),
    (8,),(9,)),
   (
    (1,(Keycode.K,)),(1,(Keycode.M,)),(1,(Keycode.L,)),(1,(Keycode.J,)),
    (1,(Keycode.PERIOD,)),(1,(Keycode.COMMA,)),(1,(Keycode.F,)),(1,(Keycode.I,)),
    (8,),(9,)),
   (
    (1,(Keycode.WINDOWS,Keycode.G,)),(1,(Keycode.WINDOWS,Keycode.H,)),(1,(Keycode.WINDOWS,Keycode.I,)),(1,(Keycode.WINDOWS,Keycode.L,)),
    (1,(Keycode.WINDOWS,Keycode.P,)),(1,(Keycode.WINDOWS,Keycode.V,)),(1,(Keycode.WINDOWS,Keycode.KEYPAD_PLUS,)),(1,(Keycode.WINDOWS,Keycode.KEYPAD_MINUS,)),
    (8,),(9,)),
   (
    (3,"Hello"),(3,"Bye"),(3,"OK"),(3,"NG"),(3,"login"),(3,"logout"),(3,"prev"),(3,"next"),(8,),(9,))
   )
#1:NormalWheel,2:Ctrl+Wheel,3:Volume,4:Brightness,5:MouseX,6:MouseY
rotally_cmds=((3,2),(3,4),(5,6),(5,6))


def buttonAction(button):
    global mode
    global isDisplay
    global btn_cmds
    global display
    global keyboard,cc,layout
    if btn_cmds[mode][button][0] == 1:
        keyboard.press(*(btn_cmds[mode][button][1]))
        keyboard.release(*(btn_cmds[mode][button][1]))
    elif btn_cmds[mode][button][0] == 2:
        cc.press(*(btn_cmds[mode][button][1]))
        cc.release(*(btn_cmds[mode][button][1]))
    elif btn_cmds[mode][button][0] == 3:
        layout.write(btn_cmds[mode][button][1])

    elif btn_cmds[mode][button][0] == 8:
        mode= (mode +1 )%4
        DisplayMenu(mode)
        with open('mode.ini', 'w') as file:
            file.write("{}".format(mode))
    elif btn_cmds[mode][button][0] == 9:
        if isDisplay :
            display.fill(0)
            display.show()
            isDisplay=False
        else :
            DisplayMenu(mode)

#1:NormalWheel,2:Ctrl+Wheel,3:Volume,4:Brightness,5:MouseX,6:MouseY
def rotallyAction(kind,direction):
    global mode
    global rotally_cmds
    global keyboard,cc,mouse
    if rotally_cmds[mode][kind] == 1:
        mouse.move(0,0,direction)
    elif rotally_cmds[mode][kind] == 2:
        keyboard.press(Keycode.LEFT_CONTROL)
        mouse.move(0,0,direction)
        keyboard.release(Keycode.LEFT_CONTROL)
    elif rotally_cmds[mode][kind] == 3:
        if direction > 0 :
            cc.send(ConsumerControlCode.VOLUME_INCREMENT)
        else :
            cc.send(ConsumerControlCode.VOLUME_DECREMENT)
    elif rotally_cmds[mode][kind] == 4:
        if direction > 0 :
            cc.send(ConsumerControlCode.BRIGHTNESS_INCREMENT)
        else :
            cc.send(ConsumerControlCode.BRIGHTNESS_DECREMENT)
    elif rotally_cmds[mode][kind] == 5:
        mouse.move(direction,0,0)
    elif rotally_cmds[mode][kind] == 6:
        mouse.move(0,direction,0)

for _gpio in GP0,GP1,GP2,GP3,GP4,GP28,GP27,GP26,GP5,GP6:
    s = digitalio.DigitalInOut(_gpio)
    s.direction = digitalio.Direction.INPUT
    s.pull = digitalio.Pull.UP
    sw.append(s)
    try:
        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 :
                    rotallyAction(0,1)
                else:
                    rotallyAction(0,-1)
                o_s1,o_s2=n_s1,n_s2

            nn_s3 , nn_s4= s3.value , s4.value
            if (nn_s3 != n_s3 or nn_s4 != n_s4) :
                n_s3,n_s4 =nn_s3,nn_s4
            elif(n_s3 != o_s3 or n_s4 != o_s4) :
                if n_s3 ^ o_s4 == 0 :
                    rotallyAction(1,1)
                else:
                    rotallyAction(1,-1)
                o_s3,o_s4=n_s3,n_s4
            for i in range(10):
                n_sws[i] = sw[i].value
                if ((o_sws[i]==True)  and (n_sws[i]==False)):
                    buttonAction(i)
                o_sws[i]=n_sws[i]
                time.sleep(0.0002)
    except KeyboardInterrupt:
    #        raise
            pass
    except Exception as e:
            pass


2025年2月24日月曜日

Raspberrypi picoでのHIDキーアサイン画面表示案

キーアサインと画面表示の案。
一番左の黒のボタンはモードや画面の切り替えに使うとして、8個のキーとロータリーエンコーダのキーアサイン案
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)

strs=(
    ("――モード1 スタンダード――",
     "1.コピー 2.カット",         #Ctrl+C,Ctrl+X
     "3.はりつけ4.すべてせんたく",   #Ctrl+V,Ctrl+A
     "5.やり直し6.再実行",       #Ctrl+Z,Ctrl+Y
     "7.デスク 8.ウィンドウ閉じる", #Win+D,Ctrl+W
     "上ボリューム下Ctrl+ホイール",),
    ("――モード2 YouTube――",
     "1.停止再生 2.ミュート",      #k,m
     "3.10秒進む4.10秒もどる",  #l,j
     "5.前フレーム 6.次フレーム",    #.,,
     "7.全画面8.小ウィンドウ",      #f,i
     "上ボリューム下Ctrl+ホイール",),
    ("――モード3 Winキー ――",
     "1.ゲームバー 2.文字起こし", #G,H
     "3.設定画面 4.Winロック",   #I,L
     "5.画面設定6.クリップりれき",       #P,V
     "7.拡大表示 8.縮小表示",   #+,-
     "上マウス左右 下マウス上下",),
    )
mf = MisakiFont()
y=1
for str in strs[2]:
    i=0
    for c in str:
        d = mf.font(ord(c))
        show_bitmap(i,y,d)
        i+=8
    y+=10
display.show()


表示はこんな感じ。

KeyboardLayout.writeを使って文字列を送出することもできるけど、使う場面がないかな。

ロータリーエンコーダのよみとり(エッジ変化の読み取り)

前回までロータリーエンコーダのON,OFFの状態
を見ていたが、今回はOFF→ON(立ち上がり)ON→OFF(立ち下り)
の変化を見てみることにする。
→状態変化をイベントとして受け取ることにより、circuitpythonが
(バタつきなく)正しく読み取っていることを期待するとともに、
よくわからない待ち時間の考慮をなくす。

その前にロータリーエンコーダの仕組みを復習。
ロータリーエンコーダは、それを回すと、
...OFF→ON→ON→OFF→OFF→ON..と状態を4つで
1周期を繰り返すスイッチが2個あり、
それぞれのスイッチは他方より4分の1周期ずれているもの。

前回はスイッチを回したときのスイッチの状態に着目して、
一方のスイッチの状態ともう片方の直前の状態をを比較したときに、
同じであれば順方向、逆であれば逆方向となる都合の良い動きに着目し、
それらを排他的論理和を取ることにより方向を調べていた。

今回はスイッチを回したときのスイッチの変化に着目して、
同時に両方のスイッチは変化しない(スイッチの変化は1個づつ)、
自身の今回の変化(立ち上がり、立ち下り)の方向と
相手側の前回の変化の方向を比較し、
自身がSW1なら過去のSW2の方向変化が同じであれば順方向、違えば逆方向
自身がSW2なら過去のSW1の方向変化が同じであれば逆方向、違えば順方向
となる都合の良い動きに着目する。
前回は起動直後の状態をとれたので、最初からデータを信用できたが、
今回はプログラム起動直前の状態変化は取れないため、
最初の2カウント分の読み取りは破棄する。

ま、このページのものを参考にして
https://learn.adafruit.com/key-pad-matrix-scanning-in-circuitpython/keys-one-key-per-pin

自分用に書き換えたのが以下のプログラム
# SPDX-FileCopyrightText: 2022 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import board
import keypad

KEY_PINS = (
    board.GP0,
    board.GP1,
    board.GP2,
    board.GP3,
    board.GP4,
    board.GP28,
    board.GP27,
    board.GP26,
    board.GP5,
    board.GP6,
    board.GP16, #RE1S1
    board.GP17, #RE1S2
    board.GP18, #RE2S1
    board.GP19, #RE2S2
)

keys = keypad.Keys(KEY_PINS, value_when_pressed=False, pull=True)
waitcount=2
isRE1Sw1Raise=False
isRE1Sw2Raise=False
while True:
    event = keys.events.get()
    if event:
        # A key transition occurred.
        #print(event)

        if event.pressed:
            # Turn the key blue when pressed
            if event.key_number == 10 :
                isRE1Sw1Raise=False
                if waitcount > 0:
                    waitcount -=1
                    continue
                if isRE1Sw1Raise == isRE1Sw2Raise : print("UP")
                else : print("DOWN")
            elif event.key_number == 11 :
                isRE1Sw2Raise=False
                if waitcount > 0:
                    waitcount -=1
                    continue
                if isRE1Sw1Raise != isRE1Sw2Raise : print("UP")
                else : print("DOWN")

        # This could just be `else:`,
        # since event.pressed and event.released are opposites.
        if event.released:
            if event.key_number == 10 :
                isRE1Sw1Raise=True
                if waitcount > 0:
                    waitcount -=1
                    continue
                if isRE1Sw1Raise == isRE1Sw2Raise : print("UP")
                else : print("DOWN")
            elif event.key_number == 11 :
                isRE1Sw2Raise=True
                if waitcount > 0:
                    waitcount -=1
                    continue
                if isRE1Sw1Raise != isRE1Sw2Raise : print("UP")
                else : print("DOWN")


結果、クリックなしのロータリーエンコーダでゆっくり目に回すといい感じに読み取るが、
速く回すと、逆方向の検知がされてしまう。

クリックありの方で、コメントのprintを有効にして、その他のprintを無効にした結果、以下のようにまったく使い物にならず。

















期待していたのは、11と10が交互に出るものなんだけどそうなっていない。
10が多いということは、11を拾えていないのか10がバタついているのか、その両方なのかよくわからない。

IncrementalEncoderでクリックありのものはdivisorを4にクリックなしのものはdivisorを2で使うのがよいのかなぁ。


2025年2月23日日曜日

Raspberry Pi Picoに接続したSSD1302に美咲フォントで表示

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()



表示させるとこんな感じ。
フォントの大きさの自由度はあまりないけど、これでいいんじゃないかな?