パケットパーサの拡張
概要
パーサを追加するためには、次の4点のファイルを編集する必要があります。
- (新規)パーサのヘッダファイル
parser/FooBarPacketParser.h- 解釈するパケットの中身と、正規パケットの判定条件を書く
- (新規)パーサのソースファイル
parser/FooBarPacketParser.cpp- パケットの中身を解釈する部分を書く
- (追記)本体のヘッダファイル
MWings.h- パーサを追加する
- (追記)本体のソースファイル
MWings.cpp- パーサを追加する
ソースファイルは GitHub から入手してください。
通常、Arduino のホームディレクトリ内にある libraries/ へ配置します。
GitHubにて、実際のコミット差分を閲覧できます。
Added App_Tag (ADC) packet parser · monowireless/mwings_arduino@aa5ecdb
パーサのヘッダファイル
parser/ 以下にある既存のファイルを複製したうえで名称変更してください。
ここでは、parser/AppTagAdcPacketParser.h を作成します。
#ifndef APPTAGADCPACKETPARSER_H
#define APPTAGADCPACKETPARSER_H
#include "MWings_Common.h"
/**
* @struct ParsedAppTagAdcPacket
* @brief Packet content for App_Tag (ADC)
*/
struct ParsedAppTagAdcPacket final : public mwings::ParsedPacketBase {
uint32_t u32RouterSerialId;
uint8_t u8SensorType;
uint16_t u16AiVoltage[2];
};
/**
* @class apptagadc::PacketParser
* @brief Packet parser for App_Tag (ADC)
*/
namespace apptagadc {
class PacketParser final : public mwings::PacketParserBase {
public:
// Check if the packet is from App_Tag (ADC)
inline bool isValid(const BarePacket& barePacket) const override {
if ((barePacket.u8At(12) == 0x10)
and (barePacket.u32At(18) == 0)
and (barePacket.u16PayloadSize == 22)) {
return true;
}
return false;
}
// Parse from bare packet
bool parse(const BarePacket& barePacket, mwings::ParsedPacketBase* const parsedPacket) const override;
};
}
extern apptagadc::PacketParser AppTagAdcPacketParser;
#endif // APPTAGADCPACKETPARSER_H
解釈するパケットの中身の記述
はじめに、App_Tag(アナログセンサ)の出力書式を確認してください。
出力データの例
:80000000B700628201015A0010DF08FD09A300000000E9
| # | データ | 内容 | 値 | |
|---|---|---|---|---|
: | char | ヘッダ | : | |
80000000 | 0 | uint32 | 中継機のシリアルID | 中継なし |
B7 | 4 | uint8 | LQI | 183/255 |
0062 | 5 | uint16 | 続き番号 | 98 |
8201015A | 7 | uint32 | 送信元のシリアルID | 0x201015A |
00 | 11 | uint8 | 送信元の論理デバイスID | 0x00 |
10 | 12 | uint8 | センサー種別 | アナログセンサー |
DF | 13 | uint8 | 電源電圧(mV) | 3330mV |
08FD | 14 | uint16 | ADC1の電圧 | 2301mV |
09A3 | 16 | uint16 | ADC2の電圧 | 2467mV |
00000000 | 18 | uint32 | 未使用 | |
E9 | 22 | uint8 | チェックサム | 0xE9 |
char | フッタ | \r | ||
char | フッタ | \n |
インクルードガードやnamespace、コメント文を置換したら、mwings::ParsedPacketBaseを継承し、パケットの中身を記述します。
ここでは、対象の子機に固有のデータを記述します。宛先の論理デバイスIDといった一般的なデータは、既にmwings::ParsedPacketBaseへ登録されているからです。
mwings::ParsedPacketBaseにある項目
| 型 | 名称 | 内容 |
|---|---|---|
uint32_t | u32SourceSerialId | 送信元のシリアルID |
uint8_t | u8SourceLogicalId | 送信元の論理デバイスID |
uint16_t | u16SequenceNumber | シーケンス番号 |
uint8_t | u8Lqi | LQI |
uint16_t | u16SupplyVoltage | 電源電圧 (mV) |
電源電圧など、空き項目があっても構いません。
以下の部分において、App_Tag(アナログセンサ)に固有のデータを宣言しています。
struct ParsedAppTagAdcPacket final : public mwings::ParsedPacketBase {
uint32_t u32RouterSerialId;
uint8_t u8SensorType;
uint16_t u16AiVoltage[2];
};
正規パケットの判定条件の記述
mwings::PacketParserBaseを継承したapptagadc::PacketParserを作成し、純粋仮想関数isValid()をオーバーライドすることで正規パケットの判定条件を記述してください。
この条件に沿ったパケットを受信したとき、解釈を行います。
以下の部分では、センサ種別が0x12(アナログセンサ)であること、未使用領域が0であること、ペイロードの長さが22バイトであることを確認しています。
inline bool isValid(const BarePacket& barePacket) const override {
if ((barePacket.u8At(12) == 0x10)
and (barePacket.u32At(18) == 0)
and (barePacket.u16PayloadSize == 22)) {
return true;
}
return false;
}
パーサのソースファイル
parser/ 以下にある既存のファイルを複製したうえで名称変更してください。
ここでは、parser/AppTagAdcPacketParser.cpp を作成します。
#include "AppTagAdcPacketParser.h"
apptagadc::PacketParser AppTagAdcPacketParser;
bool apptagadc::PacketParser::parse(const BarePacket& barePacket, mwings::ParsedPacketBase* const parsedPacket) const
{
// WARNING: Note that there is NO RTTI
ParsedAppTagAdcPacket* const parsedAppTagAdcPacket = static_cast<ParsedAppTagAdcPacket*>(parsedPacket);
parsedAppTagAdcPacket->u32SourceSerialId = barePacket.u32At(7);
parsedAppTagAdcPacket->u8SourceLogicalId = barePacket.u8At(11);
parsedAppTagAdcPacket->u16SequenceNumber = barePacket.u16At(5);
parsedAppTagAdcPacket->u8Lqi = barePacket.u8At(4);
const uint16_t ecc = barePacket.u8At(13);
if (ecc <= 170) {
parsedAppTagAdcPacket->u16SupplyVoltage = 5 * ecc + 1950;
} else {
parsedAppTagAdcPacket->u16SupplyVoltage = 10 * (ecc - 170) + 2800;
}
parsedAppTagAdcPacket->u32RouterSerialId = barePacket.u32At(0);
parsedAppTagAdcPacket->u8SensorType = barePacket.u8At(12);
for (int i = 0; i < 2; i++) {
parsedAppTagAdcPacket->u16AiVoltage[i] = barePacket.u16At(2*i+14);
}
return true;
}
解釈する部分の記述
ヘッダファイル名やnamespaceを置換したら、parse()の中身を記述します。
以下の部分では、データ書式に従ってパケットの中身を格納しています。
無線タグアプリ(アナログセンサ)の出力書式
| # | データ | 内容 | 備考 |
|---|---|---|---|
char | ヘッダ | :のみ | |
| 0 | uint32 | 中継機のシリアルID | 中継なしは80000000 |
| 4 | uint8 | LQI | 0-255 |
| 5 | uint16 | 続き番号 | |
| 7 | uint32 | 送信元のシリアルID | |
| 11 | uint8 | 送信元の論理デバイスID | |
| 12 | uint8 | センサー種別 | |
| 13 | uint8 | 電源電圧(mV) | 電源電圧の計算を参照 |
| 14 | uint16 | ADC1の電圧 | |
| 16 | uint16 | ADC2の電圧 | |
| 18 | uint32 | 未使用 | |
| 22 | uint8 | チェックサム |
BarePacket.u8At()などのメソッドを使って、素のペイロードからデータを取り出します。
// WARNING: Note that there is NO RTTI
ParsedAppTagAdcPacket* const parsedAppTagAdcPacket = static_cast<ParsedAppTagAdcPacket*>(parsedPacket);
parsedAppTagAdcPacket->u32SourceSerialId = barePacket.u32At(7);
parsedAppTagAdcPacket->u8SourceLogicalId = barePacket.u8At(11);
parsedAppTagAdcPacket->u16SequenceNumber = barePacket.u16At(5);
parsedAppTagAdcPacket->u8Lqi = barePacket.u8At(4);
const uint16_t ecc = barePacket.u8At(13);
if (ecc <= 170) {
parsedAppTagAdcPacket->u16SupplyVoltage = 5 * ecc + 1950;
} else {
parsedAppTagAdcPacket->u16SupplyVoltage = 10 * (ecc - 170) + 2800;
}
parsedAppTagAdcPacket->u32RouterSerialId = barePacket.u32At(0);
parsedAppTagAdcPacket->u8SensorType = barePacket.u8At(12);
for (int i = 0; i < 2; i++) {
parsedAppTagAdcPacket->u16AiVoltage[i] = barePacket.u16At(2*i+14);
}
static_castについて
mwings::ParsedPacketBase*からapptagadc::ParsedPacketへのダウンキャストをdynamic_castではなくstatic_castによって行っています。これは、ハードウェアの制約により実行時型情報が使えないことに起因しています。本体のヘッダファイル
既存のパーサに加えて、新たなパーサを追加します。
インクルード文の追加
パケットパーサのヘッダファイルをインクルードします。
//// AppTagAdcPacketParser for App_Tag (ADC)
#include "parser/AppTagAdcPacketParser.h"
初期化子リストの拡張
イベントハンドラをnullptrで初期化します。
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket(nullptr),
終了処理の追加
イベントハンドラをnullptrに戻します。
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket = nullptr;
イベントハンドラの登録メソッドの追加
イベントハンドラを登録するon()メソッドを追加します。
//// AppTagAdcPacketParser for App_Tag (ADC)
inline void on(void (*callback)(const ParsedAppTagAdcPacket& packet)) { _onAppTagAdcPacket = callback; }
イベントハンドラの追加
イベントハンドラを格納するポインタを追加します。
//// AppTagAdcPacketParser for App_Tag (ADC)
void (*_onAppTagAdcPacket)(const ParsedAppTagAdcPacket& packet);
本体のソースファイルの編集
既存のパース処理に加えて、新たなパース処理を追加します。
イベントハンドラの初期化
begin()の呼びだし時にもイベントハンドラが初期化されるようにします。
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket = nullptr;
パース処理の追加
対象のパケットを受信したときに解釈を行うようにします。
//// Start: AppTagAdcPacketParser for App_Tag (ADC)
if (AppTagAdcPacketParser.isValid(barePacket) and _onAppTagAdcPacket) {
ParsedAppTagAdcPacket parsedAppTagAdcPacket;
if (AppTagAdcPacketParser.parse(barePacket, &parsedAppTagAdcPacket)) {
_onAppTagAdcPacket(parsedAppTagAdcPacket);
}
}
//// End: AppTagAdcPacketParser for App_Tag (ADC)
変更の適用
スケッチをビルドしたときに、ライブラリも再ビルドされます。