Pythonからお気楽にLチカ (AKI-USBIO2)


はじめに

python(PC)から簡単にLEDをOn/Off出来れば以外と使えるのでは?
例えば、何がしらWarningやErrorが自動実行中に起こった場合に外部にLEDで知らせる事が出来ればいいのでは?という動機だったりします。
実際メールとかPC内でお知らせという方法もあるのですが、例えば画面を消している状態や、メール程ラグがあると嫌だなという場面等です。

出来上がりはこんな感じです

構成

1. ハード
LEDを簡単に実装出来て且つ、PCから制御しやすいのは何があるのか調べてみました。まずはGPIOがあるものは?(いちから作れば何でもありですが)安いワンチップマイコンでPCと簡単に繋げられる物は?と調べると秋月電子からUSBIO2がありました。
USB-IO2.0(AKI)
これだとマイコン側のソフト開発をしなくてよく、且つHIDとして見えるようなので制御しやすそうです。
後、ドライブ出来る電流値が
1ピン出力電流 Max 25mA
合計出力電流 Max 80mA
なので、高輝度LEDとか使えば結構ドライブできそうです。
LEDをGPIO(J1-0 - Ja-4)それぞれにLED-抵抗(220)-半固抵抗(5K)
と5つ繋げています。(回路図という程のものでもないので回路図は省略します)
1ピンの最大電流は
5/220 = 0.02272 < 25mA
合計電流は
5/220*5 = 0.1136363
・・・手持ちの部品のせいで最大設定時に少しオーバーしますが、実際は半固抵抗で全部一度に最大(0)にする事は無いという判断でしています。(実際ブレッドボードで実験した時はそこそこ大きな値にしないと眩しすぎましたw 抵抗は500とかでも十分な気もします)
後、これは
USB-IO2.0 Km2Net Inc.
の機能縮小版なので、サンプルプログラムとかあってよさそうです。
(実際助かりました)

2. ソフト
pythonでUSB(HID)を簡単に使えるライブラリは?と探すと
cython-hidapi
It works on Linux, Windows XP and OS X.
という事でマルチプラットフォーム対応らしい(今回はWindows10で
動作確認しています)
PyUSBとかの方が有名そうですが、hid限定の場合こちらの方が簡単そうです。

動作確認

1.AKI-USBIO2が認識しているかのテスト
下記scriptを実行してみます。

test.py
import hid
# Check all  usb device
for d in hid.enumerate(0, 0):
    keys = d.keys()
    #keys.sort()
    for key in keys:
        print ("%s : %s" % (key, d[key]))
    print ("")

ずらーとHIDデバイス(マウスとかキーボードとか)が出て来るので、その中に以下のようなAKI-USBIO2があれば成功です。


product_id : 289
serial_number :
manufacturer_string : Km2Net Inc.
usage : 1
interface_number : -1
product_string : USB-IO2.0
vendor_id : 4946
release_number : 1


ここで不思議な現象が。
単純にHIDデバイスを認識しているのであればAKI-USBIO2が繋がってなくてもキーボードとかマウスとか認識しそうですがなぜかAKI-USBIO2が繋がってないと他のデバイスも認識しません。(open出来るデバイスが1つでも無いと認識しない仕様なのかな??ドキュメントでも見つけられない・・)

1.pythonのLED On/Offスクリプト作成
実は、ここで少しばかり苦労したとは・・・・。
上で、書いていますがKm2Netさんのページに仕様があるのですが。。。
USB-IO Family 制御コマンドレイアウト
これを見て、通信データにこのフォーマットに合わせてコマンドを載せればOKかなと思ったのですがそのままでは動作してくれません。サンプルのソースを見てみるとどうも送信データを1バイトずらす(先頭に0を入れる)必要があるとわかるまで苦労しました。(この仕様は???)
後、データの受け渡しコマンドが一回でデジタル入出力してしまうコマンドしかないのは不便かなという点です。LED1つの状態を変えようとしても事前にUSBIO2側のポートの状態(他のLEDの点灯状態)を知る方法がないのです。
ただ今回はLEDなのでほんの一瞬消えても気づきにくい事を利用して(?)最初にダミーデータ(all 0)でライトを行ってその時帰ってきたデータ(ダミーを書き込む前の状態)に変更したいbitだけ変更して再度書き込む事を行っています。

usbio2_test.py

import hid
import time
import math

VENDOR_ID = 0x1352       # Km2Net
PRODUCT_ID = 0x0121  # USB-IO2.0(AKI)
# Number of ports
N_PORT1 = 8
N_PORT2 = 4
N_PORT = N_PORT1 + N_PORT2

# Command list
# Detail to see : http://km2net.com/usb-fsio/command.shtml
CMD_READ_SEND = 0x20
CMD_SYSCONF_READ = 0xf8
CMD_SYSCONF_WRITE = 0xf9
MAX_CMD_LENGTH = 64

print ("Opening device")
h = hid.device()
h.open(VENDOR_ID, PRODUCT_ID)
print ("Manufacturer: %s" % h.get_manufacturer_string())
print ("Product: %s" % h.get_product_string())
print ("Serial No: %s" % h.get_serial_number_string())

def send2rcv(p1,p2):
        data = [0] * 64
        data[0] = 0
        data[1] = CMD_READ_SEND
        data[2] = 1
        data[3] = p1
        data[4] = 2
        data[5] = p2
        data[63] = 0
        h.write(data)
        return(h.read(64))

def modWrite(p1,p2,mode):       #0:off 1:on
        rcvd = send2rcv(0,0)    #dummy write
        if(mode == 0):
                p1 = rcvd[1] & ~p1
                p2 = rcvd[2] & ~p2
        else:
                p1 = rcvd[1] | p1
                p2 = rcvd[2] | p2
        rcvd_dummy = send2rcv(p1,p2)
        return(rcvd)

def readP():
        rcvd = send2rcv(0,0)    #dummy write
        p1 = rcvd[1]
        p2 = rcvd[2]
        rcvd_dummy = send2rcv(p1,p2)    #dummy write
        return(rcvd)


#All on
rcv = modWrite(255,0,1)
time.sleep(2)

#shift 0 (off)
for n in range(5):
        rcv = modWrite(pow(2,n),0,0)
        time.sleep(0.5)
        rcv = modWrite(pow(2,n),0,1)

#All off
rcv = modWrite(255,0,0)
time.sleep(2)

#shift 1 (on)
for n in range(5):
        rcv = modWrite(pow(2,n),0,1)
        time.sleep(0.5)
        rcv = modWrite(pow(2,n),0,0)

#same on at once
for n in range(32):
        rcv = modWrite(n,0,1)
        time.sleep(0.5)
        rcv = modWrite(n,0,0)
        time.sleep(0.5)

最後に

実はこの動作実験とスクリプトの元は2016年10月頃に作成していたのですが、自分の忘備録になればと書いてみました。