JetsonでUSB-IOを使う

#はじめに
JetsonNano/JetsonXavierNXには、RasberryPI互換のGPIOがあります。
一方でJetsonで多く使われるカメラはUSBに接続され、USBケーブルはリピータなどをいれることでかなり距離を稼ぐことができます。
カメラの方向のコントロールなどをGPIOを介して行った場合、USBケーブルと別にパラレルインタフェースに必要なGPIOを引き回す必要があり煩雑です。
今回はUSBに[ＵＳＢ接続デジタル入出力モジュール　ＵＳＢ－ＩＯ２．０（ＡＫＩ）](http://akizukidenshi.com/catalog/g/gM-05131/)をつないでパラレル入出力を行うことで、カメラと同様にUSBケーブルだけで本体と距離のあるなにかをコントロールします。＃する予定です
難しいとこはないですが、setup.pyの変更部分がわからず苦労したので、備忘録として。。

#やったこと
[Pythonからお気楽にLチカ　（AKI-USBIO2）](https://qiita.com/non0/items/1b6e02e8f8b3f1d36bfb)を参考に[cython-hidapi](https://github.com/gbishop/cython-hidapi)をビルド、インストールしました。
環境はJetsonXavierNX + Jetpack4.4 (+Docker )です。

#事前準備
cython , libusb , libusb-devをインストールする必要があります。

```
apt update
apt install libusb-1.0.0
apt install libusb-dev
apt install python3-pip
pip3 install cython
git clone https://github.com/gbishop/cython-hidapi.git
```

#ビルド
python3 setup.py build
python3 setup.py install
でビルド、インストールが出来ますが、jetpack4.4上で実装するとinstall時に
“hid.so: undefined symbol: libusb_open”
と言われます。
[import hid in python results in error “hid.so: undefined symbol: libusb_open](https://stackoverflow.com/questions/12140859/import-hid-in-python-results-in-error-hid-so-undefined-symbol-libusb-open)
にあるようにsetup.pyを変更します。
setup.pyのなかのsetup部分を

```
setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("hid", ["hid.pyx", "hid-libusb.c"],
                  libraries=["usb-1.0", "udev", "rt"])]
)
```
にし、usb-1.0とudevを明記します。
でもって、

```
python3 setup.py build
sudo python3 setup.py install 
```
でインストールが出来ます。

# Python3
実際に使おうとすると

```python3:hid.pyx
def write(self, buff):
      '''Accept a list of integers (0-255) and send them to the device'''
      buff = ''.join(map(chr, buff)) # convert to bytes
      cdef unsigned char* cbuff = buff # covert to c string
      return hid_write(self._c_hid, cbuff, len(buff))
```
python3だとbuffがstringになるので
      cdef unsigned char* cbuff = buff # covert to c string
がエラーになります。
cythonがよくわかってないんですが。。。適当に

```python3:hid.pyx
def write(self, buff):
      '''Accept a list of integers (0-255) and send them to the device'''
      buff = bytearray( buff )
      cdef unsigned char* cbuff = buff # covert to c string
      return hid_write(self._c_hid, cbuff, len(buff))
```
bytearrayに変換するようにしました。
同じのがあと一カ所あります。使わないですが

# Dockerでの実行
私自身は開発をDocker上でやっているので、Dockerでの実行時には
コンテナ上でusbのhidデバイスをブラウズ、アクセス出来る必要があります。
docker run に以下のどちらかをつけて起動する必要があります。
前者は、デバイス全部がコンテナからみえるようになり、
後者はUSBデバイスへの直接アクセスが出来るようになります。（/dev/videoなどはたとえUSB上でも別途オプションが必要なままです）

```
--previledged
もしくは
--device /dev/bus/usb:/dev/bus/usb
```
がいります。

#利用
私は出力しかつかわないので、入力はためしてません
買った直後はP2が入力？かな？なので、システム設定用フラッシュロム書き込みコマンドで書き換える必要があります。ただ、Pythonで実装しても挿抜しないと反映されないため、あまり楽しくないのでWindowsから付属のユーティリティで全部出力に書き換えることとしPython側では実装していません。どうせ購入後1度しかつかいませんので。
必要であれば、下記のコード上のCMD_CONFIG_WRITEを実装して
[km-net](http://km2net.com/usb-fsio/command.shtml)さんのページのコマンドで4バイト目と5バイト目を書き換える必要があります。
出力に設定してあれば、下記のコードで
import USBIO
usb = USBIO()
usb.write( 0x55,0x55 )
とかやるとP1に0x55 P2に0x5が出力されます。＃HIDデバイスをあけるのにSUDOがいります。
もしくは
import USBIO
usb = USBIO()
usb.setPinLevel( 5 , 0 ) # 5はピン番号で0−11、0はレベルで0でLow,1でHigh
usb.outputToPin() # ここでここまでのsetPinLevelが一度に反映されます。
＃＃HIDデバイスをあけるのにSUDOがいります。

```python3:USBIO.py
import hid

class USBIO:
    VENDOR_ID = 0x1352       # Km2Net
    PRODUCT_ID = 0x0121  # USB-IO2.0(AKI)
    lock = threading.Lock()
# Command list
    CMD_READ_WRITE = 0x20
    CMD_CONFIG_READ = 0xf8
    CMD_CONFIG_WRITE = 0xf9
    usb = hid.device(VENDOR_ID, PRODUCT_ID)
    def __init__(self  ):
        #writePin( 0,0 )
        self.pin = [0] *12

    def writePin(self,p1,p2):   # Prioro to use this , all pins need to be configured as output pins
        data = [0] * 64
        data[0] = self.CMD_READ_WRITE
        data[1] = 1
        data[2] = p1
        data[3] = 2
        data[4] = p2
        data[63] =89    # dummy to confirm USB-IO recognition
        self.lock.acquire() # make sure just a thread using USB
        self.usb.write(data)
        self.lock.release()
        return(self.usb.read(64))

        
    def setPinLevel(self, pin_no , level ):
        self.pin[pin_no] = level

    def outputToPin( self ):
        p1 =      self.pin[0]    + self.pin[1] * 2 + self.pin[2] * 4 + self.pin[3] *8
        p1 = p1 + self.pin[4]*16 + self.pin[5] * 32+ self.pin[6] * 64+ self.pin[7] *128
        p2 =      self.pin[8]    + self.pin[9] * 2 + self.pin[10]* 4 + self.pin[11]*8
        self.writePin( p1 , p2 )
```
LockはこのあとSteppingモータ回した時に1個目と2個目をパラで動かすためのコードなので、単体ではどうでも良いです。

#修正部分のリスト
Jetsonだからなのか、Linuxだからなのか、時間がたったからなのか、[Pythonからお気楽にLチカ　（AKI-USBIO2）](https://qiita.com/non0/items/1b6e02e8f8b3f1d36bfb)の通りには動きませんでした。
- Openを独立して呼んでますが、ここがかわってます。
- コマンドを送るときにダミーの1バイトがいることになっていますが、必要ありませんでした。
- python3でやるとcython-hidのオリジナルだとエラーになりました。（python3の項参照)

# 予定
1，USB-IOのコントロール　ーいまここ
2，USB-IO経由でステッピングモータのコントロール 
3，レーザポインタを物体検出するためのYOLO学習データの作成
4，レーザポインタを追いかけるカメラ