XBee デバイスや XBee-ZB デバイスを利用したリモートシステムを構築するときに、リモート側の機器が通信可能かどうかを監視する必要があると思います。このときに利用できるワッチドッグ方式の実現方法について説明します。
ここでは、リモート機器として XBee-ZB デバイスとマイコンを組み合わせた下記のような簡単なセンサーノードを利用していることを想定しています。XBee-ZB や XBee 単体だけで運用している場合でも、簡単な変更でこの記事で紹介するワッチドッグ方式の監視を実現できます。
このリモートノードは XBee-ZB (series2) とマイコン(ATmega328P)を組み合わせた CPUボードで、マイコンには簡単なモニタプログラムが組み込んであります。このリモート側CPU ボードの詳しい内容については、こちらの記事でも紹介していますのでご覧ください。
リモート機器側で動作しているモニタプログラム(TDCPZB for ATmega328) には定期的に XBee の RF データパケットを送信する “LIVE” イベント送信機能があり、この機能を利用して 20分に一回 Coordinator XBee-ZB が接続されているサーバーPC にデータを送信するように設定します。サーバーPC 側では定期的に “LIVE” イベントの受信状態を監視して、もしリモート側からのイベントが到着していない場合には、何らかの通信障害が発生しているとして警報メールを送信します。
今回の記事で紹介するリモート側の XBee は、XBee 802.15.4 Series1 デバイス が2つで、NodeIdentifier はそれぞれ “Device2″, “Device4″ になっています。また XBee-ZB Series2 デバイス2つも同時に監視していて、それぞれの NodeIdentifier は “Node1″, “Node3″ になっています。全てのリモート側の機器には XBee にはマイコンが接続されていて、上記のモニタプログラムから定期的にイベントを送信するように設定します。
XBee デバイス一覧です。Device2, Device4 がリモート側に設置されていて Device3 はサーバーPC に接続されています。
XBee-ZB デバイス一覧です。Node1, Node3 がリモート側に設置されていて Node2 はサーバーPC に接続されています。
モニタプログラムにはリモートから “heartbeat_rate” コマンドで送信間隔を設定します。今回は20分(1200秒)ごとに送信するようにします。
このコマンドの後に “config_save” コマンドを実行して、リモート機器側マイコンに搭載された不揮発メモリ(EEPROM) に設定値を保存します。このコマンドを全てのリモート機器に対して実行します。上記は XBee-ZB デバイスを管理しているプログラムの実行例です。XBee Series1に対しては “XBee” ツールボタンで表示される “XBee デバイス管理” プログラムから同様の設定を行います。
この設定によってサーバーPCはリモート機器からは 20分に一回、以下の文字列が含まれた RF データパケットを受信します。以下はこのときのログメッセージです。
(XBee Series1 の場合)
$$$,LIVE,0D04,8
(XBee Series2 の場合)
$$$,LIVE,32
それぞれの第2カラムには “heartbeat_rate” コマンドで設定した LIVE イベントを示す “LIVE”という文字列が格納されています。この RF データパケットをサーバー側で受信すると、XBee Series1 の場合には Lua で記述された以下のイベントハンドラスクリプト XBEE_TDCP_DATA が実行されます。
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から順番に インクリメントされた値が入る。 ]] ------------------ -- BEGIN SCRIPT -- ------------------ log_msg(g_params["NodeIdentifier"] .. "[" .. g_params["SourceAddress"] .. "," .. g_params["SerialNumber"] .. "] TDCPData = " .. g_params["TDCP_WHOLE"],file_id) ------------------------------------------------------------------- -- LIVE イベントを使用してデバイスワッチドッグを行う ------------------------------------------------------------------- if g_params["TDCP_2"] == "LIVE" then if not set_shared_data("WATCHDOG_" .. g_params["NodeIdentifier"],"") then error() end end ------------------ -- END SCRIPT -- ------------------
イベントハンドラスクリプト中ではイベント送信元の XBee デバイス NodeIdentifier 文字列に “WATCHDOG_” を付けた名前の共有変数をクリアしています。たとえばXBee NodeIdentifier が “Device4″ から送信されていた場合には “WATCHDOG_Device4″ という名前の共有変数をクリア(削除) します。この共有変数はワッチドッグカウンタになっていてサーバー側で定期的にインクリメントされています(後述)。
XBee-ZB Series2 の場合にも同様に Lua で記述された以下のイベントハンドラスクリプト 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から順番に インクリメントされた値が入る。 ]] ------------------ -- BEGIN SCRIPT -- ------------------ log_msg(g_params["NodeIdentifier"] .. "[" .. g_params["NetworkAddress"] .. "," .. g_params["SourceAddress"] .. "," .. g_params["DeviceType"] .. "," .. g_params["DeviceTypeID"] .. "] TDCPData = " .. g_params["TDCP_WHOLE"],file_id) ------------------------------------------------------------------- -- LIVE イベントを使用してデバイスワッチドッグを行う ------------------------------------------------------------------- if g_params["TDCP_2"] == "LIVE" then if not set_shared_data("WATCHDOG_" .. g_params["NodeIdentifier"],"") then error() end end ------------------ -- END SCRIPT -- ------------------
動作内容は XBee Series1 とまったく同じで、ワッチドッグカウンタをクリアするだけです。
次にサーバー側で定期的(1分に一回)に実行される PERIODIC_TIMER スクリプトに以下の様な記述を追加します。
file_id = "PERIODIC_TIMER" --[[ ****************************************************************************** * イベントハンドラスクリプト実行時間について * ****************************************************************************** 一つのスクリプトの実行は長くても数秒以内で必ず終了するようにしてください。 処理に時間がかかると、イベント処理の終了を待つサーバー側でタイムアウトが発生します。 また、同時実行可能なスクリプトの数に制限があるため、他のスクリプトの実行開始が 待たされる原因にもなります。 頻繁には発生しないイベントで、処理時間がかかるスクリプトを実行したい場合は スクリプトを別に作成して、このイベントハンドラ中から script_fork_exec() を使用して 別スレッドで実行することを検討してください。 ****************************************************************************** ]] ------------------ -- BEGIN SCRIPT -- ------------------ ----------------------------------------------------------------------------- -- TDCP,TDCPZBモニタを搭載したCPUボードのワッチドッグ監視をする ----------------------------------------------------------------------------- local error_msg = "" local dev_list = { "Device2", "Device4", "Node1", "Node3"} for k,v in ipairs(dev_list) do ------------------------------------------------------------- -- デバイス毎の WATCHDOG 監視用カウンタをインクリメントする -- このカウンタは LIVE イベント受信時にクリアされる。 ------------------------------------------------------------- local flag = "WATCHDOG_" .. v stat,val = inc_shared_data(flag) if not stat then error() end ------------------------------------------------------------- -- 規定以上カウンタ値が増加した場合にはエラーとする ------------------------------------------------------------- if tonumber(val) >= 25 then error_msg = error_msg .. " " .. v if not set_shared_data(flag,"") then error() end end end -------------------------------------------------------------------------- -- WATCHDOG エラーが発生した場合には警報メッセージを電子メールで送信する -------------------------------------------------------------------------- if error_msg ~= "" then local mail_addr = "監視警報メール宛先 <your_mail@your_mail_server.jp>"; local body = {}; table.insert(body,"*************************************") table.insert(body,"** WATCHDOG ERROR, device =" .. error_msg) table.insert(body,"*************************************") for key,val in ipairs(body) do log_msg(val,file_id) -- ログにメッセージを出力 end if not mail_send(mail_addr,"","** WATCHDOG アラームメール **",unpack(body)) then error() end end
PERIODIC_TIMER スクリプトは DeviceServer では1分に一回実行されています。この中で 監視対象の XBee デバイスの NodeIdentifier に “WATCHDOG_” を先頭につけた共有変数をカウンタに見立てて、リモート側からのイベント到着を監視しています。XBee デバイス毎に作成したカウンタは1分ごとにインクリメントされて、25(25分)を超えた場合にはリモート側で異常が発生したものとして、警報メールを送信するようにしています。
警報メールを受信したときの様子は以下になります。
リモート側が正常に動作している場合には、先に設定した LIVE イベントの受信時に実行されるスクリプトでカウンタがクリアされるのでカウンタ値は 20 以上には増加しません。
ワッチドッグ方式はこのように簡単に実現可能で、大量のリモートデバイスを監視する必要があるときに大変便利です。また、リモート側の機器故障やバッテリ消耗、電波状況の一時的な悪化などあらゆる障害を簡単に監視することができます。リモート側の監視デバイス数が少ない場合にはポーリング方式を採用して、定期的にサーバー側から全てのリモート側機器にリモートコマンドを実行して正常終了するかどうかを調べる方法でも有効だと思います。
今回は XBee とマイコンを組み合わせましたが、XBee, XBee-ZB 単体でも簡単に同様の機能を実現できます。XBee, XBee-ZB には IO Sampling イベントを送信する “IR” AT コマンドがありますので、これを利用して定期的に IO Sampling データをサーバー側で受信するように設定します。このとき、同時にXBee デバイス自身の DIO, A/D を最低限1つを有効にするのを忘れないようにします。また “DH”, “DL” ATコマンドで IO Sampling イベント送信先をサーバーPC に設定しておくと、サーバー側では XBEE_IO_DATA (Series1の場合)または ZB_IO_DATA(Series2の場合) イベントハンドラが実行されます。このイベントハンドラ中に、先に説明した WATCHDOG カウンタをクリアする記述を入れておくだけで実現できます。
XBee, XBee-ZB 単体の IO Samplingの間隔は 長くても1分毎なので、サーバー側の PERIODIC_TIMER イベントハンドラ中の監視カウンタ値の上限を 25 からもっと少ない数に設定するようにします。
それではまた。