TOCOS TWE無線タグのセンサデータを MQTT Broker に送信、TWE-MQTT Gateway

●概要

リモートに設置した TOCOS TWE無線タグ・デバイスのセンサデータを MQTT Broker に送信するシステム構築例を紹介します。このシステムは TWE-Lite や TWE-Lite-2525A 等で動作する無線タグアプリ・ファームウエア(Samp_Monitor) を動作させて、これらのリモートデバイスに接続したセンサデータを MQTT Broker に送信します。

●子機の配線

リモートに設置する無線タグデバイス・子機には TWE-Lite DIP を使用しています。東京コスモス電機のホームページで公開されている TWE-Zero アプリの “無線タグアプリ(Samp_Monitor)” を書き込んで使用します。

無線タグファームウエアに設定するセンサ種別は、デフォルト設定のアナログセンサモードで使用しています。アナログ入力AI3(a2タグ) ピンには光センサを接続しています。

A/D の入力電圧が 0-2.4V なので、これを超えないようにフォトダイオードの負荷抵抗を調整してください。上記の回路では2つの5.6K の抵抗で分圧したものを AI3 に接続しています。

子機は複数同時に使用することもできます。また、ファームウエアに設定するセンサー種別を変えて、 I2C 接続の温度センサや気圧センサなどを使用することもできます。この場合には、自動的に MQTT Broker に送信する JSON フォーマットがのタグ名が対応する名前に変更されます。

●親機の配線

子機からのセンサデータを受信する親機を準備します。子機と同様に TWE-Lite DIP デバイスを使用しています。PC の USB ポートに直接接続できる スティックタイプの TWE デバイスや TWE ライタデバイスを親機として使用できます。ここでは、TWE-Lite DIP をブレッドボード上に配置して、USB シリアルアダプタ(秋月電子: AE-UM232R)経由で PC に接続しています。

子機側の TWE-Lite DIP ファームウエアの書き換えや、コンフィギュレーション設定にも上記の回路を使用します。このため、リセットボタンとプログラム書き込み用のボタンも配置しておきます。TWE-Lite DIP デバイスの電源は USB シリアルアダプタから供給される 3.3V を使用しています。

●親機の設定

最初に親機側の TWE-Lite DIP デバイスを設定します。購入直後の TWE-Lite DIP ファームウエアには “超簡単!TWEアプリ” が入っていますが、今回はこれを書き換えて “無線タグ専用の親機ファームウエア” を書き込みます。ファームウエアは東京コスモス電機の TWE-Zero アプリのページで公開されていますのでこれを使用します。詳しい書き込み方法はファームウエアのページを参照して下さい。

ここでは、ファームウエア書き込み後の親機側のコンフィギュレーション設定を説明します。親機の TWE-Lite DIP を USB シリアルアダプタに接続した状態で、シリアル端末ソフト(TeraTerm 等)で仮想 COM ポートに接続します。

“+” キーを3回連続で押してコンフィギュレーション画面に入ります。

最初に “Application ID” を設定します。この例では 0×11223344 を設定しています。この “Application ID” は接続する全ての子機側でも同じ値に設定します。

“Option Bits” に 0×00020000 フラグを既存の設定値と ”OR” した0×00020001 に設定しています。このフラグ設定によって、親機自身から出力されるカウンタ?(::ts=xxxx)データを抑止しています。リモートの無線タグ・デバイスから送信されるセンサデータ以外のデータレコードが親機自身から送信されて、シリアル出力のサイズが増えないようにしています。後で作成するMQTT ブローカに送信する部分の Lua スクリプト中で、これらの不要なデータレコードを取り除く処理を行いますので、このフラグを設定しなくても正常に動作します。

その他のフラグやコンフィギュレーション項目はデフォルトのままで大丈夫です。

●子機の設定

次に子機の TWE-Lite DIP デバイスを設定します。ファームウエアを書き換えますので、親機の設定で使用したブレッドボードから親機用のTWE-Lite DIP デバイスを抜いて、子機の TWE-Lite DIP デバイスを代わりに差し込みます。これらの作業をするときには、PC と接続している USB ケーブルを抜いた状態でデバイスの入れ替えをすると安全に操作できます。

