/      日本語

Extending Packet Parsers

How to add packet parsers
In the MWings library, you can easily add 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

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.

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.

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.

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);
}

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.