Extending Packet Parsers
Overview
To add a parser, you need to edit the following four files:
- (New) Parser header file
parser/FooBarPacketParser.h
- Write the contents of the packet to be parsed and the criteria for a valid packet
- (New) Parser source file
parser/FooBarPacketParser.cpp
- Write the part that interprets the packet contents
- (Append) Main header file
MWings.h
- Add the parser
- (Append) Main source file
MWings.cpp
- Add the parser
You can obtain the source files from GitHub.
Typically, place them in the libraries/
directory inside your Arduino home directory.
You can view the actual commit diffs on GitHub.
Added App_Tag (ADC) packet parser · monowireless/mwings_arduino@aa5ecdb
Parser Header File
Duplicate an existing file under parser/
and rename it.
Here, we create 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
Describing the Packet Contents to be Parsed
First, please check the App_Tag (Analog Sensor) output format.
Example output data
:80000000B700628201015A0010DF08FD09A300000000E9
# | Data | Description | Value | |
---|---|---|---|---|
: | char | Header | : | |
80000000 | 0 | uint32 | Router serial ID | No router |
B7 | 4 | uint8 | LQI | 183/255 |
0062 | 5 | uint16 | Sequence number | 98 |
8201015A | 7 | uint32 | Source serial ID | 0x201015A |
00 | 11 | uint8 | Source logical ID | 0x00 |
10 | 12 | uint8 | Sensor type | Analog sensor |
DF | 13 | uint8 | Supply voltage (mV) | 3330 mV |
08FD | 14 | uint16 | ADC1 voltage | 2301 mV |
09A3 | 16 | uint16 | ADC2 voltage | 2467 mV |
00000000 | 18 | uint32 | Unused | |
E9 | 22 | uint8 | Checksum | 0xE9 |
char | Footer | \r | ||
char | Footer | \n |
After replacing the include guard, namespace
, and comments, inherit from mwings::ParsedPacketBase
and describe the packet contents.
Here, declare data unique to the target device. General data such as the destination logical device ID is already registered in mwings::ParsedPacketBase
.
Items in mwings::ParsedPacketBase
Type | Name | Description |
---|---|---|
uint32_t | u32SourceSerialId | Source serial ID |
uint8_t | u8SourceLogicalId | Source logical device ID |
uint16_t | u16SequenceNumber | Sequence number |
uint8_t | u8Lqi | LQI |
uint16_t | u16SupplyVoltage | Supply voltage (mV) |
It’s fine to have unused fields such as supply voltage.
In the following section, unique data for App_Tag (Analog Sensor) is declared.
struct ParsedAppTagAdcPacket final : public mwings::ParsedPacketBase {
uint32_t u32RouterSerialId;
uint8_t u8SensorType;
uint16_t u16AiVoltage[2];
};
Writing the Criteria for a Valid Packet
Create apptagadc::PacketParser
inheriting from mwings::PacketParserBase
and override the pure virtual function isValid()
to specify the criteria for a valid packet.
Packets matching this condition will be parsed.
In the following, it checks that the sensor type is 0x10
(analog sensor), the unused area is 0
, and the payload length is 22 bytes.
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 Source File
Duplicate an existing file under parser/
and rename it.
Here, we create 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;
}
Writing the Parsing Logic
After replacing the header file name and namespace
, write the contents of parse()
.
The following stores the packet contents according to the data format.
Wireless Tag App (Analog Sensor) Output Format
# | Data | Description | Notes |
---|---|---|---|
char | Header | : only | |
0 | uint32 | Router serial ID | 80000000 if no router |
4 | uint8 | LQI | 0 -255 |
5 | uint16 | Sequence number | |
7 | uint32 | Source serial ID | |
11 | uint8 | Source logical ID | |
12 | uint8 | Sensor type | |
13 | uint8 | Supply voltage (mV) | See Supply Voltage Calculation |
14 | uint16 | ADC1 voltage | |
16 | uint16 | ADC2 voltage | |
18 | uint32 | Unused | |
22 | uint8 | Checksum |
Use methods like BarePacket.u8At()
to extract data from the raw payload.
// 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);
}
About static_cast
mwings::ParsedPacketBase*
to apptagadc::ParsedPacket
is done using static_cast
instead of dynamic_cast
. This is due to hardware constraints that prevent using runtime type information.Main Header File
In addition to existing parsers, add the new parser.
Adding Include Directive
Include the packet parser header file.
//// AppTagAdcPacketParser for App_Tag (ADC)
#include "parser/AppTagAdcPacketParser.h"
Extending the Initializer List
Initialize the event handler to nullptr
.
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket(nullptr),
Adding Cleanup Code
Reset the event handler to nullptr
.
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket = nullptr;
Adding Event Handler Registration Method
Add an on()
method to register the event handler.
//// AppTagAdcPacketParser for App_Tag (ADC)
inline void on(void (*callback)(const ParsedAppTagAdcPacket& packet)) { _onAppTagAdcPacket = callback; }
Adding Event Handler Member
Add a pointer to store the event handler.
//// AppTagAdcPacketParser for App_Tag (ADC)
void (*_onAppTagAdcPacket)(const ParsedAppTagAdcPacket& packet);
Editing Main Source File
Add the new parsing process in addition to the existing parsing.
Initializing the Event Handler
Ensure the event handler is initialized when begin()
is called.
//// AppTagAdcPacketParser for App_Tag (ADC)
_onAppTagAdcPacket = nullptr;
Adding Parsing Process
Parse the target packet when received.
//// 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)
Applying Changes
When you build the sketch, the library will also be rebuilt.