XBee-ZB I/O データを SCRATCH プログラムから利用する

 

今回は、SCRATCH プログラムからセンサーネットワークのデータを利用する方法について紹介したいと思います。

SCRATCH は MITで開発された教育用のプログラミング環境で、ビジュアルにブロックを並べるようにしてアプリケーションを簡単に作成できます。(SCRATCH ホームページ)

SCRATCH プログラムから、PCに接続した (PicoBoard) という専用の I/O デバイスを利用してセンサー値を取得したり、I/O を操作するプログラムも作成できます。また、標準では隠されていますが “共有機能” を有効にすると、ネットワーク経由で任意のセンサー値を受信して SCRATCH プログラム内で利用できるようになります。(SCRATCH共有機能)

ここではこの共有機能を使用して、リモートに設置したXBee-ZB のセンサー値を SCRATCH から利用してみます。

上のキャプチャ画像は、XBee-ZB エンドデバイスやルータデバイスから定期的に送信される I/O データ値を SCRATCHから利用している様子です。SCRATCH では取得したセンサー値に応じて、アニメーション表示や音を鳴らすことが簡単にできます。また、自分で作成した画像をアニメーション表示できますので、簡単にセンサーの監視盤を作成できます。

この記事では XBee-ZB の I/O データについて説明していますが、DeviceServer で監視している他のセンサーデータ値を、SCRATCH で利用するように応用することも簡単にできます。

XBee-ZB エンドデバイスは下記の様にブレッドボードで作成しています。XBee-ZB にスイッチと光センサ(CDS) を接続しています。

DIO#11 をデジタル入力用に設定してタクトスイッチを接続します。ADC#3 には光センサを接続します。Vcc(3.3V) を光センサ(CDS) と抵抗で分圧した値が、XBee-ZB ADC#3 のリファレンス電圧(1.2V) に収まるように抵抗値を調整します。手持ちの CDS では+(左)側が1.5K,GND(下)側が 1K になりました。その他の配線は、前回の記事中で使用したものと同じですのでこちらを参考にしてください。

次に、XBee-ZB の詳細パラメータを設定します。DIO#11 の設定は以下のようになります。

DIO#11 を Digital input に設定しています。同時にプルアップとChange Detect も有効にしています。今回は定期的にサンプリングデータを送信しますので Change Detect は無効にしても正常に動作します。Change Detect を有効にしておくことで、スイッチを “押した” 時と “離した” 時に DIO のみが格納されたサンプリングデータが追加で送信されますので、スイッチの入力にすばやく反応して、SCRATCH 側のセンサー値を変化させることができます。

次に、光センサ(CDS) を接続した ADC#3 の設定を行います。

ADC#3 (DIO#3) を Analog input に設定します。設定画面上部の IO Sampling Rate を 300(ms) に設定して、0.3 秒ごとに I/O 値を Coordinator(DeviceServer)に送信するようにします。

これらの設定が完了すると、XBee-ZB デバイスから定期的に I/O データが送信されてきて DeviceServer の ZB_IO_DATA イベントハンドラがその都度実行されます。 ZB_IO_DATA.lua イベントハンドラは下記のようになっています。

file_id = "ZB_IO_DATA"

