This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

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.