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 ネットワークを簡単に構築できます。ぜひダウンロードして使用してみてください。

 

XBee(Series2) 機能を追加しています

久々の更新になってしまいました、

ABS-9000 DeviceServer の XBee デバイス機能ではXBee(Series1)のみをサポートしていましたが、 新たに XBee Series2 ZB(以下 “XBee(ZB)” と略します) 用のサービスモジュールの追加作業をしています。リリースの目処がついてきましたので作業中の画面構成などを紹介していきたいと思います。また、XBee(ZB) を使用するにあたって皆様にも役立つ(豆情報??) も同時に提供していきたいと思います。

既存の DeviceServer ライセンス(XBee 接続機能) をお持ちの方は、アップデートすることでXBee(Series1) と XBee(ZB) を同時に両方使用できるようになる予定です。

XBee(ZB) のデバイス管理画面のデバイス一覧は以下のようになっています。

XBee(ZB)デバイス管理画面のデバイス一覧

XBee(Series1) とは違って、16bitアドレスは Network Address として自動管理されていますので、管理画面では最新の値を表示します。DeviceServer ではリモートXBee(ZB)から送信されたフレームデータ中に含まれる、最新の16bit Network Address情報をキャッシュテーブルに保管して自動管理しています。この機能によってスクリプトやクライントプログラム からリモートXBee(ZB)デバイスにアクセスする時のパフォーマンスを高めています。

XBee(ZB) デバイスの詳細設定はもちろん GUI から行えます。デバイス一覧から XBee(ZB) を選択すると下記のような設定画面が表示されます。

XBee(ZB)デバイスの詳細設定画面(画面はまだ機能追加作業中のものです)

XBee(ZB) から送信される各種イベントフレームデータは DeviceServer のイベントハンドラスクリプト(Lua) を記述して簡単に扱うことができます。下記の画面は、XBee(ZB) デバイスのDIO と ADC、サンプリングレートを設定して、リモート側の XBee(ZB) から定期的に送信される I/O イベントフレーム受信時に実行したイベントハンドラ(Lua スクリプトファイル) のログです。

リモートの XBee(ZB) から送信された I/O フレームデータのログ

イベントハンドラスクリプト中から、フレームデータ中にある DIO や ADC値を簡単に利用できるように予め変数に値が代入されています。”Node Identifier” やデバイスタイプ情報など、XBee(ZB) のフレームデータ中には含まれない情報は DeviceServer で管理しているマスター情報から自動的に補完されますので、アプリの作成が楽になります。

その他にも XBee(ZB)特有の機能を豊富にサポートしていますので、これらも紹介していきたいと思います。また XBee(ZB) のサポート機能について ”こんなことがしたい等xx” ご要望がありましたら、遠慮なく contact@allbluesystem.com までメールをお寄せ下さい、よろしくお願いします。

それではまた、

 

UIOUSB (USB接続のI/O装置)をスマートフォン(iPod touch)から操作する

今回は、オールブルーシステムで提供している USB 経由で操作する I/O 装置(UIOUSB) を、スマートフォン等(iPod touch)から操作するWeb アプリケーションについて説明したいと思います。

DeviceServer をインストールすると、PC のWeb ブラウザ上から UIOUSB デバイスを GUI で操作するためのFlash アプリがインストールされます。(下記のキャプチャ画像参照)

今回は、この Flash アプリの デジタル I/O ポート操作部分を、Web アプリ(HTML + JavaScript) で作成した例を紹介します。スマートフォン等では Flash が動作しない場合が多いので、今回のように HTML5 Web アプリ(HTML + JavaScript) で作成することで、iPhone や iPad, Android など端末やOS の種類を問わずに操作できるようになります。Web アプリを起動すると、最初にログイン画面が表示されます。

ユーザー名とパスワードを入力して Login ボタンを押すとユーザー認証を行います。DeviceServer 側でユーザー認証に成功すると、I/O ポートの操作画面が表示されます。

チェックボックスを操作することで、UIOUSB デバイスの DIO#0 から DIO#7 までのデジタルポートをリアルタイムに ON/OFF に切り替えることができます。予めUIOUSBデバイスの I/O ポートは出力モードに設定しておく必要があります。タイトルバー上の “Logout”  ボタンを押すと確認画面の後、操作を終了してログアウトします。