親機と同様にメーカのホームページから”無線タグ専用の子機ファームウエア” を書き込みます。その後、子機のファームウエアを設定します。子機のコンフィギュレーション設定をするときだけは、TWE-Lite DIP の M2 ピンを GND に接続する必要があります。ブレッドボード上でジャンパケーブルを M2(26pin) と GND に接続してください。その後、シリアル端末ソフト(TeraTerm 等)でUSBシリアルアダプタに接続した後、ブレッドボード上のリセットボタンを押して子機ファームウエアのコンフィギュレーション画面に入ります。

“Application ID” には親機と同じ 0×11223344 を設定します。”Option Bits” にはOTA(無線経由での設定)を停止するためのフラグ 0×400 を有効にします。また、ここではセンサデータの送信間隔(Sleep Dur) を 300ms に設定していますが任意の値に設定しても構いません。

子機の設定が終了したら “save Configulation” で設定値を保存します。

全ての子機の設定を同様におこなったら、子機と親機の TWE-Lite DIP デバイスをそれぞれの本来の回路に戻しておきます。このとき、親機のブレッドボード上に配線した M2 <-> GND 間の配線を取り除くのを忘れないようにします。これで、無線タグデバイスのセットアップは完了しました。

●TWE-Lite タグデバイスの動作確認

MQTT broker への送信設定を行う前に、TWE 無線タグ・デバイスが動作しているかどうかを確かめてみます。

親機の USB シリアルアダプタにシリアル端末で再び接続してください。子機の無線タグの電源を入れると、下記の様なデータレコードが親機からシリアル送信されていればリモート側の無線タグは正常に動作しています。

*** Samp_Monitor (Parent) 1.05-5 ***
* App ID:11223344 Long Addr:81004152 Short Addr 0152 LID 00
::rc=80000000:lq=66:ct=0001:ed=81002A1E:id=0:ba=3310:a1=1922:a2=2467:p0=000:p1=000
::rc=80000000:lq=69:ct=0002:ed=81002A1E:id=0:ba=3280:a1=1845:a2=2467:p0=000:p1=000
::rc=80000000:lq=69:ct=0003:ed=81002A1E:id=0:ba=3400:a1=1891:a2=2467:p0=000:p1=000
::rc=80000000:lq=72:ct=0004:ed=81002A1E:id=0:ba=3440:a1=1975:a2=2467:p0=000:p1=000
::rc=80000000:lq=69:ct=0005:ed=81002A1E:id=0:ba=3400:a1=2035:a2=2467:p0=000:p1=000
::rc=80000000:lq=72:ct=0006:ed=81002A1E:id=0:ba=3440:a1=2105:a2=2467:p0=000:p1=000
::rc=80000000:lq=72:ct=0007:ed=81002A1E:id=0:ba=3440:a1=2120:a2=2467:p0=000:p1=000

もし、親機のファームエア設定で 0×00020000 フラグを設定していないときには、下記のようなシリアル出力になります。

::rc=80000000:lq=90:ct=0078:ed=81002A1E:id=0:ba=3440:a1=1435:a2=2467:p0=000:p1=000
::ts=435
::rc=80000000:lq=90:ct=0079:ed=81002A1E:id=0:ba=3460:a1=1442:a2=2467:p0=000:p1=000
::rc=80000000:lq=93:ct=007A:ed=81002A1E:id=0:ba=3470:a1=1435:a2=2467:p0=000:p1=000
::rc=80000000:lq=90:ct=007B:ed=81002A1E:id=0:ba=3440:a1=1418:a2=2467:p0=000:p1=000
::ts=436
::rc=80000000:lq=93:ct=007C:ed=81002A1E:id=0:ba=3450:a1=1418:a2=2467:p0=000:p1=000
::rc=80000000:lq=93:ct=007D:ed=81002A1E:id=0:ba=3460:a1=1440:a2=2467:p0=000:p1=000
::rc=80000000:lq=93:ct=007E:ed=81002A1E:id=0:ba=3460:a1=1430:a2=2467:p0=000:p1=000
::ts=437
::rc=80000000:lq=93:ct=007F:ed=81002A1E:id=0:ba=3440:a1=1420:a2=2467:p0=000:p1=000