--[[

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

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

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

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

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

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

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
FrameType		フレームデータ中のFrame Type
				(16進数2桁)												92

SourceAddress	フレームデータ中のSourceAddress
				64bit アドレス(16進数16桁)								0013A200404AC397

NetworkAddress	フレームデータ中の SourceNetworkAddress
				16bit アドレス(16進数4桁)								D565

NodeIdentifier	XBee デバイスの NodeIdentifier。
				DeviceServerのマスターファイルを検索して設定される。	Node1
				マスターにNodeIdentifier未登録の場合は"" が設定される

DeviceType		XBee デバイスの Device Type
				DeviceServerのマスターファイルを検索して設定される。	01
				マスターにDeviceType未登録の場合は"" が設定される
				8bit値(16進数2桁)
				00: coordinator
                01: router
                02: end device

DeviceTypeID	XBee デバイスの Device Type Identifier
				DeviceServerのマスターファイルを検索して設定される。
				マスターにDeviceTypeID未登録の場合は"" が設定される
				32bit値(16進数8桁)										00030000

ReceiveOptions	フレームデータ中 ReceiveOptions							01
				8bit値(16進数2桁)

SAMPLE_COUNT	I/O データのサンプル数									1

SAMPLE_DIO		I/O データ中のサンプル対象となったDIOビット番号リスト
				(10進数、カンマ区切り)									0,1,4

SAMPLE_ADC		I/O データ中のサンプル対象となったADCビット番号リスト
				(10進数、カンマ区切り)									2,3

SAMPLE_<Sample#>_<"DIO"|"ADC">_<Bit#>

				I/O サンプルデータ値。
				DIO の場合は、High で "1"、Low で "0"。					1
				ADC の場合は 10進数。									1023

				<Sample#> には 最大、SAMPLE_COUNT まで 1から順番に
				インクリメントされた値が入る。

				<"DIO"|"ADC">は、I/O サンプルデータが ADC
				もしくは、DIO のどちらであるかを示す。

				<Bit#>は、サンプルデータのビット番号。10進数。

		例:"SAMPLE_1_ADC_0" は、第一サンプルデータ中の #0番ポートのADC変換値を示す。

]]

log_msg("start..",file_id)
--for key,val in orderedPairs(g_params) do
--	log_msg(string.format("g_params[%s] = %s",key,val),file_id)
--end

local shared_key
for key,val in orderedPairs(g_params) do
	-------------------------------------------------------------------------------
	-- I/O サンプリングデータを共有メモリに格納する
	-- <NodeIdentifier>_<"DIO"|"ADC">_<Bit#> のキー名でサンプリングデータを格納する
	-------------------------------------------------------------------------------
	if string.match(key,"SAMPLE_1") then
		shared_key = g_params["NodeIdentifier"] .. string.sub(key,9,-1)
		if not set_shared_data(shared_key,val) then error() end
		----------------------------------------------------------------------------------
		-- 共有メモリに格納したサンプリングデータ項目のリストを共有メモリリストに格納して
		-- SCRATCH への送信対象とする
		----------------------------------------------------------------------------------
		if not add_shared_strlist("XBeeZBサンプリング項目",shared_key,true) then error() end
	end

end
-------------------------------------------------------------
-- SCRATCH にソケット通信でセンサーデータを送信するタスクに、
-- 送信すべきデータが揃ったことを通知する
-------------------------------------------------------------
if not event_set("SensorDataExist") then error() end

このイベントハンドラでは XBee-ZB から送信されてきた複数のI/O データを DeviceServer の共有メモリに格納しています。その後、このイベントハンドラとは別に動作している送信用のスクリプトに対して、送信対象のデータが共有メモリに設定したことを知らせるために、event_set() 関数でイベントを発生させます。

このアプリケーションでは、センサーデータの取得と処理(SCRATCH に送信)を2つのタスクに分けて別々に動作するようにしています。実は、この XBee-ZB のデータを受信したときのイベントハンドラ中から直接 SCRATCH プログラムに TCP/IP で送信しても動作するのですが、あえて2つに分けているのは以下の理由からです。

XBee-ZB のイベントデータは 300ms 間隔で定期的に送信されてきます。その間隔で SCRATCH プログラムにメッセージを通信するのは問題ないのですが、もし同様の XBee-ZB エンドデバイスを同時に複数使用する場合や、サンプリング間隔をもっと短くした場合を考えます。このときもたぶん動作しますが、SCRATCH プログラムを手動で停止させたときに問題が発生します。SCRATCH プログラムを終了させると、もちろんTCP/IP でメッセージを送信する部分でエラーが発生します。このエラー(タイムアウト)を検出するまでの間にこのイベントハンドラがいくつも同時に実行されたままになって、リソース(DeviceServer のLua スクリプトエンジンの最大同時実行数デフォルト値 15) を超えてしまい、他の監視機能に影響を与えてしまう恐れがあります。

動作を分けると、SCRATCH との通信に時間がかかって同一のI/O データ項目が複数回更新されたときでも、最後に到着したデータを送信するだけの動作になりますので効率的に通信をすることができるようになります。

下記はSCRATCH に送信する SCRATCH_SEND_TASK.lua スクリプトの内容です。

file_id = "SCRATCH_SEND_TASK"