ここから、今回のWeb アプリについて説明します。この記事で紹介した Web アプリのファイルはここからダウンロードできます。Web アプリは、HTML ファイルと JavaScript ファイル、jQuery 等のライブラリから構成されています。Web アプリの配信と Web API 機能は、DeviceServer 内蔵の HTTP サーバー機能を使用しています。DeviceServer で設定された HTTP サーバールートディレクトリ(“C:\Program Files\AllBlueSystem\WebRoot”) の下に “uiousb_io” ディレクトリを作成して、その中に今回の Web アプリのファイルを格納します。Web アプリのファイルを格納したディレクトリをエキスプローラで表示すると下記の様になっています。

DeviceServer の”サーバー設定プログラム” で HTTP サーバー機能と Web API 機能を有効に設定すると、このWebアプリが使用できるようになります。iPod touch 等から http://<DeviceServerの動作するPC ホスト名またはIP アドレス>/uiousb_io/index.html にアクセスすると Web アプリが起動されます。

Web アプリ中の index.html ファイルの内容は下記の様になっています。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>UIOUSB DIGITAL I/O</title>
        <link rel="stylesheet" href="./css/themes/default/jquery.mobile-1.2.0.css" />
        <link rel="stylesheet" href="my.css" />
        <script src="./js/jquery.js"></script>
        <script src="./js/jquery.mobile-1.2.0.js"></script>
        <script src="./device_server/webapi.js"></script>
    </head>
    <body>

        <div data-role="page" id="login" data-theme="a">
            <div data-theme="a" data-role="header" data-position="inline">
                <h3>ユーザー認証</h3>
            </div>

			<ul data-role="listview">
				<li data-role="fieldcontain">
                    <fieldset data-role="controlgroup">
  						<div><h1>&nbsp</h1></div>
                    </fieldset>
                    <fieldset data-role="controlgroup">
                        <label for="textinput1">Name</label>
                        <input id="login_name" placeholder="" value="" type="text" />
                    </fieldset>
                    <fieldset data-role="controlgroup">
                        <label for="textinput2">Password</label>
                        <input id="login_password" placeholder="" value="" type="password" />
                    </fieldset>
                    <fieldset data-role="controlgroup">
  						<div><h1>&nbsp</h1></div>
                    </fieldset>
                    <fieldset data-role="controlgroup">
						<div class="ui-grid-b">
							<div class="ui-block-a"></div>
							<div class="ui-block-b">
		                		<a data-role="button" data-inline="true" data-theme="a" data-icon="check" data-iconpos="left" id="login_btn" >Login</a>
						    </div>
							<div class="ui-block-c"></div>
						</div>
                    </fieldset>
				</li>
			</ul>

            <div data-theme="a" data-role="footer">
                <h3>ABS-9000 DeviceServer</h3>
            </div>
        </div>

        <div data-role="page" id="page1">
            <div data-theme="a" data-role="header">
				<a data-icon="home" id="logout_btn" href="#logout_caution" data-rel="dialog" data-transition="pop">Logout</a>
                <h3>UIOUSB I/O</h3>
            </div>
            <div data-role="content" data-theme="a">
                <div class="ui-grid-a">
                    <div class="ui-block-a" data-theme="a">
                        <div data-role="fieldcontain">
                            <fieldset data-role="controlgroup" data-type="vertical">
                                <input name="checkbox0" id="checkbox0" type="checkbox" class="pin" pin="0"/>
                                <label for="checkbox0">
                                    DIO#0
                                </label>
                                <input name="checkbox1" id="checkbox1" type="checkbox" class="pin" pin="1"/>
                                <label for="checkbox1">
                                    DIO#1
                                </label>
                                <input name="checkbox2" id="checkbox2" type="checkbox" class="pin" pin="2"/>
                                <label for="checkbox2">
                                    DIO#2
                                </label>
                                <input name="checkbox3" id="checkbox3" type="checkbox" class="pin" pin="3"/>
                                <label for="checkbox3">
                                    DIO#3
                                </label>
                                <input name="checkbox4" id="checkbox4" type="checkbox" class="pin" pin="4"/>
                                <label for="checkbox4">
                                    DIO#4
                                </label>
                                <input name="checkbox5" id="checkbox5" type="checkbox" class="pin" pin="5"/>
                                <label for="checkbox5">
                                    DIO#5
                                </label>
                                <input name="checkbox6" id="checkbox6" type="checkbox" class="pin" pin="6"/>
                                <label for="checkbox6">
                                    DIO#6
                                </label>
                                <input name="checkbox7" id="checkbox7" type="checkbox" class="pin" pin="7"/>
                                <label for="checkbox7">
                                    DIO#7
                                </label>
                            </fieldset>
                        </div>
                    </div>
                </div>
            </div>
        </div>

		<div data-role="page" id="login_error_dialog">
			<div data-role="header" data-theme="e">
				<h3>*ERROR*</h3>
			</div>
			<div data-role="content" data-theme="e">
				<h2>ログインに失敗しました</h2>
				<p>ユーザー名またはパスワードが間違っています。システムのログイン制限により失敗している場合があります</p>
				<p><a href="#login" data-role="button" data-inline="true" data-icon="check" data-theme="c">戻る</a></p>
			</div>
		</div>

		<div data-role="page" id="logout_caution">
			<div data-role="header" data-theme="e">
				<h3>*WARNING*</h3>
			</div>
			<div data-role="content" data-theme="e">
				<h2>ログアウトしますか?</h2>
				<p>ログアウト操作を行う場合には "OK" を押してください。"キャンセル" で元の画面に戻ります</p>
				<p><a data-role="button" data-inline="true" data-icon="back" data-rel="back" data-theme="c">キャンセル</a>
				   <a data-role="button" data-inline="true" data-icon="check" id="logout_ok_btn" data-theme="c">OK</a></p>
			</div>
		</div>

		<div data-role="page" id="error_quit_dialog">
			<div data-role="header" data-theme="e">
				<h3>*ERROR*</h3>
			</div>
			<div data-role="content" data-theme="e">
				<h2>エラーが発生しました</h2>
				<p>サーバー処理中にエラーが発生しました。現在のセッションが無効になっている場合があります。再ログイン操作を行ってください</p>
				<p><a data-role="button" data-inline="true" data-icon="check" id="server_error_ok_btn" data-theme="c">OK</a></p>
			</div>
		</div>

		<div data-role="page" id="error_back_dialog">
			<div data-role="header" data-theme="e">
				<h3>script error</h3>
			</div>
			<div data-role="content" data-theme="e">
				<h3>エラーが発生しました</h3>
				<p>サーバー処理中にエラーが発生しました。スクリプト実行中にエラーが発生した可能性がありますのでサーバー側のログを確認して下さい</p>
				<p><a data-role="button" data-inline="true" data-icon="check" data-rel="back" data-theme="e">OK</a></p>
			</div><!-- /content -->
		</div>

		<script src="main.js"></script>

    </body>