これで、親機と子機の無線タグが正常に動作しているのを確認できました。複数の子機を同時に動作させることもできますので、この場合にはそれぞれのデバイスからのデータも表示されます。

動作確認が終了したら、シリアル端末のプログラムを終了させて COM ポートを開放してください。以降の設定で、親機からの出力される上記のシリアルデータを変換して MQTT Broker に JSON 文字列を送信します。

●TWE無線タグ・親機のシリアルデバイスを DeviceServer に登録

TWE無線タグ・親機から出力されるリモートセンサデータを取り込むために、DeviceServer にシリアルデバイスを登録します。”サーバー設定プログラム” を起動して、”SERIAL” タブを選択します。

親機が接続されている仮想 COM ポート(ここでは “COM10″) にデバイスタイプ “TWE”でシリアルデバイスを登録した様子です。デバイスの詳細設定は以下の様になっています。

デバイスタイプは “TWE” を選択します。シリアルデバイスのタイトル文字は適当な名前をつけて下さい。シリアルポート通信条件はファームウエアのデフォルト値 (15200baud, 8bit, 1stop-bit, no parity) に合わせます。

これで、親機からセンサデータを受信する度に、DeviceServer 側ではイベントハンドラスクリプト SERIAL_TWE.lua が実行されるようになります。

●MQTT エンドポイント作成

シリアルデバイスの作成に続いて、サーバー設定プログラムの “MQTT” タブを選択して、MQTT Broker との接続(エンドポイント)を作成します。

ここでは、2つのエンドポイントを作成しています。1つめが、TWE リモートセンサデータを MQTT Brokerに登録するための接続で、後の1つが、MQTT Broker からセンサデータを受信して、ログに出力したり LED 表示器に A/D 変換値を出力するための接続です。

今回は、構成をわかり易くするために MQTT Broker 接続のエンドポイントを2つに分けていますが、センサーデータ登録用と受信用のエンドポイントを1つで共有して運用しても構いません。

“サーバー設定プログラム”の “MQTT” タブを押してエンドポイントリストを表示した状態です。ここでは既にデータ登録用のエンドポイントとして、タイトル名 “センサーデータ登録” と データ取得用のエンドポイント、タイトル名 “センサーデータ取得” の2つのエンドポイントが登録されています。

“センサーデータ登録” のエンドポイントの詳細設定は以下の様になっています。

接続先の MQTT Broker は、IP アドレス192.168.100.14、ポート番号1883 で動作している RaspberryPi 上の mosquitto にしています。mosquitto MQTT ブローカについては、こちらの記事も参照してください。

“センサーデータ取得” のエンドポイントの詳細設定は以下の様になっています。

MQTT Broker の IP アドレスや ポート番号は同じですが、このエンドポイントでは起動時に購読するトピックを複数設定しています。

TWE 無線タグ・デバイスのセンサーデータは、下記のトピック名で送信されています。

”/twe/<TWE 子機のMACアドレスを表す16進数文字列>/Samp_Monitor”

全ての無線タグ子機から送信されるセンサーデータを購読するために、トピック名 “/twe/+/Samp_Monitor” を購読するように設定します。

”/+/+/io” や “/+/+/tdcp” のトピック購読は、リモートに設置した XBee デバイスやマイコンボード等からのセンサーデータ購読のために指定しています。今回はこれらからのトピック・データは使用していません。

これで、DeviceServer 側の親機のシリアル設定と MQTT エンドポイント設定は完了しました。サーバー設定プログラムの “次へ” ボタンを押して設定を反映させると DeviceServer が再起動してTWE 無線タグ親機へのシリアル接続と MQTT Broker へのエンドポイント接続が自動的に開始されます。

●MQTT Broker にTWE無線タグセンサ・データを JSON 形式で送信する

次に、DeviceServer 側で実行されているデフォルトのイベントハンドラスクリプトをエディタで変更して、MQTT Broker にセンサーデータを JSON 形式で送信するようにします。

TWE 無線タグ親機からセンサーデータを受信する毎に、SERIAL_TWE.lua イベントハンドラが実行されますので、この内容を変更して下記のようにします。