------------------------------------------------------------------------------------------
-- SCRATCH プログラムの Mesh サーバーにセンサーデータを送信するためのタスクです。
-- イベント "SensorDataExist" がセットされたタイミングでセンサーデータを送信します。
-- 送信するセンサーデータは共有変数に格納されていて、共有変数の名前(キー名)が、
-- 共有文字列リスト "XBeeZBサンプリング項目"に格納されています。
--
-- このスクリプトは無限ループになっていて、終了させる場合には、"タスクリスト"プログラム
-- (TaskList.exe)を使用して強制終了します。また、SCRATCH プログラムへのソケット通信で
-- エラーが発生した場合(SCRATCH プログラムを終了した場合など)にも、このスクリプトは
-- エラーを発生させて終了します
------------------------------------------------------------------------------------------

log_msg("start..",file_id)

local stat,strlist,data
while true do	-- エラー発生または強制終了されるまでバックグランドで実行する
	------------------------------------------------------------------
	-- 既にこのタスクが実行中であるかを確認するためのフラグを更新する
	------------------------------------------------------------------
	if not set_shared_data("SCRATCH_SEND_TASK_RUNNING","1") then error() end

	------------------------------------------------------------------
	-- 送信対象のデータが揃うまでイベントを待つ
	-- 100ms ごとにタイムアウトして、上記のタスク実行中を示すフラグを更新するタイミング
	-- に利用する。
	------------------------------------------------------------------
	if event_wait("SensorDataExist",100) then

		------------------------------------------------------------------
		-- センサーデータ項目が格納された共有文字列リストの内容を取得する
		-- リストの内容を取得すると同時に文字列リストをクリアしておくことで、
		-- SCRATCH に送信するデータを更新分だけにできる
		------------------------------------------------------------------
		stat,strlist = get_shared_strlist("XBeeZBサンプリング項目",true)
		if not stat then error() end

		------------------------------------------------------------------
		-- SCRATCH に送信する "sensor-update" メッセージを組み立てる
		------------------------------------------------------------------
		local msg = "sensor-update"
		for key,val in ipairs(strlist) do
			stat,data = get_shared_data(val)
			if not stat then error() end
			msg = msg .. ' "' .. val .. '" ' .. data
		end

		log_msg(msg,file_id)
		-----------------------------------------------------------------------------
		-- sensor-update メッセージを送信
		-----------------------------------------------------------------------------
		if not scratch_send("localhost",msg) then error() end

		-----------------------------------------------------------------------------
		-- broadcast メッセージを送信する
		-- このメッセージを送信しなくても SCRATCH 側でセンサーデータ項目を利用
		-- できますが、センサーデータ更新タイミングでSCRATCH 側のスクリプトを書き易い
		-- ように送信しておく
		-----------------------------------------------------------------------------
		if not scratch_send("localhost",'broadcast "my_data_update"') then error() end

	end
end

log_msg("end.",file_id)

このスクリプトは起動されると無限ループに入って、強制的に終了させるかまたは通信エラーが発生するまでイベントの到着を待ち続けます。XBee-ZB の I/O データを受信したイベントハンドラでイベントがセットされると、共有データに格納されている I/O データの最新値を取得して、SCRATCH に送信するメッセージを組み立てます。

SCRATCH のメッセージは以下のような内容です。”sensor-update” の後にセンサー項目名とセンサー値がペアで続きます。詳しい仕様はこちらをご覧ください。

sensor-update “Node1_DIO_11″ 0 “Node1_ADC_3″ 200 …..

また、全てのセンサーデータを送信した後に ”broadcast” メッセージを送信しています。

broadcast “my_data_update”

これは、SCRATCH 側でプログラムを作るときに、”my_data_update” という名前のメッセージが届いたときに、最新のセンサーデータを利用した動作を簡単に作成できるようにするために送信しています。送信には専用の API 関数 scratch_send() 関数を使用しています。この関数はパラメータで指定された文字列を TCP ポート番号42001 で送信します。文字列の先頭にはデータ長を示す 4 バイトのデータを追加して送信します。

上記の送信スクリプトは無限ループに入ってしまうので、直接クライアントプログラムから起動せずに以下の SCRATCH_SEND_TASK_SUBMIT.lua スクリプトを実行して、間接的に SCRATCH_SEND_TASK.lua を起動させます。

file_id = "SCRATCH_SEND_TASK_SUBMIT"

