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

Parent_MONOSTICK

Parent application (for MONOSTICK)
    This act uses MONOSTICK as the parent device. It outputs the data payload of packets from child devices to the serial port. You can display packets from many sample acts.

    Act Features

    • Receives packets from child devices of sample acts and outputs them to the serial port.

    How to Use the Act

    Required TWELITE and Wiring

    RoleExample
    Parent deviceMONOSTICK BLUE/RED
    Child deviceTWELITE series set as child devices in sample acts
    (e.g., Slp_Wk_and_Tx, PAL_AMB, PAL_MAG, PAL_MOT???, etc.)

    Please check initially with the following default settings.

    • Application ID: 0x1234abcd
    • Channel: 13

    Explanation of the Act

    Declaration Section

    Include

    // use twelite mwx c++ template library
    #include <TWELITE>
    #include <MONOSTICK>
    #include <NWK_SIMPLE>
    #include <STG_STD>

    Includes the board behavior <MONOSTICK> for MONOSTICK. This board support includes LED control and watchdog support.

    • <NWK_SIMPLE> loads definitions for a simple relay network
    • <STG_STD> loads definitions for interactive mode.

    Others

    // application ID
    const uint32_t DEFAULT_APP_ID = 0x1234abcd;
    // channel
    const uint8_t DEFAULT_CHANNEL = 13;
    // option bits
    uint32_t OPT_BITS = 0;
    
    /*** function prototype */
    bool analyze_payload(packet_rx& rx);

    Declares default values and function prototypes.

    setup()

    auto&& brd = the_twelite.board.use<MONOSTICK>();
    auto&& set = the_twelite.settings.use<STG_STD>();
    auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();

    In setup(), first load <MONOSTICK> board behavior, <STG_STD> interactive mode behavior, and <NWK_SIMPLE> behavior using use<>. This procedure must be done inside setup().

    set << SETTINGS::appname("PARENT"); // Title displayed in the settings screen
    set << SETTINGS::appid_default(DEFAULT_APP_ID); // Default application ID
    set << SETTINGS::ch_default(DEFAULT_CHANNEL); // Default channel
    set << SETTINGS::lid_default(0x00); // Default LID
    set.hide_items(E_STGSTD_SETID::OPT_DWORD2, E_STGSTD_SETID::OPT_DWORD3, E_STGSTD_SETID::OPT_DWORD4, E_STGSTD_SETID::ENC_KEY_STRING, E_STGSTD_SETID::ENC_MODE);
    set.reload(); // Load settings from non-volatile memory
    OPT_BITS = set.u32opt1(); // Example of reading (option bits)
    

    Next, set the interactive mode settings and read the settings. <STG_STD> interactive mode provides standard items but allows some customization for each act.

    • appname → Act name displayed in the title line of the settings screen
    • appid_default → Default application ID
    • ch_default → Default channel
    • lid_default → Default device ID (LID)
    • .hide_items() → Hide specific items

    Always call .reload() before reading settings. Methods like .u32opt1() are provided for each setting.

    the_twelite
    	<< set                    // Apply interactive mode settings
    	<< TWENET::rx_when_idle() // Specify to receive
    	;
    
    // Register Network
    nwk << set;							// Apply interactive mode settings
    nwk << NWK_SIMPLE::logical_id(0x00) // Re-set only LID
    	;

    Some settings can be directly applied using the <STG_STD> object. Also, if you want to overwrite certain values due to DIP switch settings, you can change them after applying settings. In the example above, application ID, channel, and wireless output are set on the_twelite object, and LID and retransmission count are set on nwk object, then LID is reset to 0.

    brd.set_led_red(LED_TIMER::ON_RX, 200); // RED (on receiving)
    brd.set_led_yellow(LED_TIMER::BLINK, 500); // YELLOW (blinking)
    

    The <MONOSTICK> board behavior provides procedures for LED control.

    The first line sets the red LED to light for 200ms when a wireless packet is received. The first parameter LED_TIMER::ON_RX means “on receiving a wireless packet”. The second parameter specifies the lighting time in ms.

    The second line sets the LED to blink. The first parameter LED_TIMER::BLINK means blinking, and the second parameter is the ON/OFF switching time. The LED will turn on and off every 500ms (i.e., blinking with a 1-second cycle).

    the_twelite.begin(); // start twelite!
    

    Procedure to start the_twelite. Although it did not appear in act0..4, if you configure the_twelite or register various behaviors, always call this.

    loop()

    There is no processing inside loop() in this sample.

    void loop() {
    }

    on_rx_packet()

    This callback function is called when a packet is received. In this example, several outputs are made for the received packet data.

    void on_rx_packet(packet_rx& rx, bool_t &handled) {
    	Serial << ".. coming packet (" << int(millis()&0xffff) << ')' << mwx::crlf;
    
      ...
    
    	// packet analyze
    	analyze_payload(rx);
    }
    analyze_payload

    The analyze_payload() called at the end of the function contains code to interpret packets from several sample acts. Please refer to the packet generation part in the sample acts for correspondence.

    bool b_handled = false;
    
    uint8_t fourchars[4]{};
    auto&& np = expand_bytes(
    	    rx.get_payload().begin(), rx.get_payload().end()
    		, fourchars
        );
    
    if (np == nullptr) return;
    
    // display fourchars at first
    Serial
    	<< fourchars
    	<< format("(ID=%d/LQ=%d)", rx.get_addr_src_lid(), rx.get_lqi())
    	<< "-> ";

    This function first reads 4-character identification data into the fourchars[5] array.

    Reading is done using the expand_bytes() function. The first and second parameters follow the C++ standard library convention, giving the starting pointer .begin() and the pointer just after the end .end() of the received packet’s payload part. The following parameters are variadic arguments specifying the data variables to read. The return value is nullptr on error, otherwise the next interpretation pointer. If parsing reaches the end, .end() is returned. Here the parameter is uint8_t fourchars[4].

    Next, process corresponding to the 4-byte header is performed. Here, the packet of the sample act Slp_Wk_and_Tx is interpreted and displayed.

    // Slp_Wk_and_Tx
    if (!b_handled && !strncmp(fourchars, "TXSP", 4)) {
    	b_handled = true;
    	uint32_t tick_ms;
    	uint16_t u16work_ct;
    
    	np = expand_bytes(np, rx.get_payload().end()
    		, tick_ms
    		, u16work_ct
    	);
    
    	if (np != nullptr) {
    		Serial << format("Tick=%d WkCt=%d", tick_ms, u16work_ct);
    	} else {
    		Serial << ".. error ..";
    	}
    }

    Set b_handled to true to skip other interpretation parts.

    The "TXSP" packet contains a uint32_t system timer count and a uint16_t dummy counter value. Declare each variable and read them using expand_bytes(). The difference from above is that the first parameter for reading is np. Provide tick_ms and u16work_ct as parameters, reading values stored in big-endian byte sequence in the payload.

    If reading succeeds, output the contents and finish.

    Define and output a custom ASCII format

    Construct ASCII format in the user-defined order.

    smplbuf_u8<128> buf;
    mwx::pack_bytes(buf
    	, uint8_t(rx.get_addr_src_lid())		   // Source logical ID
    	, uint8_t(0xCC)											   // 0xCC
    	, uint8_t(rx.get_psRxDataApp()->u8Seq) // Packet sequence number
    	, uint32_t(rx.get_addr_src_long())		 // Source serial number
    	, uint32_t(rx.get_addr_dst())			     // Destination address
    	, uint8_t(rx.get_lqi())					       // LQI: reception quality
    	, uint16_t(rx.get_length())				     // Number of bytes following
    	, rx.get_payload() 						         // Data payload
    );
    
    serparser_attach pout;
    pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
    
    Serial << "FMT PACKET -> ";
    pout >> Serial;
    Serial << mwx::flush;

    The first line declares a local object buffer to store the data sequence before converting to ASCII format.

    The second line uses pack_bytes() to store the data sequence into the buf. See source code comments for data structure. The parameter of pack_bytes() can be a container of type smplbuf_u8 (smplbuf<uint8_t, ???>).

    Lines 13, 14, and 17 declare the serial parser, configure it, and output.

    Dump output including NWK_SIMPLE header

    The first output (disabled by if(0)) displays all data including control data of <NWK_SIMPLE>. The control data is 11 bytes. Normally, control information is not directly referenced but shown here for reference.

    serparser_attach pout;
    pout.begin(PARSER::ASCII, rx.get_psRxDataApp()->auData,
        rx.get_psRxDataApp()->u8Len, rx.get_psRxDataApp()->u8Len);
    
    Serial << "RAW PACKET -> ";
    pout >> Serial;
    Serial << mwx::flush;
    
    // Reference: Packet structure of control part
    // uint8_t  : 0x01 fixed
    // uint8_t  : Source LID
    // uint32_t : Source long address (serial number)
    // uint32_t : Destination address
    // uint8_t  : Relay count
    

    The first line declares a serial parser local object for output. It does not have an internal buffer and uses an external buffer, leveraging the parser’s output function to output the byte sequence in the buffer as ASCII.

    The second line sets the buffer of the serial parser. It specifies the existing data array, i.e., the payload part of the received packet. serparser_attach pout declares a serial parser using an existing buffer. The first parameter of pout.begin() specifies the parser format as PARSER::ASCII (ASCII format). The second parameter is the start address of the buffer. The third is the valid data length in the buffer, and the fourth is the maximum buffer length. The fourth parameter is the same as the third since it is used only for output, not for format interpretation.

    Line 6 outputs to the serial port using the >> operator.

    Line 7 Serial << mwx::flush waits for the output of any remaining data to complete. (Equivalent to Serial.flush())