(ここで紹介しているイベントハンドラの内容はコメントアウトされた状態でインストールキットに含まれています。そのため、この記事の内容を試す場合にはイベントハンドラ中の該当部分のコメント指定を外して直ぐに使用できます)

file_id = "SERIAL_TWE"

--[[

******************************************************************************
* イベントハンドラスクリプト実行時間について                                 *
******************************************************************************

一つのスクリプトの実行は長くても数秒以内で必ず終了するようにしてください。
処理に時間がかかると、イベント処理の終了を待つサーバー側でタイムアウトが発生します。

また、同時実行可能なスクリプトの数に制限があるため、他のスクリプトの実行開始が
待たされる原因にもなります。

頻繁には発生しないイベントで、処理時間がかかるスクリプトを実行したい場合は
スクリプトを別に作成して、このイベントハンドラ中から script_fork_exec() を使用して
別スレッドで実行することを検討してください。

******************************************************************************

SERIAL_TWE スクリプト起動時に渡される追加パラメータ
---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
COMPort			イベントを送信したシリアルデバイスの COMポート名		"COM10"

Title			イベントを送信したシリアルデバイスのタイトル名			"TWE-Lite PAN#1"
				タイトルが設定されていない場合にはこのパラメータは
				設定されません

TWE_DATA		COM ポートから入力されたアスキー形式のデータパケット全体を格納しています。

	値の例

	タイプ(1)	":01890902010A010203040405060708092F"
	タイプ(2)	"::rc=80000000:lq=84:ct=0001:ed=81002A1E:id=1:ba=3320:a1=0009:a2=0009:p0=001:p1=000"
	タイプ(3)	";116;00000000;054;001;1002ecd;3330;0007;0042;0007;0007;S;"
	タイプ(4)	"!INF TOCOS TWELITE DIP APP V1-00-2, SID=0x81000038, LID=0x78"
	タイプ(5)	"*** Samp_Monitor (Parent) 1.03-3 *** Title = TWE-Zero"

	文字列は、下記の何れかのデータで終端されたものです。
	ヌル文字(0x00),CR(0x0D),LF(0x0A),CR-LF(0x0D,0x0A)
	TWE_DATA パラメータには、終端文字を含まない文字列部分が格納されています

TWE_DATAを解析してこのスクリプト中で作成されるテーブルと文字列変数
---------------------------------------------------------------------------------
テーブルまたは文字列変数名		説明									値の例
---------------------------------------------------------------------------------
byte_arr	TWE_DATA が ":" 1文字から始まっている場合に、16進数文字列部分を
			1バイト毎に数値に変換して配列に格納したものが入る
			上記 TWE_DATA データ例タイプ(1)を参照

			byte_arr[1] = 0x01
			byte_arr[2] = 0x89
			byte_arr[3] = 0x09
			..

key_val		TWE_DATA が "::" 2文字から始まっている場合に、続く ":" 文字毎にカラムを
			分けて "<key>=<val>" で記述された部分を連想配列に格納したものが入る。
			上記 TWE_DATA データ例タイプ(2)を参照

			key_val["rc"] = "80000000"
			key_val["lq"] = "84"
			key_val["ct"] = "0001"
			key_val["ed"] = "81002A1E"
			..

tag_arr		TWE_DATA が ";" 1文字から始まっている場合に、続く ";" 文字毎にカラムを
			分けたものを文字列形式で配列に格納したものが入る。
			上記 TWE_DATA データ例タイプ(3)を参照

			tag_arr[1] = "116"
			tag_arr[2] = "00000000"
			tag_arr[3] = "054"
			tag_arr[4] = "001"
			..

comment		TWE_DATA が ":"または ";" 文字以外から始まっている場合に、
			データパケット全体の文字列を格納したものが入る
			上記 TWE_DATA データ例タイプ(4),(5)を参照

			COMMENT = "!INF TOCOS TWELITE DIP APP V1-00-2, SID=0x81000038, LID=0x78"

TWE_DATAを解析してこのスクリプト中で作成されるグローバル共有変数と共有文字列リスト
---------------------------------------------------------------------------------
グローバル共有変数名						説明
または、共有文字列リストChannel名
---------------------------------------------------------------------------------

TWE_<COMPort>_<ChildID>

			TWE_DATA が ":" 1文字から始まっていて、かつコマンド種別を示すバイト値が
			0x81(状態通知)の場合に、メッセージ内容を解析した値がカンマ区切りで
			グローバル共有変数に格納される。この変数の値は常に最後のイベント発生時
			の内容で更新されます。

			<COMPort>は、イベントを送信したシリアルデバイスの COMポート名になります
			<ChildID>は、メッセージ中の送信元論理デバイスIDを10進数にしたものになります

			変数の内容に設定されるカンマ区切りの文字列は下記のフォーマットになります。

			<LQI>,<Batt>,<DI1>,<DI2>,<DI3>,<DI4>,<AD1>,<AD2>,<AD3>,<AD4>

			<LQI> にはLQI値フィールドの値を10進数に変換したものが入ります
			<Batt> には電源電圧[mV]フィールドの値を10進数に変換したものが入ります
			<DI1>..<DI4> にはDI の状態ビットが Lowの場合に1, High の場合に 0 が入ります
			<AD1>..<AD4> にはAD変換値の値を10進数に変換したものが入ります

TWE_<COMPort>_CHILD_LIST (共有文字列リストChannel名)
			TWE_DATA が ":" 1文字から始まっているパケットデータを受信したときの
			<ChildID> 部分を文字列リストに保存します。文字列リストにはメッセージ中の
			送信元論理デバイスIDを10進数表現にしたものを重複なく保存しています。
]]