-------------------------------------------------------------------------------------
-- SCRATCH_SEND_TASK を別スレッドで起動する
-------------------------------------------------------------------------------------

log_msg("SCRATCH_SEND_TASK が実行中であるかをチェックします.. 2秒かかります",file_id)
if not set_shared_data("SCRATCH_SEND_TASK_RUNNING","") then error() end

-------------------------------------------------------------------------------------
-- SCRATCH_SEND_TASK が実行中と仮定して、センサーデータの確認イベント待ちでタイムアウト
-- が確実に発生するまでウェイトする。(100ms + SCRATCH へ送信中の場合にはそれが完了するまでの時間)
-- もしタイムアウトが発生すると、共有変数 "SCRATCH_SEND_TASK_RUNNING" が "1" に設定
-- されるのでSCRATCH_SEND_TASK スクリプトタスクが実行中であることが判る
-------------------------------------------------------------------------------------

wait_time(2000) 

local stat,val = get_shared_data("SCRATCH_SEND_TASK_RUNNING")
if not stat then error() end
if val == "1" then
	log_msg("SCRATCH_SEND_TASK は既に実行中です",file_id)
	return
end

log_msg("SCRATCH_SEND_TASK を別スレッドで実行します",file_id)
local stat,taskid = script_fork_exec("SCRATCH_SEND_TASK",{})
if not stat then error() end

このスクリプトでは送信用のスクリプトを2重に起動しないような仕組みも記述しています。詳しくはスクリプト中のコメントを参照してください。

これで センサーデータ送信側の準備が整いましたので、SCRATCH プログラムを起動します。SCRATCH プログラムは、ここからダウンロードできます。今回の記事で紹介している内容は SCRATCH ver1.4 のプログラムを対象にしています。オンライン版(ver2.x) の SCRATCH も公開されていますが、これには対応していませんので注意してください。

SCRATCH プログラムを起動した状態では外部からのネットワーク経由のセンサーデータ受信には対応していませんので、このサイトを参考に共有機能メニューを有効にする必要があります。

詳しい手順は上記のサイトに書かれていて、SCRATCH プログラム自身のソースをSystem Browser で変更しています。一度設定を変更してデフォルト値を保存しておくと、次回からはこれらの操作を行う必要はありません。次に、共有メニューを “SHIFT” キーを押しながら選択すると “HOST Mesh” が選択できるようになりますので、これを実行します。

またこの方法とは別に、簡単に共有設定を行う方法があります。SCRATCH コンポーネントの “xxxのセンサー値 ” をマウスで長押しすることで表示される ”遠隔センサーを有効にする” をチェックすると同様の機能が使えるようになります。

これで SCRATCH プログラムでソケットサーバーが起動されて外部からの接続を受け付ける状態になっています。ここで先に設定した、”センサー値を送信するスクリプト” を起動するためのスクリプト SCRATCH_SEND_TASK_SUBMIT.lua を実行します。

実行すると、XBee-ZB から定期的に送信されているセンサーデータが SCRATCH にも送られるようになります。このときのログは以下のように出力されます。

この状態で SCRATCH からは XBee-ZB のI/O データをセンサー値として利用できるようになっています。SCRATCH コンポーネントの “xxxのセンサー値 ” のプルダウンメニューに、XBee-ZB から送信された I/O データが表示されていると思います。

複数の XBee-ZB エンドデバイスから送信されている場合には、センサー名の頭に NodeIdentifier がついていますので簡単に区別できます。XBee-ZB デバイスの詳細設定でI/O 設定を変更して I/O を増やした場合には自動的に新規のメニュー項目が追加されます。

“xxxのセンサー値 ” コンポーネントのチェックボックスにチェックを付けると、SCRATCH 画面の右上に監視盤が表示されて、現在のセンサー値がダイナミックに表示されるようになります。

上のキャプチャ画像では監視盤に加えて、スクラッチ側で簡単なスクリプトを作成しています。XBee-ZB に接続した光センサに手をかざして暗くなると、キャラクタの色が変化するようにしています。このスクリプトはセンサーデータと同時に SCRATCH に送信した broadcast メッセージを受信したタイミングで実行します。

