リモート(XBee + CPUボード)から取得したセンサデータをクラウドサービス(Xively)で表示

今回は、リモートに設置した複数のセンサーノードから取得したデータを、クラウドサービス(Xively) に定期的に送信してグラフ表示する例を紹介します。

実は、DeviceServer には組み込みのデータベース(Firebird)と統計機能がありますので、今回紹介する Xively を使用しなくてもセンサーデータの集計やグラフ作成を行うことができまます。ただ、DeviceServer 側にグラフ作成用のWebアプリやスクリプトを用意する必要があります。

クラウドサービスを利用すると、これらのアプリを作成しなくてもセンサーデータを送信するだけで簡単にグラフ表示することができます。送信したセンサデータのメンテナンス機能もクラウド側で提供されていますので本格的な運用にも応用できます。

Xivey ( https://xively.com/ ) は、データの保存とグラフ表示を行ってくれるクラウドサービスで、無料で開発者アカウントを作ってセンサーシステムの集計機能として利用することができます。アカウント登録や利用条件など詳しくは上記サイトを確認してください。いろいろなサイトでも Xively の利用方法を紹介されていますので、情報も豊富に入手できます。

今回紹介するセンサーネットワークではリモートセンサーノードを2つ用意しています。センサーノード1は、XBee Series1(Device4) と CPU ボード(ATmega1284P) を使用したセンサーノードで、アナログ温度センサ(LM35) と人感センサ(IR)が接続されています。

センサーノードのボックスを開けると中は、汎用のCPU ボードとセンサ部分に分かれています。

2つ目のセンサーノード2は、XBee-ZB Series2(Node1)と CPUボード(ATmega328P) を使用して作成しています。センサーノード2にはI2C 接続の温度センサ(TMP102)と人感センサ(IR)、明るさを測るためのアナログ照度センサ(CDS)を接続しています。

これらのリモートセンサーノードから10分に一回、定期的に DeviceServer が動作するサーバーPC にサンプリングデータを送信しています。サーバーPC では受信したサンプリングデータから摂氏温度など扱いやすい値に変換した後、Xively に送信します。

Xively サービスに送信したセンサーデータはいつでもWebブラウザから表示できるようになります。下記は最新のセンサーデータを表示しているところです。 “Graphs” をクリックすると任意の期間のデータをグラフで表示することもできます(記事の最後で紹介します)

それではこれらの仕組みを詳しく説明していきます。まずは、XBee Series1 と ATmega1284 を組み合わせたセンサーノード1について説明します。

センサーノード1の回路図は以下になります。

ATmega1284 では サンプリングデータをXBee データパケットに格納して定期的に送信するためのモニタプログラムが動作しています。モニタプログラム(TDCP プログラム)のファームウエアとマニュアルはこちらからダウンロードすることができます。

このセンサーノードでは ATmega1284 プロセッサのデジタル入力ポートに人感(IR)センサを接続して、センサが反応した回数を数えています。また、A/D ポートに LM35 アナログ温度センサを接続して現在の温度を測定します。TDCP プログラムは定期的に上記のセンサから取得したデータをサンプリングイベントデータとしてサーバーに XBee 経由で送信します。

TDCP プログラムのサンプリング機能を設定するために、下記のリモートコマンドを実行します。

sampling_rate,600  // 600秒(10分)ごとにサーバーPC に SAMPLING イベントデータを送信

TDCP モニタプログラムではこの他にも、DIOとA/Dのコンフィギュレーションやプルアップ抵抗の有無などを設定するために、以下のリモートコマンドを実行しておきます。コマンドの詳細についてはマニュアルをご覧ください。

app_mode,8     //DIO, A/D機能等をポートに割り当てるモードを設定

server_addr,0C03  // イベント送信先 XBee アドレス設定、サーバーPC 側に設置したXBee のアドレスになります。

pullup,FF // デジタル入力ポートのプルアップ設定、IR センサはオープンコレクタ出力のためプルアップを有効にします。

adc_vref,3 // A/D リファレンス電圧の設定

リモートデバイスの設定を行うと、下記のサンプリングイベントデータ文字列が格納された XBee フレームデータが10分に1回サーバーに送信されます。

センサーノード1(XBee Device4) から送信される SAMPLING イベントデータ

$$$,SAMPLING,0D04,8,FF,2,0,0,0,76

カンマ区切りで表現されたイベントデータの第6カラム(値は2) に IR センサが10分間に何回アクティブになったかを示すカウント値が入っています。また、第10カラム(値は76) には A/D #3 に接続された LM35 のアナログ出力値が格納されています。

上記の SAMPLING イベントデータを格納した XBee フレームデータを受信したときに、DeviceServer は XBEE_TDCP_DATA イベントハンドラ(Luaスクリプト) を実行します。

file_id = "XBEE_TDCP_DATA"

--[[

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

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

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

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

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

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

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------
APIType			フレームデータ中のAPI Type(16進数2桁)					81

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

SerialNumber	XBee デバイスの SerialNumber
				DeviceServer に保持されたマスターファイルを使用して、
				SourceAddress から変換した値が設定される。				0013A200404AC397

NodeIdentifier	XBee デバイスの NodeIdentifier。
				DeviceServer に保持されたマスターファイルを使用して、
				SourceAddress から変換した値が設定される。				Device1

RSSI			フレームデータ中のRSSI(16進数2桁)						45

Options			フレームデータ中Options									00

TDCP_WHOLE		カンマ区切りのTDCP データ全体							"$$$,CHANGE_DETECT,0A01,8,01,FE"

TDCP_COUNT		TDCP データカラム数										2

TDCP_<Column#>	TDCP データ値(ASCII 文字列)
				TDCP_1 は常にコマンドプリフィックス文字列を表す			"$$$1234"
				"$$$" で始まり、0文字以上の任意の文字列が後に続く。

				TDCP_2 はコマンド実行ステータスを表す					"1"
				"1" はコマンド実行成功、"0" は失敗を示す
				イベントデータの場合にはイベント名が入る

				TDCP_3以降のデータはTDCPコマンド毎に決められた、
				オプション文字列が入る	

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

]]

log_msg(g_params["NodeIdentifier"] .. "[" .. g_params["SourceAddress"] .. "," .. g_params["SerialNumber"] .. "] TDCPData = " .. g_params["TDCP_WHOLE"],file_id)

-------------------------------------------------------------------
-- サンプリングデータをデータベースに登録する
-- 時間がかかる処理を行うため、別スレッドでスクリプトを実行します
-------------------------------------------------------------------
if not script_fork_exec("XBEE_SENSOR_DATA_STORE",g_params) then error() end

イベントハンドラスクリプトの最後の部分に処理が記述されています。

サンプリングデータを解析してセンサーデータのデータベース登録や、Xively クラウドサービスへ送信するときに時間がかかる場合がありますので、処理部分を別スクリプト XBEE_SENSOR_DATA_STORE に作成して、このスクリプトを別スレッドで実行するようにしています。このように別スレッドに処理に分けることで、XBEE_TDCP_DATA イベントハンドラに別の処理を行うためにスクリプトを追加した場合でも、それらの処理開始時間がクラウド側へのデータ送信時間などに影響されないようになります。

XBEE_TDCP_DATA イベントハンドラに渡されているスクリプトパラメータ(g_params[] )は、連想配列のまま全て XBEE_SENSOR_DATA_STORE スクリプトのスクリプトパラメータとして渡します。XBEE_SENSOR_DATA_STORE は下記の様になっています。

file_id = "XBEE_SENSOR_DATA_STORE"

--[[

●機能概要

TDCP デバイスから送信されたサンプリングデータを DeviceServerに登録
するスクリプトの例です。このスクリプトはユーザー環境に合わせて
カストマイズして使用してください。

パラメータで指定した XBeeデバイスに接続している TDCPボード上の
センサーデータを取得してデータベースに登録します。

このスクリプトは、リモートデバイスから定期的に送信されてくる
SAMPLINGイベントのイベントハンドラ中からコールされて使用できるように
しています。

リモートデバイスに接続した各種センサーから、I2C、SPI、A/D変換入力、
デジタルポート、カウンタ入力ポート経由で現在の測定値を取得します。
測定した値は、センサーデータとして扱いやすい値に変換した後
データベースに登録します。

リモートデバイスごとに、接続しているセンサーが異なる場合には、
スクリプト内でデバイスの NodeIdentifier を元に処理を分けることができます。

データベースに登録するときには、キー名に NodeIdentifier と センサー種別
を示す文字列を含めると、後のデータ処理が行い易くなります。

●リクエストパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

TDCP_1			TDCPイベント文字列第1カラム(Prefix文字列)				"$$$"

TDCP_2			TDCPイベント文字列第2カラム(イベント種別)				"SAMPLING

TDCP_3			送信元XBee 16ビットアドレス								"0D04"

TDCP_4			TDCPイベント文字列第3カラム(app_mode)					"8"

TDCP_5			TDCPイベント文字列第4カラム(イベントデータ#1)			"0F"

TDCP_6			TDCPイベント文字列第5カラム(イベントデータ#2)			"0"
..
..

TDCP_N			TDCPイベント文字列第4カラム(イベントデータ#n)			"123"

( XBEE_TDCP_DATA イベントハンドラに渡された全てのパラメータを、このスクリプトを
  コールする時にも指定する)

NodeIdentifier		XBee デバイスのNodeIdentifier						"Device1"

●リターンパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

●備考

●変更履歴

]]

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

local dev = g_params["NodeIdentifier"]
local param = {}

---------------------------------------------------------------------------------
-- XBeeデバイスに接続された TDCPリモートCPUボードに、以下のセンサーデバイスが
-- 接続されている場合の例です, app_mode は 8 に設定されているものとします
--   * アナログ温度センサ	(A/D#3に接続)
--   * IR(人感)センサ		(DIO#4/COUNTER入力接続)
---------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- TDCP イベント種別、TDCP app_modeが一致するものだけを選択して
-- サンプリングデータをデータベースに格納する。
--------------------------------------------------------------------------------
if (g_params["TDCP_2"] == "SAMPLING") and (g_params["TDCP_4"] == "8") then 

	---------------------------------------------
	-- Xively サービスに登録するためのパラメータ
	---------------------------------------------
	local xively_ch_data = {}

	-----------------------------------------------------------
	-- A/D#3 に接続された LM35センサから温度データを取得
	-----------------------------------------------------------
	local temperature = (100 * 2.56 * tonumber(g_params["TDCP_10"])) / 1024;

	-----------------------------------------------------------------
	-- 統計データベースに測定値を保存、共有データには最新の値を保存
	-- キー名  "SENSOR_TP_<NodeIdentifier>"
	-----------------------------------------------------------------
	if not add_stat_data("SENSOR_TP_" .. dev,tostring(temperature)) then error() end
	if not set_shared_data("SENSOR_TP_" .. dev,tostring(temperature)) then error() end
	xively_ch_data["SENSOR_TP_" .. dev] = tostring(temperature) -- for Xively

	-----------------------------------------------------------------------------
	-- SAMPLING イベント発生時の COUNTER値 はIRセンサーのカウンタ値が格納されている
	--
	-- 統計データベースに測定値を保存、共有データには最新の値を保存
	-- キー名  "SENSOR_IR_<NodeIdentifier>"
	-----------------------------------------------------------------------------
	if not add_stat_data("SENSOR_IR_" .. dev,g_params["TDCP_6"]) then error() end
	if not set_shared_data("SENSOR_IR_" .. dev,g_params["TDCP_6"]) then error() end
	xively_ch_data["SENSOR_IR_" .. dev] = g_params["TDCP_6"] -- for Xively

	-----------------------------------------------------------------------------
	-- Xively サービスにセンサーデータを登録
	-----------------------------------------------------------------------------
	if not script_exec("XIVELY_DATA_STORE",xively_ch_data) then error() end

end

スクリプトの最初の部分で、イベントハンドラからコールされたこのスクリプトが処理対象とするかどうかを判断しています。イベント名が格納されたスクリプトパラメータの内容が “SAMPLING” になっているかと、イベントデータ中に格納された TDCP モニタプログラムの動作モードが設定した内容と一致しているかをチェックしています。これによって将来別のセンサを搭載したセンサーノードを追加したときに、処理をわけることができます。

このスクリプトでは Xively にセンサデータを送信するだけではなく、DeviceServer 内蔵の統計データベースにも同時にセンサデータを格納しています。また、最新のセンサデータを DeviceServer の他のモジュールから利用しやすいようにグローバル共有変数にも格納しています。

XBEE_SENSOR_DATA_STOREスクリプト中に作成した xively_ch_data[] 連想配列変数に、温度センサと IR センサのデータを一時的に格納しています。配列のキー名にはそれぞれ以下の名前を割り当てています。(”Device4″ はセンサーノード1に接続された XBee Series1 の NodeIdentifier 文字列です)

SENSOR_TP_Device4       センサーノード1の温度データ

SENSOR_IR_Device4      センサーノード1の人感センサ(IR) データ

Xively サービスにセンサデータを登録するために作成した XIVELY_DATA_STORE スクリプトのスクリプトパラメータに上記の xively_ch_data[] 連想配列変数 を指定してコールします。XIVELY_DATA_STORE スクリプトの内容は以下になります。

file_id = "XIVELY_DATA_STORE"

--[[

●機能概要

Xivelyの feed にデータを格納する

●スクリプト中の変数に初期設定が必要な項目

---------------------------------------------------------------------------------
変数名			値		            									値の例
---------------------------------------------------------------------------------
feed_id			Xivery サービスに登録したデバイスの Feed ID				"12345678"

apikey			Xivery サービスに登録したアカウントに割り当てられた
				ApiKey 文字列	 	"sadfj3225n25thisisasampleapikeyj2kn2kjk3j53"

●リクエストパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

<channel_id#1>	Xiveryサービスに登録したデバイス中のチャンネル文字列をスクリプト
				パラメータのキーとして指定する。パラメータ値には、
				現在の測定値を文字列で格納する							"1.234"
..
..
<channel_id#n>

●リターンパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

●備考

Xively API にアクセスするときに TCP/80(http) ポートを使用しています。

スクリプトパラメータに指定した複数のチャンネルに対するデータ値を同時に
登録することができます。パラメータで指定した各チャンネルのデータ値は
current_value に登録されます。

●変更履歴

2014/05/07	初版作成

ABS-9000 DeviceServer        copyright(c) All Blue System

]]

local feed_id = "<your_Xively_device_feedid>"	-- 自分の環境に合わせて変更してください
local apikey = "<your_Xively_API_key>"			-- 自分の環境に合わせて変更してください

local host = "api.xively.com"
local port = 80
local path = "/v2/feeds/" .. feed_id ..  ".json"

-----------------------------------------------------------------------------------
-- スクリプトパラメータで渡されたチャンネル毎のセンサデータを JSON 文字列に変換する
-----------------------------------------------------------------------------------
local json_str = '{ "version": "1.0.0", "datastreams": ['
local first = true
for key,val in pairs(g_params) do
	if first then
		first = false
	else
		json_str = json_str .. ","
	end
	json_str = json_str .. string.format('{"id":"%s","current_value": "%s"}',key,val)
end
json_str = json_str .. ']}'
log_msg(json_str,file_id) -- 登録データ確認用にログ出力

---------------------
-- HTTP PUT データ
---------------------
local data = {}
table.insert(data,json_str)

-----------------------
-- HTTP カスタムヘッダ
-----------------------
local header = {}
header["X-ApiKey"] = apikey

--------------------------------------------
-- Xively API をコールしてデータ登録
--------------------------------------------
local stat,rpl = http_put(host,port,path,data,header)
if not stat then error() end

スクリプトパラメータで渡された複数のセンサーデータ値のキーを Xively 側の Channel名としてXively にデータを登録します。センサーデータ値と Channel 名を Xively の登録用 API のリクエストフォーマット文字列(JSON) に変換した後、HTTP PUT コマンドでデータを転送しています。

http_put() ライブラリ関数は DeviceServer のスクリプトから HTTP PUT コマンドで任意のデータを送信することができます。またオプションで HTTP ヘッダに任意のキーと値のペアを指定することができます。Xively ではこの HTTP ヘッダ部分にユーザーアカウントに割り当てられた ApiKey 文字列を設定しています。

Xively 側では予めセンサーデータの格納用の Channel を作成しておきます。また、Xively のアカウントを作成したときにアサインされる ApiKey と feed ID をスクリプト中に設定しておきます。

これで、センサーノード1から定期的に送信されるセンサデータがクラウド側に保存されるようになりました。

次に、センサーノード2側の説明をします。センサーノード1と比べると、使用する XBee デバイスが XBee Series1 と XBee-ZB Series2の違いだけで、ほぼ処理の流れは同じです。

センサーノード2に使用しているモニタプログラムと回路については、こちらの記事この記事で詳しく紹介していますので参照してください。センサーノード1と同様に TDCP モニタプログラムが動作していて、デジタル入力ポートに IR センサが、アナログ入力ポートには照度(CDS)センサが接続されています。また、温度センサは I2C バスに接続するタイプのためアナログ入力ではなく SDA, SCL ラインに接続しています。

TDCP モニタプログラムでは以下の設定を行います。

sampling_rate,600  // 定期的にサンプリングデータをサーバーに送信する間隔(10分)

dio_config,00  // DIO ポートを全てデジタル入力として使用する

dio_pullup,0F  // デジタル入力ポートのプルアップ設定、IR センサはオープンコレクタ出力のためプルアップを有効にします。

adc_vref,1 // A/D リファレンス電圧の設定

i2c_use,1  // I2C 機能を有効にする

リモートデバイスの設定を行うと、下記のサンプリングイベントデータ文字列が格納された XBee-ZB フレームデータが10分に1回サーバーに送信されます。

センサーノード1(XBee-ZB Node1) から送信される SAMPLING イベントデータ

$$$,SAMPLING,32,0F,0,16,804,797,791,787

カ ンマ区切りのイベントデータの第6カラム(値は16) に IR センサが10分間に何回アクティブになったかのカウント値が入っています。また、第7カラム(値は804) には A/D #0 に接続された CDS のアナログ出力値が格納されています。

ここでは、I2C バスに接続された温度センサ(TMP102)の値は、サンプリングイベントデータには含まれていない点に注意してください。I2Cバス接続のデバイスはスレーブデバイスの指定と、レジスタへの書き込みや読み込みトランザクションを実行して、初めてデータを取得できます。これらのデバイス毎に異なる I2Cバス操作は SAMPLING イベントでは行えません。このため、今回は SAMPLING イベント受信時に実行されるイベントハンドラを実行するタイミングで、サーバー側からリモートコマンドを実行して I2Cバスを操作して温度センサからデータを取得します。(詳しくは後述)

SAMPLING イベントデータを含んだ XBee-ZB フレームデータを受信したときには、DeviceServer では ZB_TDCP_DATA イベントハンドラが実行されます。

file_id = "ZB_TDCP_DATA"

--[[

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

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

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

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

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

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

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

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桁)

TDCP_WHOLE		カンマ区切りのTDCP データ全体							"$$$,CHANGE_DETECT,8,01,FE"

TDCP_COUNT		TDCP データカラム数										2

TDCP_<Column#>	TDCP データ値(ASCII 文字列)
				TDCP_1 は常にコマンドプリフィックス文字列を表す			"$$$1234"
				"$$$" で始まり、0文字以上の任意の文字列が後に続く。

				TDCP_2 はコマンド実行ステータスを表す					"1"
				"1" はコマンド実行成功、"0" は失敗を示す
				イベントデータの場合にはイベント名が入る

				TDCP_3以降のデータはTDCPコマンド毎に決められた、
				オプション文字列が入る	

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

]]

log_msg(g_params["NodeIdentifier"] .. "[" .. g_params["NetworkAddress"] .. "," .. g_params["SourceAddress"] .. "," .. g_params["DeviceType"] .. "," .. g_params["DeviceTypeID"] .. "] TDCPData = " .. g_params["TDCP_WHOLE"],file_id)

-------------------------------------------------------------------
-- サンプリングデータをデータベースに登録する
-- 時間がかかる処理を行うため、別スレッドでスクリプトを実行します
-------------------------------------------------------------------
if not script_fork_exec("ZB_SENSOR_DATA_STORE",g_params) then error() end

XBee Series1 を使用したセンサーノード1のイベントハンドラ XBEE_TDCP_DATA と同様に、最後の部分にデータベース登録や Xively サービスへの送信処理を行うための別スクリプト ZB_SENSOR_DATA_STORE を別スレッドでコールしています。

ZB_SENSOR_DATA_STORE スクリプトには ZB_TDCP_DATA イベントハンドラに渡された全てのスクリプトパラメータが連想配列のまま全て渡しています。ZB_SENSOR_DATA_STORE は下記の様になっています。

file_id = "ZB_SENSOR_DATA_STORE"

--[[

●機能概要

TDCP デバイスから送信されたサンプリングデータを DeviceServerに登録
するスクリプトの例です。このスクリプトはユーザー環境に合わせて
カストマイズして使用してください。

パラメータで指定した XBeeデバイスに接続している TDCPボード上の
センサーデータを取得してデータベースに登録します。

このスクリプトは、リモートデバイスから定期的に送信されてくる
SAMPLINGイベントのイベントハンドラ中からコールされて使用できるように
しています。

リモートデバイスに接続した各種センサーから、I2C、SPI、A/D変換入力、
デジタルポート、カウンタ入力ポート経由で現在の測定値を取得します。
測定した値は、センサーデータとして扱いやすい値に変換した後
データベースに登録します。

リモートデバイスごとに、接続しているセンサーが異なる場合には、
スクリプト内でデバイスの NodeIdentifier を元に処理を分けることができます。

データベースに登録するときには、キー名に NodeIdentifier と センサー種別
を示す文字列を含めると、後のデータ処理が行い易くなります。

●リクエストパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

TDCP_1			TDCPイベント文字列第1カラム(Prefix文字列)				"$$$"

TDCP_2			TDCPイベント文字列第2カラム(イベント種別)				"SAMPLING

TDCP_3			TDCPイベント文字列第3カラム(app_mode)					"32"

TDCP_4			TDCPイベント文字列第4カラム(イベントデータ#1)			"0F"

TDCP_5			TDCPイベント文字列第5カラム(イベントデータ#2)			"0"
..
..

TDCP_N			TDCPイベント文字列第4カラム(イベントデータ#n)			"123"

( ZB_TDCP_DATA イベントハンドラに渡された全てのパラメータを、このスクリプトを
  コールする時にも指定する)

NodeIdentifier		XBee-ZB デバイスのNodeIdentifier					"Node1"

●リターンパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

●備考

●変更履歴

]]

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

local dev = g_params["NodeIdentifier"]
local param = {}

---------------------------------------------------------------------------------
-- NodeIdentifierが Node1 の名前をもつ XBee-ZBデバイスに接続された TDCPZB リモート
-- CPUボードに、以下のセンサーデバイスが接続されている場合の例です
--   * TMP102 温度センサ	(I2Cバスに接続)
--   * CDS照度センサ		(A/D#0に接続)
--   * IR(人感)センサ		(DIO#3/COUNTER入力接続)
---------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- NodeIdentifier, TDCP イベント種別、TDCP app_modeが一致するものだけを選択して
-- サンプリングデータをデータベースに格納する。
--------------------------------------------------------------------------------
if (dev == "Node1") and (g_params["TDCP_2"] == "SAMPLING") and (g_params["TDCP_3"] == "32") then 

	---------------------------------------------
	-- Xively サービスに登録するためのパラメータ
	---------------------------------------------
	local xively_ch_data = {}

	-----------------------------------------------------------
	-- I2Cバスに接続された TMP102 センサーから温度データを取得
	-----------------------------------------------------------
	param["device"] = dev
	stat,result = script_exec2("ZB/DEVICE/TMP102_READ",param)
	if not stat then error() end

	-----------------------------------------------------------------
	-- 統計データベースに測定値を保存、共有データには最新の値を保存
	-- キー名  "SENSOR_TP_<NodeIdentifier>"
	-----------------------------------------------------------------
	if not add_stat_data("SENSOR_TP_" .. dev,result["temperature"]) then error() end
	if not set_shared_data("SENSOR_TP_" .. dev,result["temperature"]) then error() end
	xively_ch_data["SENSOR_TP_" .. dev] = result["temperature"] -- for Xively

	-----------------------------------------------------------------------------
	-- SAMPLING イベント発生時の A/D#0 (TDCP_7) は光センサーの値が格納されている
	--
	-- 統計データベースに測定値を保存、共有データには最新の値を保存
	-- キー名  "SENSOR_LUMI_<NodeIdentifier>"
	-----------------------------------------------------------------------------
	if not add_stat_data("SENSOR_LUMI_" .. dev,g_params["TDCP_7"]) then error() end
	if not set_shared_data("SENSOR_LUMI_" .. dev,g_params["TDCP_7"]) then error() end
	xively_ch_data["SENSOR_LUMI_" .. dev] = g_params["TDCP_7"] -- for Xively

	-----------------------------------------------------------------------------
	-- SAMPLING イベント発生時の COUNTER (TDCP_6) はIRセンサーのカウンタ値が格納されている
	--
	-- 統計データベースに測定値を保存、共有データには最新の値を保存
	-- キー名  "SENSOR_IR_<NodeIdentifier>"
	-----------------------------------------------------------------------------
	if not add_stat_data("SENSOR_IR_" .. dev,g_params["TDCP_6"]) then error() end
	if not set_shared_data("SENSOR_IR_" .. dev,g_params["TDCP_6"]) then error() end
	xively_ch_data["SENSOR_IR_" .. dev] = g_params["TDCP_6"] -- for Xively

	-----------------------------------------------------------------------------
	-- Xively サービスにセンサーデータを登録
	-----------------------------------------------------------------------------
	if not script_exec("XIVELY_DATA_STORE",xively_ch_data) then error() end

end

処理の内容はほぼセンサーノード1の XBEE_SENSOR_DATA_STORE と同じですが、SAMPLING イベント中に含まれる、IR センサのカウント値と CDS のA/D 変換値データのカラム位置が XBee の場合と少し違っています。詳しくはスクリプト中のコメントをご覧ください。

温度データは別途リモート CPU ボードに接続した I2Cバスを操作する必要があります。これは別に作成したスクリプト TMP102_READ をコールすることで実現します。

local dev = g_params["NodeIdentifier"]

local param = {}

param["device"] = dev

stat,result = script_exec2(“ZB/DEVICE/TMP102_READ”,param)

スクリプトパラメータに XBee-ZB の NodeIdentifier を格納した param 連想配列を指定してコールします。script_exec2() 関数は同じスレッド中で別の lua スクリプトを実行してリターンパラメータを取得するライブラリ関数です。

スクリプト名に “/” で区切ったフォルダ名を指定しています。今回の場合には DeviceServer のデフォルトスクリプトフォルダ “C:\Program Files (x86)\AllBlueSystem\Scripts” (Windows7 64bit の場合) フォルダの中の “C:\Program Files (x86)\AllBlueSystem\Scripts\ZB\DEVICE\TMP102_READ.lua” ファイルを実行します。スクリプトの内容は以下になります。

file_id = "TMP102_READ"

--[[

●機能概要

I2C バスに接続した温度センサー(TMP102) の値を取得する

●リクエストパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

device		XBee-ZB デバイス64ビットアドレスまたは NodeIdentifier	"Node1"

●リターンパラメータ

---------------------------------------------------------------------------------
キー値			値		            									値の例
---------------------------------------------------------------------------------

temperature			センサーから取得した摂氏温度						"12.5"
					                                                    "-25.0"

●備考

●変更履歴

2014/04/23	初版作成

ABS-9000 DeviceServer        copyright(c) All Blue System

]]

local slave_addr = "48"

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

----------------------------------------
-- 12 bit 幅の2の補数を符号付整数に変換
----------------------------------------
function calc_2comp(val)
	if(bit_and(val,0x800) ~= 0) then
		return -1 * (bit_and(bit_not(val),0xfff) + 1)
	else
		return val
	end
end

-----------------------------------------------------------------------
-- TMP102温度レジスタの値を取得する
-- pointer register 0x00 をセットした後、2 バイトのレジスタ値を取得する
-----------------------------------------------------------------------
stat,result = zb_tdcp_safe_retry(g_params["device"],"i2c_write," .. slave_addr .. ",00,2")
if (not stat) or (result[2] == "0") then error() end

---------------------------------------
-- 温度レジスタ値から摂氏温度を計算する
---------------------------------------
local reg = {}
reg = hex_to_tbl(result[3])

local temp_int = bit_lshift(reg[1],4) + bit_rshift(reg[2],4)
local temperature = 0.0625 * calc_2comp(temp_int)
script_result(g_taskid,"temperature",string.format("%3.1f",temperature))

リモートCPU ボードで動作している TDCP モニタプログラムの i2c_write コマンドを実行して、I2C バスを操作しています。

i2c_write,48,00,2

を実行すると、I2C スレーブアドレス 0×48 を持つ TMP102 温度センサに、レジスタアドレス 0×00 を書き込んだ後、2バイト分のデータを続けて読み込みます。この2バイトのセンサデータから摂氏温度を計算して、結果をスクリプトリターンパラメータ ”temperature” に設定してスクリプトを終了します。その後コールを行った ZB_SENSOR_DATA_STORE に制御が戻ります。

ZB_SENSOR_DATA_STORE中の xively_ch_data[] 連想配列変数に、温度値、IR センサのデータ、CDS の値を格納します。xively_ch_data[] 連想配列のキー名にはそれぞれ以下の名前を割り当てています。(”Node1″ はセンサーノード2に接続された XBee-ZB Series2 の NodeIdentifier 文字列です)

SENSOR_TP_Node1       センサーノード2の温度データ

SENSOR_IR_Node1     センサーノード2の人感センサ(IR) データ

SENSOR_LUMI_Node1 センサーノード2の CDSセンサデータ

このxively_ch_data[] 連想配列変数をスクリプトパラメータに指定して、センサーノード1の時と同じXIVELY_DATA_STORE スクリプトをコールして、Xively サービスにデータを登録します。

これで、センサーノード2から定期的に送信されるデータもクラウド側に保存されるようになりました。

センサーノード1とセンサーノード2からセンサーデータが送信されているときのログは下記のようになります。

定期的に SAMPLING イベントデータが送信されている様子がわかります。センサーノード2からイベントを受信したときには同時に、リモート側の I2Cバスを操作している様子も記録されています。Xively サービスには HTTP PUT コマンドで JSON 文字列を送信しています。このJSON 文字列の内容もログに出力していますので Xively を利用するときの参考にしてください。

一日分のデータを参照するために、Xively のアカウントにログインしてセンサーデータをグラフ表示してみました。

クラウドサービスを使用すると、すぐにセンサーデータの傾向がつかめるので、センサーネットワークを構築する開発段階に非常に強力なツールとなると思います。また、複数の拠点にサーバーPCに分けて設置した場合でも、全体のセンサーデータをまとめてクラウドサービスで表示できますので、分散センサーネットワークのデータ参照システムを簡単に作成できると思います。

それではまた。