安価で低消費電力の無線マイコン、TWE-LiteのシリアルデータをRaspberry Pi上のpythonで受け取るメモ。
構成
| TWE-Lite | - | raspberry Pi |
まず、Raspberry Piのシリアルを使えるようにする。
この辺を見るといいです。
http://qiita.com/ryugyoku/items/bf5fd10512c84a55d030
テスト
TWE-LiteのTX,RDをRaspberry Piのシリアルポートに接続します。
試しにminicomで読み込んでみましょう。
$ sudo minicom -b 115200 -o -D /dev/ttyAMA0
minicomはsudo apt-get install minicomでインストールできます。
-Dオプションがターミナルの場所。
USBシリアル変換を使用している場合はここを/dev/ttyUSB0とかに変えればいいです。
minicom へようこそ 2.7
オプション: I18n
コンパイルされた日時は: Jan 12 2014, 05:42:53.
ポート /dev/ttyAMA0, 01:54:51
CTRL-A Z を押すと、説明画面になります。
:0F8115015481012CFB00A387000BB0150101FFFFFFFFFF66
:0F8115015181012CFB00A389000BB0150101FFFFFFFFFF67
:0F8115015481012CFB00A38B000BAC150101FFFFFFFFFF66
:0F8115015181012CFB00A38D000BAC150101FFFFFFFFFF67
:0F8115015481012CFB00A38F000BB0150101FFFFFFFFFF5E
:0F8115014E81012CFB00A393000BAC150101FFFFFFFFFF64
このような物が出たら接続は正常です。
Ctrl+A,Qで終了。
こいつをPythonで受け取ってパースしましょう
シリアル受け取り
シリアルの取得はpyserialを使用します。
$ pip3 install pyserial
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> ser = serial.Serial('/dev/ttyAMA0',115200)
>>> ser.readline()
b':0F8115014E81012CFB00A437000BAC150101FFFFFFFFFFBF'
>>> ser.close()
<bound method Serial.close of Serial<id=0xb68fd550, open=True>(port='/dev/ttyAMA0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)>
>>>
チェックサム計算
公式によると、コードの最後の1バイトがチェックサムとなっています。
計算方法は、先頭行から1バイトずつ加算していき、和の下位1バイトの補数がチェックサムとなっています。
つまり、':0F8115014E81012CFB00A437000BAC150101FFFFFFFFFFBF'の場合のチェックサムは最後のBFで、
0x0f+0x81+....0xff+0xff = 0x941 これの下1バイトの0x41 + 0xBF = 0xa00となります。
ということは、最初の":"を除く全桁を1バイトずつ加算した総和の下1バイトはかならず0になるということです。
python的な計算をすると、
Python 3.5.2 (default, Jun 28 2016, 08:46:01)
[GCC 6.1.1 20160602] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> string = ':0F8115014E81012CFB00A437000BAC150101FFFFFFFFFFBF'
>>> string = string[1:] #冒頭の:を除く
>>> lst = [ int(string[i:i+2], 16) for i in range(0, len(string), 2) ] #二桁ずつ切り取って16進数として数値化
>>> s = sum(lst) #総和を取る
>>> hex(s & 0b1111)
'0x0'
>>>
0になりましたね。話のわかるやつだ
パース
データフォーマットはマニュアルを参照。
# coding: utf-8
#!/usr/bin/env python3
def parse(string):
if string[-2:] == b"\r\n" : string = string[:-2]
if string[0] == ord(':') : string = string[1:]
lst = [ int(string[i:i+2], 16) for i in range(0, len(string), 2) ]
if sum(lst) & 0xff == 0:
return lst
return False
if __name__ == '__main__':
import serial
s = serial.Serial('/dev/ttyAMA0',115200)
l = s.readline()
lst = parse(l)
print (lst)
SOURCE_DEVIDE = lst[0]
COMMAND = lst[1]
PACKET = lst[2]
PROTOCOL = lst[3]
LQI = (7 * lst[4] - 1970 ) / 20
SN = lst[5] << 24 | lst[6] << 16 | lst[7] << 8 | lst[8]
DESTINATION_ID = lst[9]
TIMESTAMP = lst[10] << 8 | lst[11]
RELATED = True if lst[15] % 1 else False
VOLTAGE = lst[13] << 8 | lst[14]
NONE = 0
DIN = lst[16]
DCHANGE = lst[17]
ACOMP = lst[22]
AD1 = (lst[18] * 4 + ACOMP) * 4
AD2 = (lst[19] * 4 + ACOMP) * 4
AD3 = (lst[20] * 4 + ACOMP) * 4
AD4 = (lst[21] * 4 + ACOMP) * 4
SUM = lst[-1]
D1 = True if lst[16] & 0b0001 else False
D2 = True if lst[16] & 0b0010 else False
D3 = True if lst[16] & 0b0100 else False
D4 = True if lst[16] & 0b1000 else False
あとはご自由に。
ライブラリですが、今見たら酷い出来だったのでそのうち改造します