SCRATCH プログラムを終了させると、DeviceServer 側で動作している送信用のスクリプト SCRATCH_SEND_TASK.lua 内で通信エラーが発生して自動的にスクリプトが終了します。手動でスクリプトを停止させる場合には DeviceServer アプリケーションに付属のタスク管理プログラムから “削除” ボタンを押してスクリプトを強制終了できます。

 

ここまでの操作を動画にまとめてありますので是非ご覧ください。(音量注意)

それではまた。

 

XBee-ZB で ZigBee ネットワーク構築とリモート DIO 操作

ABS-9000 DeviceServer に新しく XBee-ZB Series2 デバイスを管理するためのサービスモジュールをリリースしました。ここでは、このXBee-ZB 用の ZB サービスモジュールを使って簡単なZigBee センサーネットワークを構築する例を紹介したいと思います。この記事で説明しきれない部分については、ホームページ または、下記のマニュアルも併せてご覧ください。

セットアップガイドでは XBee-ZB デバイスの初期設定やマスター登録について説明しています。ユーザーマニュアルではXBee-ZB のイベントハンドラやAPI ライブラリ関数について詳しく説明しています。

今回の構築例では、ZigBee の各デバイスタイプ(coordinator, router, end device) をそれぞれ1つずつ使用してネットワークを作成しています。end device には下記の様にスイッチと LED を搭載した回路をブレッドボードで作成して簡単な機能を実現しています。

ブレッドボード上のタクトスイッチはデジタル入力に設定した XBee-ZB の DIO#11 に接続しています。このスイッチを押すごとに IO イベントデータが coordinator(DeivceServer) に送信されます。また、LED を XBee-ZB の DIO#1 に接続して点灯・消灯 できるようにします。今回のセンサーネットワークで実現する機能は、この end device のスイッチを押すと LED が点灯して、再びスイッチを押すと LED が消灯するような動作を繰り返します。router デバイスでも同様の回路を作成して、スイッチと LED を連動させた同じ動作を行います。

単純な動作ですが、XBee-ZB デバイスのリモートコマンド操作、 IO イベント送信機能とサーバー側の処理と合わせて実現します。

end device では cyclic sleep モードで動作しています。スイッチを押すと同時に end device がcyclic sleep を抜けてデバイスがwake up 状態になります。これは XBee-ZB 9pin に wake up (アクティブ Low) 信号を加えることで実現します。このため XBee-ZB はcyclic sleep with pin wake モード(5) を使用しています。wake up 信号を使用しない場合には、end device デフォルトの cyclic sleep モード(4) を使用しても問題なくスイッチ入力に反応して IO イベントを送信できます。ただこの場合は少し、スイッチの反応が遅くなります。デフォルトのスリープパラメータを変更してスリープに入る時間やポーリングの間隔を変更する場合には、wake up 信号を使用する方法がいいと思います。

以下のキャプチャは end device を DeviceServer のデバイス管理プログラムの詳細設定画面( Sleep タブ) で設定している様子です。デバイス管理プログラムのマスター登録や詳細設定については後で紹介する動画もご覧ください。

Sleep Mode を 5 に設定しています。画面下部分の DIO8 タブで XBee-ZB デバイス 9pin の wake up ラインのプルアップを有効にしています。9pin にはダイオード経由でDIO#11 デジタル入力ピンと同じ値を入力します。ダイオードを使用しないでラインを直結しても動作しますが、wake up 信号をテスト的に入れたり、複数のスイッチ入力に改造するときに誤動作させないために念のため入れておきます。

以下は DIO#11 設定の様子です。

DIO#11 のモードを 3 (digital input) に設定します。同時にプルアップ と change detect を有効にして DIO#11 に接続したスイッチを操作したときに IO イベントデータを送信するようにします。送信先は下記の Addressingタブで設定します。

Destination address High/Low をデフォルト値の 0 に設定して、IOイベントデータが coordinator デバイス(DeviceServer を接続しています)に送信されるようにします。また ZigBee ネットワークのテスト時に便利な “コミッションボタン” を追加する場合に備えて、DIO#0 のモードを 1にしてプルアップも有効にしておくことをお勧めします。