local str = ""
for key,val in pairs(g_params) do
	str = str .. key .. " = " .. val .. " "
end
log_msg(str,file_id)

------------------------------------------------------------
-- g_params["TWE_DATA"] データパケット文字列をデコードする
------------------------------------------------------------
local data = g_params["TWE_DATA"]
local byte_arr,key_val,tag_arr,comment
if string.match(data,"^::") then		-- タイプ(2)
	key_val = key_val_to_tbl(string.sub(data,3,-1))
elseif string.match(data,"^:%x") then	-- タイプ(1)
	byte_arr = hex_to_tbl(string.sub(data,2,-1))
	local id = tostring(byte_arr[1])
	if not add_shared_strlist("TWE_" .. g_params["COMPort"] .. "_CHILD_LIST",id,true) then error() end -- 子機のIDリストを更新
	if byte_arr[2] == 0x81 then -- 状態通知の場合には共有変数に現在のセンサ値を保存
		local di1,di2,di3,di4,ad1,ad2,ad3,ad4
		if bit_and(byte_arr[17],0x01) ~= 0 then di1 = "1" else di1 = "0" end
		if bit_and(byte_arr[17],0x02) ~= 0 then di2 = "1" else di2 = "0" end
		if bit_and(byte_arr[17],0x04) ~= 0 then di3 = "1" else di3 = "0" end
		if bit_and(byte_arr[17],0x08) ~= 0 then di4 = "1" else di4 = "0" end
		if byte_arr[19] == 0xFF then ad1 = "-1" else ad1 = tostring((byte_arr[19]*4 + bit_and(byte_arr[23],0x03))*4) end
		if byte_arr[20] == 0xFF then ad2 = "-1" else ad2 = tostring((byte_arr[20]*4 + bit_and(bit_rshift(byte_arr[23],2),0x03))*4) end
		if byte_arr[21] == 0xFF then ad3 = "-1" else ad3 = tostring((byte_arr[21]*4 + bit_and(bit_rshift(byte_arr[23],4),0x03))*4) end
		if byte_arr[22] == 0xFF then ad4 = "-1" else ad4 = tostring((byte_arr[22]*4 + bit_and(bit_rshift(byte_arr[23],6),0x03))*4) end
		local val = tostring(byte_arr[5]) .. "," .. tostring(byte_arr[14]*256 + byte_arr[15])
				.. "," .. di1 .. "," .. di2 .. "," .. di3 .. "," .. di4
				.. "," .. ad1 .. "," .. ad2 .. "," .. ad3 .. "," .. ad4
		if not set_shared_data("TWE_" .. g_params["COMPort"] .. "_" .. id,val) then error() end
		-- リレーサーバーに最新データの配信を依頼する場合には下記のコメントを削除する
		script_exec("RELAY_SERVER_UPLINK","COM,TYPE,NodeID,LQI,Batt,DI1,DI2,DI3,DI4,AD1,AD2,AD3,AD4",g_params["COMPort"] .. ",TWE_UPDATE," .. id .. "," .. val)
	end