</html>

HTML ファイルでは、ログイン認証画面と I/Oポート操作画面、エラーや警告のダイアログ画面を定義しています。GUI は jQuery-mobile ライブラリを使用して作成しています。

Web アプリの動作ロジックが記述された main.js ファイルは下記の様になっています。

// スクリプト実行結果ステータスのみをチェック
function script_exec_callback(data){
	if (data.Result != "Success"){
		if(data.ErrorText.match(/CertifyUpdateSession failed/i)) {
			$.mobile.changePage( "#error_quit_dialog", {transition: "pop",role:"dialog"});
		} else {
			$.mobile.changePage( "#error_back_dialog", {transition: "pop",role:"dialog"});
		}
	}
}

// スクリプト実行結果ステータスチェックとUI 値リロード
function script_exec_callback_reload(data){
	if (data.Result != "Success"){
		if(data.ErrorText.match(/CertifyUpdateSession failed/i)) {
			$.mobile.changePage( "#error_quit_dialog", {transition: "pop",role:"dialog"});
		} else {
			$.mobile.changePage( "#error_back_dialog", {transition: "pop",role:"dialog"});
		}
	}
	load_uiousb_dio();
}

// UI コンポーネントの xml 属性値を検索取得
function getAttrVal(node,name){
	var val = "";
	var attr =  node.attributes;
	for (var i=0; i<attr.length; i++){
		if (attr[i].nodeName == name){
			val = attr[i].nodeValue;
		}
	}
	return val;
}

// IO ポート値の配列を受け取ってチェックボックスの値に反映させる
// パラメータフォーマット {"DIGITAL_PORT":"xx"}
function apply_ui(ports){
	var pval;
	var	flag;

	if (ports.DIGITAL_PORT != ""){
		pval = parseInt(ports.DIGITAL_PORT,16);

		flag = ((pval & (1 << 0)) != 0);
		if ($("#checkbox0").checked != flag) $("#checkbox0").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 1)) != 0);
		if ($("#checkbox1").checked != flag) $("#checkbox1").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 2)) != 0);
		if ($("#checkbox2").checked != flag) $("#checkbox2").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 3)) != 0);
		if ($("#checkbox3").checked != flag) $("#checkbox3").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 4)) != 0);
		if ($("#checkbox4").checked != flag) $("#checkbox4").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 5)) != 0);
		if ($("#checkbox5").checked != flag) $("#checkbox5").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 6)) != 0);
		if ($("#checkbox6").checked != flag) $("#checkbox6").attr("checked",flag).checkboxradio("refresh");
		flag = ((pval & (1 << 7)) != 0);
		if ($("#checkbox7").checked != flag) $("#checkbox7").attr("checked",flag).checkboxradio("refresh");

	}

}