スイッチを押すと DIO#11 は High -> Low に変化します。この時 DIO#11 XBee-ZB のIO イベントデータが coordinator に送信されます。スイッチを離すと DIO#11は Low->High に変化して再び  IO イベントデータが送信されます。end device から送信された IO イベントデータを coordinator が受信すると XBee-ZB のAPI フレームデータが DeviceServer に送られてイベントハンドラ (ZB_IO_DATA.lua) が実行されます。

このとき実行する ZB_IO_DATA.lua イベントハンドラは以下のようになっています。

file_id = "ZB_IO_DATA"

--[[

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

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
FrameType	フレームデータ中のFrame Type
				(16進数2桁)												92

SourceAddress	フレームデータ中のSourceAddress
				64bit アドレス(16進数16桁)								0013A200404AC397

NetworkAddress	フレームデータ中の SourceNetworkAddress
				16bit アドレス(16進数4桁)								D565

NodeIdentifier	XBee デバイスの NodeIdentifier。
				DeviceServerのマスターファイルを検索して設定される。	Node1
				マスターにNodeIdentifier未登録の場合は"" が設定される

DeviceType		XBee デバイスの Device Type
				DeviceServerのマスターファイルを検索して設定される。	01
				マスターにDeviceType未登録の場合は"" が設定される
				8bit値(16進数2桁)
				00: coordinator
                01: router
                02: end device

DeviceTypeID	XBee デバイスの Device Type Identifier
				DeviceServerのマスターファイルを検索して設定される。
				マスターにDeviceTypeID未登録の場合は"" が設定される
				32bit値(16進数8桁)										00030000

ReceiveOptions	フレームデータ中 ReceiveOptions							01
				8bit値(16進数2桁)

SAMPLE_COUNT	I/O データのサンプル数									1

SAMPLE_DIO		I/O データ中のサンプル対象となったDIOビット番号リスト
				(10進数、カンマ区切り)									0,1,4

SAMPLE_ADC		I/O データ中のサンプル対象となったADCビット番号リスト
				(10進数、カンマ区切り)									2,3

SAMPLE_<Sample#>_<"DIO"|"ADC">_<Bit#>

				I/O サンプルデータ値。
				DIO の場合は、High で "1"、Low で "0"。					1
				ADC の場合は 10進数。									1023

				<Sample#> には 最大、SAMPLE_COUNT まで 1から順番に
				インクリメントされた値が入る。

				<"DIO"|"ADC">は、I/O サンプルデータが ADC
				もしくは、DIO のどちらであるかを示す。

				<Bit#>は、サンプルデータのビット番号。10進数。

		例:"SAMPLE_1_ADC_0" は、第一サンプルデータ中の #0番ポートのADC変換値を示す。

]]

log_msg("start..",file_id)
for key,val in orderedPairs(g_params) do
	log_msg(string.format("g_params[%s] = %s",key,val),file_id)
end

実質の動作部分は最後尾の 3 行だけで、イベントハンドラに渡された IO イベントデータを全てログに出力しています。end device のスイッチを操作すると下記の様なログメッセージが出力されます。

IOイベントデータ中には XBee デバイスで digital input に設定した項目を含む複数の IO データがまとめて格納されています。この中で g_params[SAMPLE_1_DIO_11] = 1 (または 0) でログ出力されている部分がスイッチ入力のデータです。XBee-ZB のデジタル入力は、ある程度のチャタリングをフィルタリングしてくれますので、このように綺麗にスイッチの押した状態と離した状態がログに出力されると思います。

ここで今回実現する機能の、スイッチ入力ごとに LED をON,OFF を繰り返すように変更します。先ほどのイベントハンドラ スクリプトファイル(ZB_IO_DATA.lua) をテキストエディタで下記のように変更します。

file_id = "ZB_IO_DATA"

