TWELITEとArduinoを使った測定データの収集について8(Arduino用ソースコード)
前回のハードウェア構成に対するArduinoのスケッチについて考える.
このブログ上ではID=0d42のエンドデバイスのスケッチのみ示し,その他のスケッチはGitHubに上げることにする(こちら). スケッチ中に説明が入っているので,やっていることはすぐわかると思う. 中継デバイスでもほとんど同じコードが利用できるようになっている. なお,『■■』の箇所はデバイスごとに見直しが必要な箇所である.
enddevice_0d42.ino
メインとなるスケッチ.関数acquire内のコードを書き換えることで,様々な測定対象に対応できる.
#include <SoftwareSerial.h> #include "peripheral.h" #include "handler.h" static const unsigned long MeasInterval = 3000; // ■■測定間隔 SoftwareSerial mySerial(RX_PIN, TX_PIN); static unsigned long previousMillis = 0; static char rxMessage[RX_HEADER_LEN + MAX_CONTENT_LEN + 3] = ""; static int rxMsgLength; void acquire(int *measValue); void transmit(const int *measValue); bool parseMessage(MessageHandler *pHandler, const char *rxMessage); RxMsgHandler ignoreHandler = {{handleIgnore}, 0, 0}; RxMsgHandler normalMsgHandler = {{handleNormalMsg}, NORMAL_ID, MAX_CONTENT_LEN}; ChainedHandler normalMsgChain = {{isHandled}, &normalMsgHandler.base, NULL}; ChainedHandler ignoreChain = {{isHandled}, &ignoreHandler.base, &normalMsgChain.base}; void setup() { pinMode(LED_PIN, OUTPUT); Serial.begin(9600); while (!Serial) yield(); mySerial.begin(9600); } void loop() { // 一定時間おきにデータを測定して,送信するための処理 unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= MeasInterval) { int measValue[NUM_OF_MEAS_ITEMS]; acquire(measValue); transmit(measValue); previousMillis = currentMillis; } //データを受信した場合の処理 while (mySerial.available()) { char c = mySerial.read(); // 受信した文字が改行コードLFの場合には受信した文字列を解析する // LFとCR以外の場合には受信文字をrxMessageに蓄積する if (c == 10) { rxMessage[rxMsgLength] = '\0'; Serial.print("RX: "); Serial.println(rxMessage); if (!parseMessage(&ignoreChain.base, rxMessage)) Serial.println("Handler: None"); rxMessage[0] = '\0'; rxMsgLength = 0; } else if (c != 13) { rxMessage[rxMsgLength] = c; rxMsgLength++; } } } // ■■測定値を取得する.戻り値は整数とする void acquire(int *measValue) { for (int i = 0; i < NUM_OF_MEAS_ITEMS; i++) measValue[i] = random(0, 10); } // 測定値を文字列としてTWELITEへ送信する void transmit(const int *measValue) { char txMessage[TX_HEADER_LEN + MAX_CONTENT_LEN + 3] = ""; // 送信文字列のヘッダーの作成 sprintf(txMessage, ":%02xA0%02xFF%02x", DOWNSTREAM_TWELITE, NORMAL_ID, THIS_TWELITE); // 送信文字列への測定値の追加 for (int i = 0; i < NUM_OF_MEAS_ITEMS; i++) sprintf(txMessage + TX_HEADER_LEN + 2 + i * MEAS_VALUE_LEN, "%04x", measValue[i]); // 送信文字列へのChecksumの追加 sprintf(txMessage + TX_HEADER_LEN + 2 + NUM_OF_MEAS_ITEMS * MEAS_VALUE_LEN, "%02x", getChecksumOf(txMessage)); // 送信実行 toUpperStr(txMessage); writeToSerial(txMessage); } // 受信メッセージに応じて,チェインオブレスポンシビリティパターンによる適切な処理を行う // 処理ができた場合にはtrueを,処理ができなかった場合にはfalseを返す bool parseMessage(MessageHandler *pHandler, const char *rxMessage) { RxContent rxContent; // 各種データをrxContentに格納する rxContent.message = rxMessage; sscanf(rxMessage + 1, "%2x", &rxContent.tweliteId); sscanf(rxMessage + 5, "%2x", &rxContent.responseId); rxContent.contentLength = strlen(rxMessage) - RX_HEADER_LEN - 2; // 受信メッセージの内容に応じて,適切に処理する return pHandler->handleMessage(pHandler, &rxContent); }
peripheral.h
各種設定値を入力するためのヘッダファイル.
#ifndef T_SENSOR_UNIT #define T_SENSOR_UNIT #include "util.h" extern SoftwareSerial mySerial; // 応答IDの設定 typedef enum { NORMAL_ID = 80, // 0x50 } ResponseId; // メッセージの長さの設定 typedef enum { TX_HEADER_LEN = 9, // 送信メッセージのヘッダー長 RX_HEADER_LEN = 29, // 受信メッセージのヘッダー長 MEAS_VALUE_LEN = 4, // 測定値の文字列長 NUM_OF_MEAS_ITEMS = 2, // ■■測定値の個数 MAX_CONTENT_LEN = 22, // NUM_OF_MEAS_ITEMS*MEAS_VALUE_LEN+2 } MessageLength; // LEDのピンの設定 typedef enum { SENSOR_PIN = 0, RX_PIN = 6, TX_PIN = 7, LED_PIN = 2, } PinSetting; // TWELITE論理IDの設定 typedef enum { THIS_TWELITE = 42, // ■■このTWELITEの論理ID(10進数) DOWNSTREAM_TWELITE = 45, // ■■下流側TWELITEの論理ID(10進数) IGNORE_TWELITE = 219, // 0xDB } TweliteId; // 受信メッセージを格納する構造体 typedef struct { const char *message; // 受信した文字列 unsigned int tweliteId; // 送信元のTWELITEの論理ID unsigned int responseId; // 受信メッセージのヘッダーに含まれている応答ID int contentLength; // HEADERとCHECKSUMを除いた文字列長 } RxContent; #endif
handler.h
チェインオブレスポンシビリティパターンで使用する構造体等を宣言するためのヘッダファイル.
#ifndef HANDLER_H #define HANDLER_H // 引数にMessageHandler型のポインタとRxContent型のポインタをもち, // bool型の変数を返す関数へのポインタを保持する構造体 typedef struct _MessageHandler { bool (* const handleMessage)(struct _MessageHandler *pThis, RxContent *pContent); } MessageHandler; // MessageHandlerとそれ以外に必要なデータを格納する構造体 // 擬似的にMessageHandlerを継承した構造体となっている typedef struct { MessageHandler base; const unsigned int responseId; const int contentLength; } RxMsgHandler; // 処理の連鎖を格納するための構造体 // 擬似的にMessageHandlerを継承した構造体となっている typedef struct { MessageHandler base; MessageHandler *pWrapped; MessageHandler *pNext; } ChainedHandler; // 受信メッセージの処理を試みて,処理ができた場合にはtrueを返す // 処理がができなかった場合には、後続のHandlerを呼び出し, // 後続のHandlerの戻り値をそのまま呼び出し元に返す // 後続のHandlerがいない場合にはfalseを返す bool isHandled(MessageHandler *pThis, RxContent *pContent); // 不要な受信文字列を無視するHandler bool handleIgnore(MessageHandler *pThis, RxContent *pContent); // 通常の受信メッセージを下流側のデバイスに送信するHandler bool handleNormalMsg(MessageHandler *pThis, RxContent *pContent); #endif
handler.ino
チェインオブレスポンシビリティパターンで使用する関数を定義するためのスケッチ.
#include "handler.h" #include "peripheral.h" bool isHandled(MessageHandler *pHandler, RxContent *pContent) { ChainedHandler *pThis = (ChainedHandler *)pHandler; pHandler = pThis->pWrapped; if (pHandler->handleMessage(pHandler, pContent)) return true; pHandler = pThis->pNext; if (pHandler == NULL) return false; return pHandler->handleMessage(pHandler, pContent); } bool handleIgnore(MessageHandler *pHandler, RxContent *pContent) { // 下記の条件の場合には即退出 if (pContent->tweliteId != IGNORE_TWELITE && pContent->contentLength <= MAX_CONTENT_LEN) return false; // 何もせず終了 Serial.println("Handler: Ignore"); return true; } bool handleNormalMsg(MessageHandler *pHandler, RxContent *pContent) { RxMsgHandler *pThis = (RxMsgHandler *)pHandler; // 下記の条件の場合には即退出 if (pContent->responseId != pThis->responseId) return false; if ((pContent->contentLength % MEAS_VALUE_LEN) != 2) return false; char txMessage[TX_HEADER_LEN + MAX_CONTENT_LEN + 3]; sprintf(txMessage, ":%02xA0%02xFF", DOWNSTREAM_TWELITE, pThis->responseId); for (int i = 0; i < pContent->contentLength; i++) txMessage[i + TX_HEADER_LEN] = rxMessage[i + RX_HEADER_LEN]; txMessage[TX_HEADER_LEN + pContent->contentLength] = '\0'; sprintf(txMessage + (TX_HEADER_LEN + pContent->contentLength), "%02x", getChecksumOf(txMessage)); toUpperStr(txMessage); writeToSerial(txMessage); Serial.println("Handler: NormalMessage"); return true; }
util.h
その他の関数を定義するためのヘッダファイル.
#ifndef UTIL_H #define UTIL_H #include "peripheral.h" // チェックサムを計算するための関数 int getChecksumOf(const char *); // 大文字に変換するための関数 void toUpperStr(const char *); // 標準のシリアルポートとSoftwareSerialで擬似的に作成された // シリアルポートの両方に書き込みを行うための関数 void writeToSerial(const char *); #endif
util.ino
その他の関数を定義するためのスケッチ.
#include "util.h" int getChecksumOf(const char *message) { char str[3]; int checksum = 0; byte msgLength = strlen(message); for (int i = 1; i < msgLength; i = i + 2) { str[0] = message[i]; str[1] = message[i + 1]; str[2] = '\0'; checksum += strtol(str, NULL, 16); } return 256 - (checksum % 256); } void toUpperStr(const char *message) { char *p = (char *)message; while (*p) { *p = toupper(*p); p++; } } void writeToSerial(const char *txMessage) { digitalWrite(LED_PIN, HIGH); Serial.print("TX: "); Serial.println(txMessage); mySerial.println(txMessage); digitalWrite(LED_PIN, LOW); }