// UIOUSB_IO_GET スクリプト実行結果のイベントハンドラ。
function io_get_handler(data){
	if (data.Result != "Success"){
		if(data.ErrorText.match(/CertifyUpdateSession failed/i)) {
			$.mobile.changePage( "#error_quit_dialog", {transition: "pop",role:"dialog"});
		} else {
			$.mobile.changePage( "#error_back_dialog", {transition: "pop",role:"dialog"});
		}
		return;
	}
	apply_ui(data.ResultParams);
}

// UI コンポーネントの初期値ロード
function load_uiousb_dio(){
	var params = {};
	script_exec("UIOUSB_IO_GET",params,"io_get_handler");
}

// メインページが表示された
$( '#page1' ).live( 'pageshow',function(event){
	load_uiousb_dio();
});

// チェックボックスを操作してUIOUSB DIOの値が更新された
$('input[class="pin"]' ).bind( "change", function(event, ui){
	var params = {};
	var attrVal = getAttrVal(this,"pin");
	if (attrVal != ""){
		params["pin"] = attrVal;
		if (this.checked){
			params["value"] = "1";
		} else {
			params["value"] = "0";
		}
		script_exec("UIOUSB_PIN_SET",params,"script_exec_callback");
	}
});

////////////////////////////////////////////////////////////////

// サーバー側でログイン操作が成功したらメイン画面に移動する
function login_callback(data){
	if (data.Result == "Success"){
		session_token = data.SessionToken;
		// dump_login_result(data);
		$.mobile.changePage( "#page1", { transition: "slide"});
	} else {
		$.mobile.changePage( "#login_error_dialog", {transition: "pop",role:"dialog"});
	}
}

// サーバー側でログアウト操作が完了したらログイン画面に戻る
function logout_callback(data){
	session_token = "";
	$("#login_password").val("");
	$.mobile.changePage( "#login", { transition: "pop"});
}

// ログインボタンを押した
$( "#login_btn" ).bind( "click", function(event, ui){
	var user = $("#login_name").val();
	var pass = $("#login_password").val();
	login(user,pass,"login_callback");
});

// ログアウトボタンを押した
$( "#logout_ok_btn" ).bind( "click", function(event, ui){
	logout("logout_callback");
});

// サーバーエラーのダイアログから復帰する場合はログイン画面に戻る
$( "#server_error_ok_btn" ).bind( "click", function(event, ui){
	session_token = "";
	$.mobile.changePage( "#login", { transition: "pop"});
});

// ログインページが表示された
$( '#login' ).live( 'pageshow',function(event){
	// セッショントークンが指定されている場合にはユーザー認証を省略する
	if (session_token != ""){
		$.mobile.changePage( "#page1", { transition: "slide"});
	}
});

main.js (JavaScript) ファイルでは、ログインボタンやDIO#xx チェックボックスを操作した時に実行されるイベントハンドラを定義しています。これらのイベントハンドラでは、DeviceServer 側に配置された Lua スクリプトファイルを Web API 経由で実行しています。Web API 経由で実行する Lua スクリプトは以下の2種類あります。

UIOUSB_IO_GET.lua スクリプトファイル

file_id = "UIOUSB_IO_GET"
log_msg("start..",file_id)

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

UIOUSB デバイスの I/O 値を取得する

リターン時に返されるパラメータ
---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
DIGITAL_PORT	UIOUSB DIO の現在の値(16進数2桁)						"FC"

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

local stat,ret = uio_command("di")
if stat then
	if not script_result(g_taskid,"DIGITAL_PORT",string.sub(ret,3,5)) then error() end
else
	error()
end

このスクリプトは、Web アプリの I/Oポート操作画面が表示されたときに実行されて、現在の I/O ポート値を UIOUSB デバイスから取得します。

UIOUSB_PIN_SET.lua スクリプトファイル

file_id = "UIOUSB_PIN_SET"
log_msg("start..",file_id)

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

UIOUSB デバイスの指定したI/O ピンの値を設定する

