Parent_MONOSTICK
This act includes the following:
- Receiving wireless packets
- Interpreting received packet data
- Setting interactive mode -
<STG_STD>
- Converting byte sequences to ASCII format -
serparser
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
Role | Example |
---|---|
Parent device | MONOSTICK BLUE/RED |
Child device | TWELITE 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 screenappid_default
→ Default application IDch_default
→ Default channellid_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]
.
This notation corresponds only to uint8_t[N]
type with specified array length N. When using uint8*
, char*
, or char[]
types, you need to specify using make_pair(char*, int)
.
char fourchars[5]{}; // Allocate 5 bytes including null terminator \0
auto&& np = expand_bytes(
rx.get_payload().begin(), rx.get_payload().end()
, make_pair((char *)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, ???>)
.
The packet sequence number is automatically set by <NWK_SIMPLE>
and assigned in the order of transmitted packets. This value is used to detect duplicate packets.
LQI (Link Quality Indicator) corresponds to the radio signal strength at reception; the larger the value, the stronger the received field strength. However, there is no strict correlation defined between this value and physical quantities, and since it is relative to environmental noise, even a larger LQI with more noise may result in lower communication success rate.
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()
)