WioNodeの非同期(websocket)処理をPythonで書いてみる

いつかやろうと思っていたんだけどできなかったものの消化です。

はじめに

WioNodeを使用していると単純にcurlコマンドなどを使ってセンサーの状態の取得ができるのですが、センサーによっては常時通信を行いながら、 そのイベントのトリガーをJSON形式で受信するというタイプのものもあります。

例えば、ButtonやGestureなどがそれにあたります。

WioアプリのAPIページにも使い方の記載されているので大きく問題はないのですが、WebSocketのサンプルはJavaScriptのみなので、JSがそんなに得意ではない自分には できればPythonから使えないかなと考えてしまいます。

f:id:ueponx:20181228154259p:plain

今回のエントリーでは、そのPythonで書いてみようというものです。 PythonのWebSocketのライブラリは調べてみると2つほどあったので、今回はその両方でチャレンジしました。

前処理

WioNodeの設定に関しては、過去のエントリーなどを参照いただければと思います。

【参考】 uepon.hatenadiary.com

今回は、ButtonのGroveモジュールをWioNodeに接続してテストを行います。

f:id:ueponx:20181228163141p:plain

Buttonの設定を行ってFirmwareの書き換えをし、画面右上のハンバーガーメニューからViewAPIのページを表示します。APIページの一覧の中にJavaScriptで書かれたWebSocket経由でのイベントの取得サンプルがあります。 ソース内の*はアクセスキーになりますので伏せてあります。

f:id:ueponx:20181228203243p:plain

f:id:ueponx:20181228203501p:plain

<script>
var ws = new WebSocket('wss://us.wio.seeed.io/v1/node/event');
ws.onopen = function() {
    ws.send("*****************************");
};
ws.onmessage = function (evt) {
    alert(evt.data);
};
</script>

これと同じ処理を行うコードをPythonで書いていくことになります。

PythonのWebSocketのライブラリモジュールを調べてみると

WebSocketのライブラリモジュールをググってみると2つほどヒットします。

  • websocket-client
  • websockets

個人的には、websocket-clientをオススメしますが、非同期的なwebsocketsも今っぽい感じで悪くないかなという印象です。 ただ、websocket-clientがオススメなのはJSなどとの類似性があるのでソースの可読性が高い点が理由かなと思います。(移植がらく)

websocket-clientを使用してコードを書いてみる

インストールはpipでOKです。Python 2.7とPython 3.4以上に対応しています。

【Windows10の場合】

$ pip install websocket-client

【RaspberryPiの場合】

$ pip3 install websocket-client

インストール時には依存性がある別のパッケージがインストールされます。詳細は以下のページを御覧ください。

pypi.org

このページにあるサンプルを改良していきます。*はキーの部分なので伏せてあります。

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")

def on_open(ws):
    ws.send('*******************')

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp('wss://us.wio.seeed.io/v1/node/event',
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

変更点としては - WebSocketのURL - WebSocketの接続後に接続キーを送信する(Openイベントが発生したタイミング)

あたりかなと思います。

実行すると以下のように動作します。WioNodeにつけたButtonを押すとJSONデータが受信されます。 JSONデータにはイベント名(今回は"button_pressed")接続されたpin番号が受信されます。

>> WioNode WEB Socket server connected.
< {"msg": {"button_pressed": "3"}}
< {"msg": {"button_pressed": "3"}}
< {"msg": {"button_pressed": "3"}}

Windows10のAnaconda環境でも、RaspberryPiでもうまく動いているようです。

f:id:ueponx:20181228175843p:plain

f:id:ueponx:20181228182235p:plain

JavaScriptと同じようにonほげのような感じでイベントハンドラの登録ができるような感じです。かなり分かりやすい印象です。

websocketsを使用してコードを書いてみる

インストールはpipでOKです。Python 3.4以上に対応していますが、それ以下でも書き方次第で動くかも?

【Windows10の場合】

$ pip install websockets

【RaspberryPiの場合】

$ pip3 install websockets

詳細は以下のページを御覧ください。

pypi.org

このページのサンプルに改良していきます。*はキーの部分なので伏せてあります。

#!/usr/bin/env python
import asyncio
import websockets

async def loop():
    async with websockets.connect('wss://us.wio.seeed.io/v1/node/event') as websocket:
        await websocket.send('*******************')
        print(">> WioNode WEB Socket server connected.")
        while True:
            receiveData = await websocket.recv()
            print("{}".format(receiveData))

asyncio.get_event_loop().run_until_complete(loop())

python3で使用可能となったasync/awaitasyncioを使用しています。 asyncioの持つイベントループにWebSocketの接続をいれてデータの送受信をする形になります。 非同期処理なんですが、同期的っぽくかけます。 他のライブラリなどとは記載の仕方が違う点は特殊ですが、分かりやすいといえば分かりやすいかもって感じです。 ただ、Pythonのバージョンが限定されるので、用途が限られるかなという印象はあります。

f:id:ueponx:20181228184520p:plain

f:id:ueponx:20181228185224p:plain

こちらもWindows10のAnaconda環境でもRaspberryPiでも動作していましたが、RaspberryPiではCtrl+CのInterruptでエラーメッセージが大量に表示されるので微妙なのかも。 RaspberryPiのPython3のバージョンは3.5.3でした。

おわりに

ようやくやれてなかったことをまとめられました。JavaScriptもう少しかけるように慣れればいいんでしょうけど個人的にはPythonも捨てがたいです。

/* -----codeの行番号----- */