スクリプト起動時に渡されるパラメータ
---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
pin			UIOUSB DIO#番号(0 から 7 までの整数)						"7"

value		pin に指定したI/Oポートに設定する値(0 または 1)				"1"

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

-------------------------
-- パラメータチェック
-------------------------
if not (g_params["pin"] and g_params["value"]) then
	log_msg("parameter error",file_id)
	error()
end

if (g_params["value"] == "1") then
	if not uio_don(tonumber(g_params["pin"]),true) then error() end
else
	if not uio_don(tonumber(g_params["pin"]),false) then error() end
end

このスクリプトは、Web アプリの DIO#xx のチェックボックスが操作されたときに実行されて、UIOUSB デバイスのI/O ポート値をビット毎に更新します。スクリプトパラメータとしてDIO ポートのビット番号と設定値を指定します。

ここで説明したファイル以外にも、DeviceServer の Web API を実行するためのライブラリ関数やログメッセージ出力関数などがライブラリファイル(webapi.js) として Web アプリのファイルに格納されています。これらのファイルは、ダウンロードして内容を確認して下さい。webapi.js ファイル中で定義されたグローバル変数 ”session_token” に、予め DeviceServer で作成済みのセッショントークン文字列を代入しておくことで、ログイン認証画面をスキップして直接 I/O 操作画面を表示させることもできます。詳しくは webapi.js ファイル中のコメントを参照して下さい。

下記に、Web アプリを動作させた時の動画を添付しますので参考にしてください(音量注意)

この記事で説明した Web アプリは、オールブルーシステムの “ホームセンサーキット”または、”UIOUSB キット” 製品が動作している環境で直ぐに使用することができます。

DeviceServer のセンサーネットワークで管理している全てのデバイスは、今回の記事と同様に、簡単に Web アプリケーションとしてユーザーが作成したGUI から操作することができます。スマートフォンやタブレット端末毎の専用開発環境の準備やアプリの登録審査などは一切不要で、手軽にユーザーが作成することができます。シリアルポートで接続した計測器やI/O 装置、Arduino デバイス、データベースにデータを格納するアクイジション装置などを操作する Web アプリを作成して、外出先からでもスマートフォンやタブレットで簡単に操作できるようになります。

それではまた。

 

電波時計もどき?XBee を使って I2C コントロール

今回は、ちょっとした時計ガジェットを紹介したいと思います。

ただの時計なのですが、サーバーPC から XBee 経由で時刻情報(文字列)を1分ごとに受信して時刻を表示しています。

全ての回路はブレッドボード上に作成しています。左半分は XBee と ATmega328P のCPU ボードで Arduino と XBee シールドを組み合わせた様な回路です。右半分は、I2C でコントロールする 7セグメントLED ドライバを書き込んだ ATmega168 と 4連の7セグメントLED です。全体を3.3V で動作させていますので、青色LED のドライブにはちょっと無理がありますが…

一分毎に下記のスクリプトを DeviceServer で実行しています。XBee で送信するデータパケットに i2c の書き込みコマンドを格納してLED の表示データを更新しています。

file_id = "I2C_7SEG_TEST4"

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

I2C バスに接続した 7SEG8 デバイスに現在時刻を表示

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

local xbee_device = "Device1";
local slave_addr = "32";

local now = os.date "*t";
local tmp = string.format("%2d%2.2d",now["hour"],now["min"]);

if not xbee_tdcp_safe_retry(xbee_device,"i2c_write," .. slave_addr .. ",11" .. list_to_hex(unpack(str_to_tbl(tmp)))) then error() end

この時計は時刻合わせが不要で、サーバーPCとの時間差が1 分以上にはなりませんので意外に実用的です。

XBee と XBee Explorer USB、ATmega CPU等 が手元にありましたら簡単に作成できます。7セグメントLED ドライバ(SB-7SEG8)と XBee コントロールのファームウエア(TDCP for ATmega328) はオールブルーシステムのホームページで公開していますので自由に書き込んで使用して下さい。DeviceServer を使用しななくても、直接 XBee の API パケットに i2c コントロール用のコマンド文字列を入れても動作しますので試してみて下さい。

以下に、動作中の動画を載せましたので参照してください。(音量注意) 撮影用に強制的に時刻を更新しましたので、ちょっと時刻間隔がずれています。秒毎の7セグメントLED のコロン点滅と、LED の電飾(ピカピカ光っている) はATmega328 側で動作している TDCP for ATmega328 ファームウエアの “pulse” コマンドを使用しています。

それではまた。