今回の実験は、Moteに内蔵されている非力なマイコンを使って、振動計測ソリューションのFFTデータをSmartMeshで送信したIoTエッジコンピューティングを実践してみました。
振動計測ソリューションの詳細はこちら SmartMeshのSDK詳細はこちら
振動計測ソリューションの制御方法
振動計測ソリューションは、ADuCM4050をベースにしたしシステムで、高精度加速度センサーADXL1002による加速度データを4096ポイントのFFTを実行し、その結果をUARTにデータを出力するIoTエッジコンピューティングプラットフォームです(図1)。
標準で添付されるアプリは、Python3系のシステムで実行可能なスクリプトが用意されており、ADCデータおよびFFTデータの取得および、波形表示が行えます(図2)。
このシステムを利用するには、PySerialとnumpyを事前にインストールする必要があります。今回の説明では、割愛します。
UARTのデータフォーマット
振動計測システムは、2つのUARTのデータフォーマットがあります。一つは、システムにコマンド設定するコマンドフォーマット。もう一つは、システムから送られてくるデータフォーマットです。
FFT解析を行うには、システムにコマンド設定する必要があり、今回の設定では、以下のコマンドを指定しています。
:0 CPS 0000 :0 RMC 0000 :0 CPS 0000 :0 RRP 005s :0 CPS 0000 :0 BSZ 0000 :0 CPS 0000 :0 DFA 0001 :0 CPS 0000 :0 AL1 0002 :0 CPS 0000 :0 AH1 0080 :0 CPS 0000 :0 AL2 0080 :0 CPS 0000 :0 AH2 0800 :0 CPS 0000 :0 AC8 0003 :0 CPS 0000 :0 AW8 0008 :0 CPS 0000 :0 VAX 0001 :0 CPS 0000 :0 CRS 0000
CPSコマンドは、コマンド設定モードです。CPSに続くコマンドを設定しますが、連続に指定することはできないので、コマンド設定するたびに、CPSコマンドを指定する必要があります。
詳しくは、振動計測ソリューションのサイトからダウンロードしてください。
FFTデータの受信
コマンドを指定すると、FFTのデータが送られてきますが、データフォーマットは、3種類あります。
- XDSフォーマット
- XFDフォーマット
- ANIフォーマット
FFTの実行が開始されると、XDSフォーマットに続いて、XFDフォーマットがUARTから出力されます。ANIフォーマットは、事前に閾値を超えた場合に出力される仕様になっています(図3)。
XFDフォーマットは、ヘッダ部分に、経過時刻、CH情報、アラーム情報、バンドごとの周波数、バンドごとの周波数レベルが入っており、続けてFFTデータが4096データ分が送られてきます(図4)。
SmartMeshでは、IoTエッジコンピューティングらしさを出すために、FFTのデータを丸ごと送出するのではなく、XFDのヘッダ部分を使用することにしました。
OCSDKで、Moteのアプリを構築
振動計測ソリューションから出力されるFFT解析データは、UART経由で送られてきますので、Mote側もUARTで受け取る必要があります。Mote側のUARTは、OCSDKのサンプルであるUART(RAW)をベースに構築します。
振動計測ソリューションとMoteは、図5のようなデータ・フローを構築する必要があります。
OCSDKをビルドするための準備
今回の実験には、OCSDKのサンプルをベースにしますが、プロジェクトをビルドする必要があります。前回のBlinkモードでも行ったように、ソフトウェアの開発環境として、IARシステムズ社のEmbedded Workbench for Armが必要です。
IAR Systems Embedded Workbench for Arm
UART(HDLC)とUART(RAW)の違い
OCSDKのUARTは、UART(HDLC)とUART(RAW)があります。UART(HDLC)は、外部のマイコンから、SmartMeshのネットワーク参加およびデータ転送を行う仕組みを提供していますが、HDLCフォーマットを前提にしています。なので、MoteのUART API仕様に基づいたフォーマットで出力する必要があるのですが、振動計測ソリューションはHDLCのフォーマットでは出力できないので、UART(RAW)を使用します。
UART(RAW)による振動計測システムとのハンドリング
UART(RAW)の場合、HDLCのエンコードおよびデコードの必要はありません。OCSDKのサンプルは、UARTの送信タスクとUARTの受信タスクのみで構成されています。そのまま使うには不足する要素が多いので、アプリケーションタスクを追加し、コマンド処理およびデータ抽出処理を行うように構築しました(図6)。
タスク間は、セマフォで同期を取る方法を実装しています。SmartMeshのMoteは、それ以外にも内部でいくつものタスクが実行されています。SMartMeshのラジオ制御であったり、CLIの制御であったり、10タスクほど動いています。そのため、必要とするタスクのスタックメモリもやたら大きく取ればいいわけではないことも容易に想像できます。
今回、使用したスタックサイズは、
- アプリケーションタスク 2048 Bytes
- UART送信タスク 512 Bytes
- UART受信タスク 512 Bytes
としています。
UARTの最大受信可能サイズ
SmartMeshのMote側は、1回の転送可能なサイズに制限があります。もちろん、どのマイコンも転送できるサイズは制限されているのですが、SmartMeshのMoteは、120Bytesに制限されています。UARTのOCSDKサンプルも、120Bytesに制限されています。120Bytesを超えるデータを送受信するには、アプリ側で120Bytes毎に分割してデータ転送すればいいでしょう。
こういった技術的背景があるので、振動計測ソリューションのデータも120Bytes毎に分割したデータ転送版も用意してあります。
FFTヘッダデータの抽出
FFTのデータ抽出は、UARTから送られてくるペイロードのヘッダをキーにして、目的のデータのみを受信するようにしています。XFDのデータは、先頭に:0 XFD
が入っているので、この文字列をキーにします。受信タスクからデータを受信した際に、アプリケーションタスクのループ内でキー判定をしています。
while(f_Data) { OSSemPend(uart_app_v.uartAppSem, 0, &osErr); if(strncmp((char *)&uart_app_v.uartRxBuf[0], Vib_xfd, sizeof(Vib_xfd))==0) { dnm_ucli_printf("XFD Frame Detect\r\n" ); : :
OSSemPend()は、セマフォを待っています。UART受信タスクが受信を完了すると、このセマフォを解除します。
次のstrncmp()で先頭6バイト分のヘッダを識別しています。
これで、振動計測ソリューションから送られてくるFFTのヘッダ情報の抽出ができるようになります。
SmartMeshへデータ送信
次に、SmartMesh側の実装です。OCSDKには、SmartMeshに参加するためのいくつかのサンプルがあるので、それに習った実装をしています。
ヘッダ指定
SmartMesh側にデータを送信する場合に、ヘッダ情報を指定する必要があります。ここでは、他のサンプルでも指定している方法を用いています。
// prepare packet to send pkToSend = (loc_sendtoNW_t*)uart_app_v.pkBuf; pkToSend->locSendTo.socketId = loc_getSocketId(); pkToSend->locSendTo.destAddr = DN_MGR_IPV6_MULTICAST_ADDR; pkToSend->locSendTo.destPort = WKP_USER_1; pkToSend->locSendTo.serviceType = DN_API_SERVICE_TYPE_BW; pkToSend->locSendTo.priority = DN_API_PRIORITY_MED; pkToSend->locSendTo.packetId = 0xFFFF;
ヘッダ指定
FFTのヘッダ情報をSmartMeshのペイロードデータに指定します。
for (i=0;i<= XFD_HEADER_SIZE;i++) { pkToSend->locSendTo.payload[i] = uart_app_v.uartRxBuf[i+12]; }
XFD_HEADER_SIZEは36です。UARTから受信したデータポジションは、12からなので、このような記述にしています。
パケット送信と送信確認
SmartMesh側にデータ転送する時には、下記のように実装します。
dnErr = dnm_loc_sendtoCmd(pkToSend, 36, &rc); ASSERT(dnErr==DN_ERR_NONE); // print if (rc==DN_API_RC_OK) { dnm_ucli_printf("packet sent\r\n"); } else { dnm_ucli_printf("rc = 0x%02x\r\n",rc); }
rcには、成功したかどうかの情報が入ります。DN_API_RC_OK
であれば、送信できたことになります。
ここまでが、振動計測ソリューションとOCSDKを利用したMote側の説明になります。
ManagerのPythonアプリ
これまでV-Managerをベースに実験してきましたが、E-Managerも多く利用されている現状を踏まえ、E-Managerをベースに振動計測ソリューションのFFT抽出データを表示するアプリを構築してみました。
SmartMesh SDKは、Python2.7系
SmartMesh SDKはPython2.7ベースに作られています。GUIベースのものから、コマンドラインベースのものまで、様々なバリエーションが用意されています。
CLIで振動計測データを表示する
SmartMesh SDKのサンプルにある「Simple」フォルダにある、「SimpleIPUpstreamMgr.py」をベースに、Moteから送られてくるデータを表示するアプリを構築しました。
ペイロードデータ
Managerが受信したデータは、def handle_data(notifName, notifParams):
関数で確認することができます。この関数で渡されるnotifParamsに、SmartMeshのヘッダ情報および、ペイロードデータが保持されています。そこから必要なデータを抽出します。
Mote情報
Moteの情報は、受信したデータヘッダから抽出します。
print ('Received from mote {0}: '.format('-'.join(['%02x'%b for b in notifParams.macAddress])))
受信したMoteのアドレスが表示されます。
経過時間情報
経過時間情報は、ペイロードデータの先頭4バイト分に入っています。
Timestamp_raw = ((notifParams.data[3] * 16777216) + (notifParams.data[2] * 65536) + (notifParams.data[1] * 256) + notifParams.data[0]) TS_ms = Timestamp_raw % 1000 TS_ss = Timestamp_raw / 1000 TS_s = TS_ss % 60 TS_m = TS_ss / 60 TS_h = TS_ss / 3600
print ( 'Timestamp : {0:d}H : {1:d}m : {2:d}s : {3:d}ms '.format(TS_h, TS_m, TS_s, TS_ms))
で表示しています。
周波数Indexの抽出
周波数Indexは、12ビット表記で送られてきます。そのため、Indexを復号するために、ビット操作による復号をする必要があります。
Index1とIndex2の場合の実装例です。
f_ch0 = ((notifParams.data[9] & 0x0F) << 8) + notifParams.data[8] f_ch1 = ((notifParams.data[10] << 4) + ((notifParams.data[9] & 0xF0) >> 4))
Index1およびIndex2に該当する周波数情報が復元できます。この値から、サンプリング周波数をかけて、FFTのポイント数で割った値が、目的の周波数になります。
print ( 'Peak Index1 Frequency : {0:.3f}KHz'.format((f_ch0 * 102.4)/4096)) print ( 'Peak Index2 Frequency : {0:.3f}KHz'.format((f_ch1 * 102.4)/4096))
これで、FFT解析結果の周波数が表示されます。
PeakLevelの抽出
周波数Indexに該当するPeakLevelの復号方法です。PeakLevelは、下記の式にあるIEEE754 半精度浮動小数点を用いてFFTフォーマットのデコードを実装しています。
v_ch0 = (1.0 - 2 * ((notifParams.data[21] & 0x80) >> 7)) * (2 ** ((notifParams.data[21] & 0x7C) >> 2)) / (2**15) * (1024 + (notifParams.data[21] & 0x03) * 256 + notifParams.data[20]) / 1024.0 v_ch1 = (1.0 - 2 * ((notifParams.data[23] & 0x80) >> 7)) * (2 ** ((notifParams.data[23] & 0x7C) >> 2)) / (2**15) * (1024 + (notifParams.data[23] & 0x03) * 256 + notifParams.data[22]) / 1024.0
この結果から、スケールレベルに調整するために、対数を取り、最大スーケルファクタ(ここでは34)を引きます。
print ( 'Peak Level Index1: {0:.4f}dB'.format(20 * np.log10(v_ch0) - 34.0)) print ( 'Peak Level Index2: {0:.4f}dB'.format(20 * np.log10(v_ch1) - 34.0))
これで、Indexに相当する周波数のPeakLevelが表示されるようになります。
Python2.7系の注意点
最近のPythonは3系が主流になっています。2.7系との違いも多くあります。特に数値演算では、小数点を入れていなければ整数扱いされてしまいます。
FFTのデータを受信して、表示
図7は、Moteから受信したデータをコマンドラインに表示している様子です。この例では信号発生機から、650Hzと2.6KHzの振動を与え、バンド1で650Hz、バンド2で2.6KHzを検出している様子がわかります。
まとめ
今回は、SmartMeshで実現するIoTエッジコンピューティングを実践してみました。振動計測ソリューションもSmartMeshも省電力で実現できるシステムです。センサーネットワークといえば、小さいデータがメインになりますが、今回のようなエッジサイドでFFTまで計算することで、ホスト側の負荷低減もさることながら、メッシュネットワークのメリットである多ノード対応も容易に実現することができます。
最新情報をメーカーサイトで見る
こちらも是非
“もっと見る” 実験室
組み込みアプリを止めるな!printfやブレークポイントも使わず内部状態をチェック!
産業機器、オーディオ製品、通信デバイスをはじめとする多くの組み込みアプリケーションは、わずかなタイミングの違いで動きが変化します。そのため、開発・検証のためにprintfなどを仕掛けることが難しく、開発効率が伸び悩むケースも増えています。そこで今回の実験室では、システムを止める事なく内部状態を取得できるデータ計測ツール「EVRICA」を紹介いたします。組み込みアプリケーションの開発や検証に欠かせない、新しい開発ツールです。
非絶縁型中間バスコンバータ750Wで、48Vと12Vを自由自在
VicorのNBMシリーズの最新デバイスであるNBM2317シリーズは、23×17×7mmという超小型パッケージで最大750W出力、48V→12Vまたは、12V→48Vの双方向において98%のピーク効率を実現した非絶縁型中間バスコンバータ。今回の実験室は、実際に動かしつつ、その性能とメリットを解説している。その様子は、是非動画で見て欲しい。
センサーイベント発生!そんな時だけネットワークへ参加できるBLINKモード
SmartMeshには、ある特定のイベントが発生した場合だけネットワークに参加するという機能があります。それが、「Blinkモード」です。今回は、通常のMoteとBlinkモードに設定されたMoteの違いを試してみます。