--[[

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

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
FrameType		フレームデータ中のFrame Type
				(16進数2桁)												92

SourceAddress	フレームデータ中のSourceAddress
				64bit アドレス(16進数16桁)								0013A200404AC397

NetworkAddress	フレームデータ中の SourceNetworkAddress
				16bit アドレス(16進数4桁)								D565

NodeIdentifier	XBee デバイスの NodeIdentifier。
				DeviceServerのマスターファイルを検索して設定される。	Node1
				マスターにNodeIdentifier未登録の場合は"" が設定される

DeviceType		XBee デバイスの Device Type
				DeviceServerのマスターファイルを検索して設定される。	01
				マスターにDeviceType未登録の場合は"" が設定される
				8bit値(16進数2桁)
				00: coordinator
                01: router
                02: end device

DeviceTypeID	XBee デバイスの Device Type Identifier
				DeviceServerのマスターファイルを検索して設定される。
				マスターにDeviceTypeID未登録の場合は"" が設定される
				32bit値(16進数8桁)										00030000

ReceiveOptions	フレームデータ中 ReceiveOptions							01
				8bit値(16進数2桁)

SAMPLE_COUNT	I/O データのサンプル数									1

SAMPLE_DIO		I/O データ中のサンプル対象となったDIOビット番号リスト
				(10進数、カンマ区切り)									0,1,4

SAMPLE_ADC		I/O データ中のサンプル対象となったADCビット番号リスト
				(10進数、カンマ区切り)									2,3

SAMPLE_<Sample#>_<"DIO"|"ADC">_<Bit#>

				I/O サンプルデータ値。
				DIO の場合は、High で "1"、Low で "0"。					1
				ADC の場合は 10進数。									1023

				<Sample#> には 最大、SAMPLE_COUNT まで 1から順番に
				インクリメントされた値が入る。

				<"DIO"|"ADC">は、I/O サンプルデータが ADC
				もしくは、DIO のどちらであるかを示す。

				<Bit#>は、サンプルデータのビット番号。10進数。

		例:"SAMPLE_1_ADC_0" は、第一サンプルデータ中の #0番ポートのADC変換値を示す。

]]

log_msg("start..",file_id)
--for key,val in orderedPairs(g_params) do
--	log_msg(string.format("g_params[%s] = %s",key,val),file_id)
--end

------------------------------------------------------------------------
-- DIO#11 のスイッチが押されてボタンから手を離したタイミングで処理する
------------------------------------------------------------------------
if g_params["SAMPLE_1_DIO_11"] and (g_params["SAMPLE_1_DIO_11"] == "1") then
	local dev = g_params["SourceAddress"]	-- XBee-ZB デバイスの64bitアドレス文字列
	local state_key = dev .. "_DIO"			-- LED の ON,OFF のステートをサーバーでデバイス毎に保存するためのキー名

	-----------------------------------------------------------------------------
	-- ボタンを押すごとに LED のステート(共有メモリ値) を Null,"1" の間で繰り返す
	-----------------------------------------------------------------------------
	local stat,val = inc_shared_data(state_key)
	if not stat then error() end
	if (tonumber(val) == 1) then
		-------------------------------------------------
		-- XBee-ZB DIO#1 を "digital output high" に設定
		-------------------------------------------------
		if not zb_at_command(dev,"D1","05") then error() end
	else
		-------------------------------------------------
		-- XBee-ZB DIO#1 を "digital output low" に設定
		-------------------------------------------------
		if not zb_at_command(dev,"D1","04") then error() end
		if not set_shared_data(state_key,"") then error() end -- 共有メモリ値をクリア
	end
end

さきほどのイベントデータをログに出力する部分を “–” でコメントアウトしています。最初の if 文では IO イベントデータ中に XBee-ZB の DIO#11 のデータが含まれているかどうかと、含まれていた場合にその値が 1 (High,スイッチを離した瞬間) であることを確認しています。

このイベントハンドラは PAN 内に属している XBee-ZB から送信された IO イベントデータを受信したときに共通してコールされます。dev = g_params["SourceAddress"] 文で送信元デバイスアドレス を dev 変数に入れて、LED のステート管理をサーバーで保管するときの共有変数のキーとして使用します。これによって複数の XBee-ZB デバイスがあった場合でもデバイスごとにLED のステート(ON,OFF)を管理できるようにしています。

zb_at_command(dev,”D1″,”05″) 文はリモート XBee-ZB デバイスに任意の AT コマンドを送信する API ライブラリ関数です。AT コマンド “D1″ でパラメータ “05″(0×05) を送信すると DIO#1 が High になって LED が点灯します。パラメータ “04″ を送ると Low になって LED が消灯します。

これまでの設定手順と実際に動作させたときの様子を是非ご覧ください(音量注意)

ABS-9000 DeviceServer ではこのように、XBee-ZB を使用した ZigBee ネットワークを簡単に構築できます。ぜひダウンロードして使用してみてください。