elseif string.match(data,"^;") then		-- タイプ(3)
	tag_arr = ssv_to_tbl(string.sub(data,2,-2))
else									-- タイプ(4),(5)
	comment = data
end

--[[
********************************************************************

TWE(Samp_Monitor)->MQTT Gateway 機能の実装例

Samp_Monitor ファームウエアで動作中のリモート TWEデバイスから送信されたタグ・データを MQTT に登録します。
Samp_Monitor ファームウエアのオプション設定によって、送信されるタグの項目は変わります。詳しくは
ファームウエアのドキュメントを参照して下さい。このスクリプトでは送信されたタグのキーと値をそのまま
文字列ペアとして JSON に変換しています。

トピック名: "/twe/<Samp_Monitor フレーム中の "ed"(MACアドレス)タグの値>/Samp_Monitor"
メッセージ文字列: JSON キー名部分は下記スクリプト中の "tag_name" テーブルに設定することで変更できます

{"id":"0","ct":"0007","ed":"81002A1E","ba":"2840","rc":"80000000","lq":"57","p0":"000","a2":"2113","a1":"11..(キーと値のペアが続く)

送信先 MQTT ブローカは、サーバー設定プログラムを使用して
エンドポイントを登録して下さい。下記スクリプト中の end_point 変数に
エンドポイントのタイトル文字列または ClientID を設定します。

********************************************************************
]]

-- データが文字列 "::" から始まるデータタイプ(2)の場合で、且つ "ed"タグが含まれているものだけを、MQTTブローカ送信対象にする
if key_val and key_val["ed"] then
	local dev = key_val["ed"]
	local end_point = "センサーデータ登録"
	local topic = "/twe/" .. dev .. "/Samp_Monitor"
	local qos = 1

	-- TWE のタグ名を変更して JSON 形式にする場合は、変更したいタグを下記テーブルに指定する
	local tag_name = {}
	tag_name["ba"] = "battery"
	tag_name["ct"] = "count"
	tag_name["lq"] = "LQI"
	tag_name["te"] = "temperature"
	tag_name["hu"] = "humidity"
	tag_name["at"] = "pressure"
	tag_name["x"] = "acc_x"
	tag_name["y"] = "acc_y"
	tag_name["z"] = "acc_z"

	local json_str = '{'
	local first = true
	for key,val in pairs(key_val) do
		if first then
			first = false
		else
			json_str = json_str .. ","
		end
		if tag_name[key] then
			json_str = json_str .. string.format('"%s":"%s"',tag_name[key],val)
		else
			json_str = json_str .. string.format('"%s":"%s"',key,val)
		end
	end
	json_str = json_str .. '}'
	mqtt_publish(end_point,topic,json_str,qos)
end

イベントハンドラの最初の部分では、TWE-Zero アプリ(ファームウエア) で使用される幾つかの種類のレコードフォーマットを解析するための共通ルーチンが定義されています。

無線タグアプリ(Samp_Monitor) は上記コメント中の タイプ(2) のレコードフォーマットとして解析されて、Lua テーブル key_val[] にタグのキーと値が対応付けられて作成されます。

スクリプトの後半部分で、MQTT Brokerに送信するための JSON 形式の文字列を作成しています。key_val[] テーブルのキーと値は、TWE無線タグ親機から送信されるタグの、キーと値にそのまま対応しています。ここではこのキー名と値を利用して下記のような JSON 形式の文字列に変換します。

{“id”:”0″,”ct”:”0007″,”ed”:”81002A1E”,”ba”:”2840″,”rc”:”80000000″,”lq”:”57″,”p0″:”000″,”a2″:”2113″,”a1″:”11″,”a2″:”334″}

“ba” や ”lq” などのタグ名を、JSON データ利用側で判りやすいように “battery”, “LQI” などの名前に変換するための機能がスクリプト中に記述されています。詳しくはスクリプト中のコメントをご覧ください。

mqtt_publish() ライブラリ関数をコールすることで、JSON 形式に変換したリモート無線タグから受信したセンサデータを MQTT Broker に送信します。

これで、リモートに設置した全てのTWE 無線タグのセンサデータが MQTT Broker に送信されるようになります。TWE無線タグ・子機のオプション設定を変更して、接続するセンサーデバイスを変更した場合でも、出力されるデータのタグ名に対応した JSON 形式に変換されて MQTT Broker に送信されます。

●MQTT Broker からTWE無線タグ・センサ・データを受信する

次に、DeviceServer 側から MQTT Broker のセンサーデータの購読する部分のスクリプトを作成します。MQTT Broker に送信したセンサデータを再び DeviceServer 側に取り込んで利用する形になります。今回のシステム構成では、MQTT Broker に送信する前にセンサデータを処理できるのであくまでも、MQTT Broker からのデータ取得の一例として紹介します。

インストール直後のデフォルトスクリプトは、MQTT Broker から購読したデータ列を文字列に変換してログに出力しているだけですが、この部分を変更して子機無線タグのAI3 (“a2″タグ) に接続した光センサの値をログに出力します。MQTT_PUBLISH.lua イベントハンドラの内容に下記の記述を追加します。

file_id = "MQTT_PUBLISH"

--[[

******************************************************************************
* イベントハンドラスクリプト実行時間について                                 *
******************************************************************************

一つのスクリプトの実行は長くても数秒以内で必ず終了するようにしてください。
処理に時間がかかると、イベント処理の終了を待つサーバー側でタイムアウトが発生します。

また、同時実行可能なスクリプトの数に制限があるため、他のスクリプトの実行開始が
待たされる原因にもなります。

頻繁には発生しないイベントで、処理時間がかかるスクリプトを実行したい場合は
スクリプトを別に作成して、このイベントハンドラ中から script_fork_exec() を使用して
別スレッドで実行することを検討してください。

******************************************************************************

MQTT_PUBLISH スクリプト起動時に渡される追加パラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
ClientID		エンドポイントの ClientID 文字列						"abs9k:2222-eagle"

Title			エンドポイントに設定されたタイトル文字列。
				タイトル文字列が設定されていない場合には、"" 空文字列
				が入ります												"センサーデバイス#1"

MessageType		MQTT プロトコルで定義されたメッセージタイプが入ります。	"3"
				PUBSLIH メッセージの場合には常に "3"が設定されます

MessageID		Brokerから送信するときに使用された MQTT メッセージID が
				入ります。(QoS = 1 または QoS = 2 の場合) 値は "1" から
				"65535" の整数値をとります。
				QoS = 0 の場合には常に "0" が設定されます。				"1234"

Dup				MQTT 固定ヘッダ中の Dup フラグの値が設定されます。
				"0" または "1" の値をとります。							"0"

QoS				MQTT 固定ヘッダ中の QoS フラグの値が設定されます。
				"0", "1", "2" の何れかの値をとります。					"0"

Retain			MQTT 固定ヘッダ中の Retain フラグの値が設定されます。
				"0" または "1" の値をとります。							"0"

PublishTopic	MQTT ブローカから受信した PUBLISH メッセージ中の Topic
				文字列。												"センサー/ノード1"

PublishData		MQTT ブローカから受信した PUBLISH メッセージ中のペイロー
				ドデータ。
				バイナリデータを16進数文字列に変換したものが格納されます
				ペイロードデータに格納されたデータが UTF-8 文字列の場合
				には文字列コードのバイト列が格納されています。
				イベントハンドラ中でこれらの文字列データをデコードする処
				理がデフォルトで記述されていますので、UTF-8 文字列を扱う
				場合にはデコード後の変数を利用することができます。		"010203414243"

PublishDataで渡されたペイロードデータを解析して作成される文字列変数

PublishString	PublishData に格納されたペイロードデータ部分のサイズが
				2048 Bytes以内の場合に、データバイト列を UTF-8形式で
				文字列にデコードした結果を PublishString に格納します。
				変換対象のバイト列のサイズを変更したいときには該当する
				スクリプト部分を変更して下さい。

]]

------------------------------------------------------------------------------------------
-- 受信したペイロードデータのサイズが 2048 bytes 以内の場合には
-- バイナリデータ列を UTF-8 文字列としてデコードしたものを PublishString 変数に格納する
------------------------------------------------------------------------------------------
local PublishString = ""
local pub_len = string.len(g_params["PublishData"]) / 2
if pub_len < 2048 then
	PublishString = readUTF_hex(bit_tohex(pub_len,4) .. g_params["PublishData"])
end

log_msg(g_params["Title"] .. "[" .. g_params["ClientID"] .. "] msg:" .. g_params["MessageID"] .. " dup:" .. g_params["Dup"]  ..
" retain:" .. g_params["Retain"]  .. " qos:" .. g_params["QoS"]  .. " topic:" .. g_params["PublishTopic"]  .. " " .. PublishString,file_id)

if g_params["Title"] == "センサーデータ取得" then

	---------------------------------------------------------------------------------------
	-- 過去データ確認用に時系列データベースに Publish されてきた文字列(JSON)を保管しておく
	---------------------------------------------------------------------------------------
	if not add_series_str(g_params["PublishTopic"],PublishString) then error() end

	------------------------------------------------
	-- その他のアクション定義
	------------------------------------------------
	json = require('json')
	local data_tbl = json.decode(PublishString) -- JSON デコードして Lua テーブル構造に変換

	if data_tbl.a2 then -- JSON データ中に "a2" キー名に対応するデータが存在するときだけ実行
		local a2_num = tonumber(data_tbl.a2)
		log_msg("A2 = " .. tostring(a2_num),file_id)
		------------------------------------------------
		-- 7セグLED表示器にメッセージ表示
		------------------------------------------------
		local tbl = {}
		tbl["com"] = "Arduino実験ボード#1"
		tbl["data"] = tostring(a2_num)
		tbl["width"] = "8"
		if not script_exec("ARDUINO/DEVICE/SPI_7SEGLED",tbl) then error() end
	end

end

add_series_str() をコールしている部分は、後で過去のデータをグラフ表示する時などに利用できるように、DeviceServer 内蔵のデータベースに取得した JSON 文字列をそのまま格納しています。

次の部分では、JSON 文字列をデコードして Lua のテーブル構造に変換しています。テーブルの形にしておくとで、センサデータの内容を取り出すことが簡単にできるようになります。data_tbl.a2 には JSON 文字列中の { …. “a2″:”xxxx” } の部分が入っていますので、これを数値に変換することで AI3 の A/D 変換値が得られます。ここでは、この値をログに出力しています。

動作中のログを下記に示します。MQTT Broker にセンサーデータを登録して、同時にそのセンサーデータを MQTT から購読した後上記スクリプト中で A/D 変換値を出力するまでがログに出力されています。

●おまけ

MQTT_PUBLISH.lua イベントハンドラスクリプト中からは、Arduino に接続した 7セグLED 表示器(秋月電子 M-06681) に現在の A/D 変換値を表示しています。

7セグLED 表示器は SPI 接続で簡単にスクリプト中から操作できますので、最新のデータを確認するのに便利です。

Arduino と DeviceServer 間はシリアル接続されいて、Firmata プロトコルで SPI 通信コマンドをやり取りしています。(Firmata プロトコルで Arduino をDeviceServerから操作するアプリ例はこちらの記事を参照してください)

動作の詳細はインストールキットに含まれている下記のスクリプトをご覧ください。また、Arduino で動作させるスケッチ(SensorControlModule.ino)もインストールキットに含まれていますのでご利用下さい。

Arduino に SPI 接続した LED表示器を DeviceServer から操作するスクリプト

C:\Program Files (x86)\AllBlueSystem\Scripts\ARDUINO\DEVICE\SPI_7SEGLED.lua

C:\Program Files (x86)\AllBlueSystem\Scripts\ARDUINO\DEVICE\SPI_WRITE.lua

それではまた。

 

コメントは受け付けていません。