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

Classes

Various Classes

1 - MWX_APIRET

Return Value Class
API return value class wrapping a 32-bit type. The MSB (bit 31) indicates success or failure. Bits 0 to 30 are used to store the return value.
class MWX_APIRET {
	uint32_t _code;
public:
	MWX_APIRET() : _code(0) {}
	MWX_APIRET(bool b) {
	  _code = b ? 0x80000000 : 0;
  }
	MWX_APIRET(bool b, uint32_t val) {
		_code = (b ? 0x80000000 : 0) + (val & 0x7fffffff);
	}
	inline bool is_success() const { return ((_code & 0x80000000) != 0); }
	inline bool is_fail() const { return ((_code & 0x80000000) == 0); }
	inline uint32_t get_value() const { return _code & 0x7fffffff; }
	inline operator uint32_t() const { return get_value(); }
	inline operator bool() const { return is_success(); }
};

Constructors

MWX_APIRET()
MWX_APIRET(bool b)
MWX_APIRET(bool b, uint32_t val)

The default constructor initializes with the combination false, 0.

You can also explicitly construct with bool and uint32_t parameters.

Since a constructor with a bool parameter is implemented, you can use true/false as return values, like the following example:

MWX_APIRET myfunc() {
  if (...) return true;
  else false;
}

Methods

is_success(), operator bool()

inline bool is_success()
inline operator bool()

Returns true if the MSB is set to 1.

inline bool is_fail()

Returns true if the MSB is 0.

inline uint32_t get_value()
inline operator uint32_t()

Retrieves the value part from bits 0 to 30.

2 - alloc

Memory Allocation
Used as a template argument for container classes (smplbuf, smplque) to specify or allocate internal memory regions.
Class NameDescription
alloc_attach<T>Attach an existing buffer
alloc_local<T, int N>Statically allocate a buffer of N bytes internally
alloc_heap<T>Allocate the specified size on the heap

For alloc_attach and alloc_heap, you must call the appropriate initialization method (init_???()) depending on the allocator type.

Initialization

void attach(T* p, int n) // alloc_attach
void init_local()        // alloc_local
void init_heap(int n)    // alloc_heap

Initializes with buffer p and size n.

Methods

alloc_size()

uint16_t alloc_size()

Returns the size of the buffer.

_is_attach(), _is_local(), _is_heap()

These methods trigger a compile-time error using static_assert if an incompatible allocator method is used.

3 - axis_xyzt

Container class for accelerometer data
A struct for storing three-axis accelerometer sensor values, with additional utilities to enhance data handling convenience.
struct axis_xyzt {
    int16_t x;
    int16_t y;
    int16_t z;
    uint16_t t;
};

get_axis_{x,y,z}_iter()

/* Return type is a long template name, so we use auto&& for brevity */
auto&& get_axis_x_iter(Iter p)
auto&& get_axis_y_iter(Iter p)
auto&& get_axis_z_iter(Iter p)

Generates an iterator that accesses one of the X, Y, or Z axis elements, given an iterator to a container class holding axis_xyzt elements.

In the following example, buf.begin() and buf.end() are passed to get_axis_x_iter() and used with the std::minmax_element algorithm to analyze the X-axis.

##include <algorithm>

void myfunc() {
  // Container class
  smplbuf_local<axis_xyzt, 10> buf;

  // Insert test data
  buf[0] = { 1, 2, 3, 4 };
  buf[1] = { 2, 3, 4, 5 };
  ...

  // Algorithm to get min and max values
  auto&& minmax = std::minmax_element(
    get_axis_x_iter(buf.begin()),
    get_axis_x_iter(buf.end()));

  Serial << "min=" << int(*minmax.first)
        << ",max=" << int(*minmax.second) << mwx::crlf;
}

get_axis_{x,y,z}()

/* Return type is a long template name, so we use auto&& for brevity */
auto&& get_axis_x(T& c)
auto&& get_axis_y(T& c)
auto&& get_axis_z(T& c)

These functions generate a virtual container class that extracts one of the X, Y, or Z axes from a container class holding axis_xyzt elements. The generated container only implements begin() and end() methods, which return iterators equivalent to those from get_axis_{x,y,z}_iter().

##include <algorithm>

void myfunc() {
  // Container class
  smplbuf_local<axis_xyzt, 10> buf;

  // Insert test data
  buf[0] = { 1, 2, 3, 4 };
  buf[1] = { 2, 3, 4, 5 };
  ...

  // Extract the X-axis values from the queue
  auto&& vx = get_axis_x(que);

  // Use range-based for loop
  for (auto&& e : vx) { Serial << int(e) << ','; }

  // Algorithm to get min and max values
  auto&& minmax = std::minmax_element(
      vx.begin(), vx.end());

  Serial << "min=" << int(*minmax.first)
        << ",max=" << int(*minmax.second) << mwx::crlf;
}

4 - packet_rx

Received Packet
This class is a wrapper for the tsRxDataApp structure from the TWENET library.

This class object can be obtained via a behavior callback function or the on_rx_packets() method.

In packet_rx, the packet data payload is handled via the smplbuf container, and utility functions like expand_bytes() simplify the interpretation of the payload.

Methods

get_payload()

smplbuf_u8_attach& get_payload()

Retrieves the data payload of the packet.

get_psRxDataApp()

const tsRxDataApp* get_psRxDataApp()

Retrieves the receive structure from the TWENET C library.

get_length()

uint8_t get_length()

Returns the length of the payload. Equivalent to .get_payload().size().

get_lqi()

uint8_t get_lqi()

Retrieves the LQI (Link Quality Indicator).

get_addr_src_long(), get_addr_src_lid()

uint32_t get_addr_src_long()
uint8_t get_addr_src_lid()

Retrieves the source address.

get_addr_src_long() returns the source serial number. The MSB (bit 31) is always set to 1.

get_addr_src_lid() returns the source logical ID, which ranges from 0x00 to 0xFE (used in <NWK_SIMPLE>).

get_addr_dst()

uint32_t get_addr_dst()

Retrieves the destination address.

The destination address is set by the sender, and its value range depends on the address type.

ValueDescription
The serial number is used as the destination.Specifies the serial number as the destination.
0x00-0xFFAn 8-bit logical ID is used as the destination.

is_secure_pkt()

bool is_secure_pkt()

Returns true if the packet is encrypted; otherwise returns false.

get_network_type()

uint8_t get_network_type()

Returns the network type of the packet as identified by the network behavior.

ValueDescription
mwx::NETWORK::LAYEREDPacket from <NWK_LAYERED>
mwx::NETWORK::SIMPLEPacket from <NWK_SIMPLE>
mwx::NETWORK::NONEPacket not transmitted via a network (e.g., App_Twelite)
OtherError or unidentified packet

5 - packet_tx

Transmission Packet
This class is a wrapper for the tsTxDataApp structure from the TWENET C library. Objects derived from this class can be obtained via network behaviors or on_tx_comp().
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
	pkt << tx_addr(0x00)
		<< tx_retry(0x1)
		<< tx_packet_delay(0,50,10);

	pack_bytes(pkt.get_payload()
		, make_pair("APP1", 4)
		, uint8_t(u8DI_BM)
	);

  pkt.transmit();
}

Creating the Object

It is created using the .prepare_tx_packet() method of the network behavior.

if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
  ...
}

In the example above, the object pkt is obtained using the_twelite.network.use<NWK_SIMPLE>(). Although the type is inferred using auto&&, it will be a derived class of packet_tx.

The pkt object returns true or false depending on whether the transmission queue is available. It returns false if the transmission queue is full and no more requests can be added.

Transmission Settings

Various settings, such as the destination address, must be configured for a wireless packet to be delivered. These settings are applied by passing configuration objects as the right-hand value of the << operator.

pkt << tx_addr(0x00)
	  << tx_retry(0x1)
  	<< tx_packet_delay(0,50,10);

The following describes the configuration objects used for setting up transmission.

tx_addr

tx_addr(uint32_t addr)

Specifies the destination address addr. Refer to the network behavior specification for valid address values.

  • <NWK_SIMPLE> If MSB (bit 31 = 0x80000000) is set, it indicates the destination is a serial number of a wireless module. 0x00 to 0xEF indicates an 8-bit logical ID. 0xFE is broadcast to child nodes (0x010xEF), and 0xFF is broadcast to all nodes.

tx_retry

tx_retry(uint8_t u8count, bool force_retry = false)

Specifies the number of retransmissions using u8count. If force_retry is set to true, retransmission will occur regardless of whether the transmission succeeds.

  • <NWK_SIMPLE> Sends the same packet u8count+1 times. The force_retry setting is ignored.

tx_packet_delay

tx_packet_delay(uint16_t u16DelayMin,
                uint16_t u16DelayMax,
                uint16_t u16RetryDur)

Configures delay before transmission and retry interval. Specify u16DelayMin and u16DelayMax in milliseconds. Transmission will start at a random point within this interval. Retry interval is specified by u16RetryDur, also in milliseconds.

  • <NWK_SIMPLE> This setting is valid. If the same packet arrives more than one second after the first, it will not be excluded as a duplicate. This can occur due to a long retry interval or delay in relay. Duplicate handling can be configured in <NWK_SIMPLE> initialization.

tx_process_immediate

tx_process_immediate()

Requests that the packet be transmitted as quickly as possible. Transmission is normally triggered by a TickTimer operating every 1ms. This setting forces immediate processing. Has no effect unless used with tx_packet_delay(0,0,0).

Other packet transmissions in progress will be processed normally.

  • <NWK_SIMPLE> This setting is valid.

tx_ack_required

tx_ack_required()

In wireless packet communication, ACK (acknowledgment) is a short packet returned by the recipient to confirm successful reception. This setting enables ACK-based transmission.

  • <NWK_SIMPLE> This setting is invalid in <NWK_SIMPLE> and causes a compile error. <NWK_SIMPLE> does not support ACK-based communication.

tx_addr_broadcast

tx_addr_broadcast()

Specifies broadcast as the destination.

  • <NWK_SIMPLE> This setting is invalid in <NWK_SIMPLE> and results in a compile error. Instead, use tx_addr(0xFF) for general broadcast or tx_addr(0xFE) for broadcast to child nodes.

tx_packet_type_id

tx_packet_type_id(uint8_t)

Specifies the TWENET packet type ID (0 to 7).

  • <NWK_SIMPLE> This setting is invalid in <NWK_SIMPLE> and causes a compile error. The type ID is reserved internally and not user-configurable.

6 - serparser

Serial Format Input/Output (mwx::serial_parser)
Used for serial format input and output. It has an internal buffer that holds a parsed binary sequence. During input, it reads a sequence one byte at a time and stores it in the internal buffer according to the specified format, completing the process when the entire sequence has been interpreted. For output, it outputs the buffer according to the specified output format.

Three class names are defined depending on how the memory buffer is managed (alloc).

// serparser_attach : uses an existing buffer
serparser_attach

// serparser : allocates an internal buffer of N bytes
serparser_local<N>

// serparser_heap : allocates a buffer in the heap
serparser_heap

Constants (Format Type)

These are format types passed as parameters to the begin() initializer. Two types are supported here: ASCII and binary formats.

ConstantFormat Type
uint8_t PARSER::ASCII = 1ASCII format
uint8_t PARSER::BINARY = 2Binary format

About Formats

ASCII Format

The ASCII format is a method to represent a binary data sequence as a string.

For example, the byte sequence 00A01301FF123456 is represented in ASCII format as follows. It starts with :, the checksum is B1, and the terminator is [CR:0x0d][LF:0x0a].

:00A01301FF123456B1[CR][LF]

The terminating checksum can be omitted by replacing the checksum and CRLF sequence with X. Although this makes the format more vulnerable to corrupted data, it is useful for quick tests or sending data manually.

:00A01301FF123456X

Definition

SectionByte Count (Original)Byte Count (Format)Description
Header1: (0x3A) colon character
DataN2NEach byte is represented as two ASCII characters (A–F in uppercase). For example, 0x1F is represented as 1 (0x31) and F (0x46).
Checksum2The 8-bit sum of all data bytes is calculated, and the two’s complement of the result is taken. The checksum byte is represented as two ASCII characters. For example, for 00A01301FF123456, the sum is 0x4F, and its two’s complement is 0xB1.
Footer2[CR] (0x0D) and [LF] (0x0A) characters

Binary Format

The binary format appends a header and checksum to a binary data sequence for transmission.

For example, the byte sequence 00A01301FF123456 is represented in binary format as:

0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D

Definition

SectionByte Count (Original)Byte Count (Format)Description
Header2Use 0xA5 0x5A
Data Length2Two bytes in big-endian format with MSB (0x8000) set. For example, if the data length is 8 bytes, use 0x80 0x08.
DataNNSpecifies the original data
Checksum1XOR of all data bytes. For example, XOR of 00A01301FF123456 results in 0x3D.
Footer(1)Checksum effectively marks the end. When output from a wireless module, 0x04 (EOT) is appended.

Methods

Declaration: begin()

// serparser_attach : uses an existing buffer
serparser_attach p1;

uint8_t buff[128];
p1.begin(PARSER::ASCII, buff, 0, 128);


// serparser : allocates an internal buffer of N bytes
serparser p2<128>;
p2.begin(PARSER::ASCII);


// serparser_heap : allocates a buffer on the heap
serparser_heap p3;
p3.begin(PARSER::ASCII, 128);

The declaration specifies the memory allocation class. Since this can be cumbersome, aliases are provided as shown above.

Class Name (Alias)
Memory Allocation
Description
serparser_attachUse an existing buffer specified via begin()
serparser_local<N>Allocate an internal buffer of N bytes
serparser_heapAllocate the specified size on the heap using the begin() method

Call the begin() method corresponding to the memory allocation class.

serparser_attach

void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)

Uses the format specified by ty (see formats) and the buffer pointed to by p. The total buffer size is max_siz, and the valid data length is specified by siz.

This definition is especially useful when outputting a data sequence in formatted style (see >> operator).

serparser_local<N> - Allocate internal buffer

void begin(uint8_t ty)

Initializes using the format specified by ty (see formats).

serparser_heap - Allocate buffer on heap

void begin(uint8_t ty, uint16_t siz)

Initializes using the format specified by ty and allocates siz bytes on the heap.

get_buf()

BUFTYPE& get_buf()

Returns the internal buffer. The buffer is of type smplbuf<uint8_t, alloc>.

parse()

inline bool parse(uint8_t b)

Processes input characters. Accepts one byte of input at a time and interprets it according to the specified format. For example, in ASCII format, the sequence :00112233X is processed byte-by-byte (:, 0, 0, … X), and the format interpretation is completed upon receiving X.

The parse() parameter is the input byte. It returns true if interpretation has completed.

Example

while (Serial.available()) {
    int c = Serial.read();

    if (SerialParser.parse(c)) {
        // Format parsing complete, b holds the resulting data sequence (smplbuf<uint8_t>)
        auto&& b = SerialParser.get_buf();

        // Below is the processing for the obtained data sequence
        if (b[0] == 0xcc) {
          // ...
        }
    }
}

operator bool()

operator bool()

Returns true if parsing has completed via parse(), otherwise returns false while parsing is in progress.

Example (the parse() example can be rewritten as follows)

while (Serial.available()) {
    int c = Serial.read();

    SerialParser.parse(c);

    if(SerialParser) {
        // Format parsing complete, b holds the resulting data sequence (smplbuf<uint8_t>)
        auto&& b = SerialParser.get_buf();
        // ...
    }
}

<< Operator

Outputs the internal buffer to a stream (e.g., Serial) in formatted style.

Example

uint8_t u8buf[] = { 0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc };

ser_parser pout;
pout.begin(PARSER::ASCII, u8buf, 6, 6); // Specify 6 bytes from u8buf

Serial << pout;// Output to Serial in formatted style -> :112233AABBCC??[CR][LF]

7 - pktparser

Packet Parser
pktparser (parser_packet) interprets byte sequences converted by the serparser.
serparser_heap parser_ser;

void setup() {
    // init ser parser (heap alloc)
    parser_ser.begin(PARSER::ASCII, 256);
}

void loop() {
    int c;
    while ((c = Serial.read()) >= 0) {
        parser_ser.parse(c);
        if (parser_ser.available()) {
            // get buffer object
            auto&& payl = parser_ser.get_buf();
            // identify packet type
            auto&& typ = identify_packet_type(payl.begin(), payl.end());

            // if packet type is TWELITE standard 0x81 message
            if (typ == E_PKT::PKT_TWELITE) {
                pktparser pkt; // packet parser object
                // analyze packet data
                typ = pkt.parse<TwePacketTwelite>(payl.begin(), payl.end());

                if (typ != E_PKT::PKT_ERROR) { // success!
                    // get data object
                    auto&& atw = pkt.use<TwePacketTwelite>();

                    // display packet inforamtion
                    Serial << crlf << format("TWELITE: SRC=%08X LQI=%03d "
                        , app.u32addr_src, app.u8lqi);
	                  Serial << " DI1..4="
	                      << atw.DI1 ? 'L' : 'H' << atw.DI2 ? 'L' : 'H'
                        << atw.DI3 ? 'L' : 'H' << atw.DI4 ? 'L' : 'H';
                }
            }
        }
    }
}

The example above shows how to interpret a 0x81 message from the standard application. The parser_ser object receives input from Serial and converts it to a byte sequence. This sequence is then passed to identify_packet_type() to determine the packet type (E_PKT). If the packet type is identified, .parse<TwePacketTwelite>() is called to analyze the content. The result is of type TwePacketTwelite, and the .use<TwePacketTwelite>() function is used to retrieve the parsed object. TwePacketTwelite is a class, but its member variables can be accessed like a struct.

parse<T>

template <class T>
E_PKT parse(const uint8_t* p, const uint8_t* e)

Parses the byte sequence.

Specify the packet type T to be parsed. For example, use TwePacketTwelite for a 0x81 message from the standard application.

p and e specify the beginning and one-past-the-end of the byte sequence.

Returns a value of type E_PKT. If an error occurs, it returns E_PKT::PKT_ERROR.

use<T>

template 
T& use()

Returns a reference to the object corresponding to the interpreted byte sequence. Call this only after successfully executing parse<T>.

T must be the same type used in parse<T>, or you can specify TwePacket to retrieve only basic information.

7.1 - E_PKT

Packet Type Definitions

The following packet types are supported:

NameDescription
PKT_ERRORUsed before parsing or when the packet type cannot be determined. The TwePacket does not contain valid data.
PKT_TWELITEParsed result of the 0x81 command from the standard application App_Twelite.
PKT_PALParsed format of TWELITE PAL serial output.
PKT_APPIOParsed UART messages from the remote control application App_IO.
PKT_APPUARTParsed extended format from the serial communication application App_UART.
PKT_APPTAGParsed UART messages from the wireless tag application App_Tag. Sensor-specific parts are not interpreted but returned as raw payload bytes.
PKT_ACT_STDOutput format used by samples of Act.

7.2 - identify_packet_type()

Determine Packet Type

idenify_packet_type()

Determines the packet type from a byte sequence. Returns a value of E_PKT.

E_PKT identify_packet_type(uint8_t* p, uint8_t u8len)

If the byte sequence cannot be interpreted as a known packet type, E_PKT::PKT_ERROR is returned.

7.3 - TwePacket

Packet Type
This is the base class for packet types. The common member struct contains shared information such as address data.
class TwePacket {
	public:
		static const E_PKT _pkt_id = E_PKT::PKT_ERROR;

		struct {
			uint32_t tick;     // System time at the time of interpretation [ms]
			uint32_t src_addr; // Source address (serial number)
			uint8_t src_lid;   // Source address (logical ID)
			uint8_t lqi;       // LQI
			uint16_t volt;     // Voltage [mV]
		} common;
};

7.3.1 - TwePacketTwelite

Packet from App_Twelite
The TwePacketTwelite class interprets the 0x81 command from the standard application App_Twelite.
class TwePacketTwelite : public TwePacket, public DataTwelite { ... };

After executing parse<TwePacketTwelite>(), packet information is stored in DataTwelite.

DataTwelite Struct

struct DataTwelite {
		// Source serial number
		uint32_t u32addr_src;

		// Source logical ID
		uint8_t u8addr_src;

		// Destination logical ID
		uint8_t u8addr_dst;

		// Timestamp at transmission
		uint16_t u16timestamp;

		// Flag for low-latency transmission
		bool b_lowlatency_tx;

		// Number of repeat transmissions
		uint16_t u8rpt_cnt;

		// LQI value
		uint16_t u8lqi;

		// DI state (true = active, Lo/GND)
		bool DI1, DI2, DI3, DI4;
		// DI state bitmap (from LSB: DI1, DI2, DI3, DI4)
		uint8_t DI_mask;

		// True if DI has ever been active
		bool DI1_active, DI2_active, DI3_active, DI4_active;
		// DI active bitmap (from LSB: DI1, DI2, DI3, DI4)
		uint8_t DI_active_mask;

		// Module power voltage [mV]
		uint16_t u16Volt;

		// AD values [mV]
		uint16_t u16Adc1, u16Adc2, u16Adc3, u16Adc4;
		// Bitmap indicating which ADs are active (from LSB: AD1, AD2, AD3, AD4)
		uint8_t Adc_active_mask;
};

7.3.2 - TwePacketIO

Packet from App_IO
The TwePacketAppIO class interprets the standard application’s App_IO serial message (0x81).
class TwePacketAppIO : public TwePacket, public DataAppIO { ... };

Packet data details are stored in DataTwelite after executing parse<TwePacketIO>().

DataAppIO Struct

struct DataAppIO {
		// Source serial number
		uint32_t u32addr_src;

		// Source logical ID
		uint8_t u8addr_src;

		// Destination logical ID
		uint8_t u8addr_dst;

		// Timestamp at transmission
		uint16_t u16timestamp;

		// Flag for low-latency transmission
		bool b_lowlatency_tx;

		// Number of repeat relays
		uint16_t u8rpt_cnt;

		// LQI value
		uint16_t u8lqi;

		// DI state bitmap (from LSB: DI1, DI2, DI3, DI4, ...)
		uint8_t DI_mask;

		// DI active (1 if in use) bitmap (from LSB: DI1, DI2, DI3, DI4, ...)
		uint8_t DI_active_mask;

		// Bitmap indicating whether DI is triggered by interrupt (from LSB: DI1, DI2, DI3, DI4, ...)
		uint16_t DI_int_mask;
};

7.3.3 - TwePacketUART

Packet from App_UART
The TwePacketAppUart class represents the extended format of App_UART received by the parent or repeater application App_Wings.
class TwePacketAppUART : public TwePacket, public DataAppUART

After executing parse<TwePacketUART>(), the packet information is stored in DataAppUART.

DataAppUART Struct

struct DataAppUART {
		/**
		 * source address (Serial ID)
		 */
		uint32_t u32addr_src;

		/**
		 * source address (Serial ID)
		 */
		uint32_t u32addr_dst;

		/**
		 * source address (logical ID)
		 */
		uint8_t u8addr_src;

		/**
		 * destination address (logical ID)
		 */
		uint8_t u8addr_dst;

		/**
		 * LQI value
		 */
		uint8_t u8lqi;

		/**
		 * Response ID
		 */
		uint8_t u8response_id;

		/**
		 * Payload length
		 */
		uint16_t u16paylen;

		/**
		 * payload
		 */
##if MWX_PARSER_PKT_APPUART_FIXED_BUF == 0
		mwx::smplbuf_u8_attach payload;
##else
		mwx::smplbuf_u8<MWX_PARSER_PKT_APPUART_FIXED_BUF> payload;
##endif
	};

payload represents the data portion, but the storage method varies depending on the macro definition.

If MWX_PARSER_PKT_APPUART_FIXED_BUF is set to 0 during compilation, payload will reference the byte sequence directly. If the original byte sequence is modified, the data in payload may become corrupted.

If MWX_PARSER_PKT_APPUART_FIXED_BUF is defined with a value greater than 0, a buffer of that size (in bytes) is allocated for payload. However, if the size of the serial data exceeds the buffer, parse<TwePacketAppUART>() will fail and return E_PKT::PKT_ERROR.

7.3.4 - TwePacketPAL

Packet from App_PAL
The TwePacketPal class interprets packet data from TWELITE PAL. This class provides a common interface for handling TWELITE PAL packets (e.g., sensor data sent upstream).
class TwePacketPal : public TwePacket, public DataPal { ... };

Common PAL data is defined in DataPal.

Generator functions are provided to extract sensor-board-specific data for each type of PAL.

DataPal Struct

The data structure of PAL packets varies depending on the connected sensor, but DataPal holds the common part of the data structure.

struct DataPal {
	uint8_t u8lqi;        // LQI value

	uint32_t u32addr_rpt; // Address of repeater

	uint32_t u32addr_src; // Source address
	uint8_t u8addr_src;   // Source logical address

	uint16_t u16seq;      // Sequence number

	E_PAL_PCB u8palpcb;		// Type of PAL board
	uint8_t u8palpcb_rev;	// Revision of PAL board
	uint8_t u8sensors;		// Number of sensor data entries included (MSB=1 indicates error)
	uint8_t u8snsdatalen; // Length of sensor data (in bytes)

	union {
		const uint8_t *au8snsdata; // Pointer to sensor data
		uint8_t _pobj[MWX_PARSER_PKT_APPPAL_FIXED_BUF]; // Sensor-specific objects
	};
};

A PAL packet consists of two main blocks: the common PAL part and a sensor-specific data part. The sensor-specific part is stored as-is without parsing. To simplify handling, data exceeding 32 bytes is stored in uptr_snsdata, which is dynamically allocated.

The sensor-specific part is stored in a structure that inherits from PalBase. This structure is generated by generator functions defined in TwePacketPal.

When parse<TwePacketPAL>() is executed and the data fits within MWX_PARSER_PKT_APPPAL_FIXED_BUF, sensor-specific objects are created.

If it exceeds that size, a reference to the raw byte sequence is stored in au8snsdata.

In such cases, if the original byte sequence is modified, sensor-specific objects can no longer be generated.

PalBase

All sensor-specific structures for PAL inherit from PalBase, which contains the sensor data storage status u32StoredMask.

	struct PalBase {
		uint32_t u32StoredMask; // Internal flag indicating which data is stored
	};

PalEvent

A PAL event does not directly transmit raw sensor data, but rather processed information triggered under specific conditions. For example, when acceleration exceeds a threshold from a stationary state.

	struct PalEvent {
		uint8_t b_stored;       // True if stored
		uint8_t u8event_source; // Reserved
		uint8_t u8event_id;     // Event ID
		uint32_t u32event_param;// Event parameter
	};

If event data is present, .is_PalEvent() in TwePacketPal returns true, and .get_PalEvent() provides the PalEvent structure.

Generator Functions

Generator functions are provided to extract sensor-board-specific data for each type of PAL.

void print_pal(pktparser& pkt) {
	auto&& pal = pkt.use<TwePacketPal>();
	if (pal.is_PalEvent()) {
		PalEvent obj = pal.get_PalEvent();
	} else
	switch(pal.u8palpcb) {
	case E_PAL_PCB::MAG:
	  {
		  // generate pal board specific data structure.
		  PalMag obj = pal.get_PalMag();
	  } break;
  case E_PAL_PCB::AMB:
	  {
		  // generate pal board specific data structure.
		  PalAmb obj = pal.get_PalAmb();
	  } break;
	  ...
	default: ;
	}
}

To use generator functions, first check whether pkt is an event using .is_PalEvent(). If so, retrieve the data using get_PalEvent(). Otherwise, generate the appropriate object based on u8palpcb.

get_PalMag()

PalMag get_PalMag()

// MAG
struct PalMag : public PalBase {
    uint16_t u16Volt;         // Module voltage [mV]
    uint8_t u8MagStat;        // Magnetic switch state [0: no magnet, 1, 2]
    uint8_t bRegularTransmit; // MSB flag of u8MagStat
};

If .u8palpcb == E_PAL_PCB::MAG, retrieve PalMag data for the open-close sensor PAL.

get_PalAmb()

PalAmb get_PalAmb()

// AMB
struct PalAmb : public PalBase {
    uint16_t u16Volt;       // Module voltage [mV]
    int16_t i16Temp;        // Temperature (value ×100)
    uint16_t u16Humd;       // Humidity (value ×100)
    uint32_t u32Lumi;       // Illuminance (approx. lux)
};

If .u8palpcb == E_PAL_PCB::AMB, retrieve PalAmb data for the environmental sensor PAL.

get_PalMot()

PalMot get_PalMot()

// MOT
struct PalMot : public PalBase {
    uint16_t u16Volt;  // Module voltage [mV]
    uint8_t u8samples; // Number of samples
    uint8_t u8sample_rate_code; // Sample rate (0: 25Hz, 4: 100Hz)
    int16_t i16X[16]; // X-axis
    int16_t i16Y[16]; // Y-axis
    int16_t i16Z[16]; // Z-axis
};

If .u8palpcb == E_PAL_PCB::MOT, retrieve PalMot data for the motion sensor PAL.

get_PalEvent()

PalEvent get_PalEvent()

// PAL event
struct PalEvent {
    uint8_t b_stored;        // True if event information is available
    uint8_t u8event_source;  // Event source
    uint8_t u8event_id;      // Event ID
    uint32_t u32event_param; // 24-bit event parameter
};

If .is_PalEvent() is true, retrieve PalEvent data.

8 - smplbuf

Container class with array structure
This is a container class based on an internal array structure. The maximum buffer size is defined at initialization, and within this range it behaves as a variable-length array.
template <typename T, int N> smplbuf_local
template <typename T> smplbuf_attach
template <typename T> smplbuf_heap

smplbuf is a container class that provides array operations for a memory region specified by the element type T and the memory allocation method alloc. Since specifying alloc directly is cumbersome, aliases are defined using using.

Below is an example of object declaration. After declaration, call an initialization method. Each object starts with a maximum size of 128 bytes and a current size of 0. Expand the size as needed during use.

// Array area is a fixed array as a class member variable
smplbuf_local<uint8_t, 128> b1;

// Array area references an existing buffer
uint8_t buf[128];
smplbuf_attach<uint8_t> b2(;

// Array area is allocated on the heap
smplbuf_heap<uint8_t> b3;

// Initialization (if defined globally, do this in setup())
void setup() {
    b1.init_local();
    b2.attach(buf, 0, 128);
    b3.init_heap(128);
}

// Inside a processing function
void some_func() {
    smplbuf_local<uint8_t, 128> bl;
    // Can be omitted if smplbuf_local is defined locally

    bl.push_back('a');
}

Aliases are available for the uint8_t type only.

template <int N>
smplbuf_u8
// smplbuf<uint8_t, alloc_local<uint8_t, N>>

smplbuf_u8_attach
// smplbuf<uint8_t, alloc_attach<uint8_t>>

smplbuf_u8_heap
// smplbuf<uint8_t, alloc_heap<uint8_t>>

You can access elements using the [] operator like a normal array, and also use iterators.

void begin() { // begin() runs only once at startup
  smplbuf_u8<32> b1;
  b1.reserve(5); // Initializes 5 bytes of usable area (accessible via b1[0..5])

  b1[0] = 1;
  b1[1] = 4;
  b1[2] = 9;
  b1[3] = 16;
  b1[4] = 25;

  for(uint8_t x : b1) { // Loop using .begin() and .end() implicitly
    Serial << int(x) << ",";
  }
}

The push_back() method is defined, allowing algorithms that append data to the end.

Declaration and Initialization

smplbuf_local<T,N>()
smplbuf_local<T,N>::init_local()

smplbuf_attach<T>(T* buf, uint16_t size, uint16_t N)
smplbuf_attach<T>::attach(T* buf, uint16_t size, uint16_t N)

smplbuf_heap<T>()
smplbuf_heap<T>::init_heap(uint16_t N)

// Example
// Fixed-size internal array
smplbuf_local<uint8_t, 128> b1;
b1.init_local();

// Using an existing array
uint8_t buf[128];
smplbuf_attach<uint8_t> b2;
b2.attach(buf, 0, 128);

// Allocated on the heap
smplbuf_heap<uint8_t> b3;
b3.init_heap(128);

Declares a container of type T and size N. Call an initialization method after declaration.

smplbuf_local allocates memory using an internal fixed array. Initialization via constructor is also supported.

smplbuf_attach requires the pointer to the buffer T* buf, the initial size size, and the maximum size N. Initialization via constructor is also supported.

smplbuf_heap allocates memory in the heap (a memory region that cannot be released but can be allocated at runtime). Since the memory cannot be freed once allocated, it is typically used as a global object. Memory allocation is done via init_heap(). Allocation via constructor is not supported; always use init_heap().

Initializer List

void in_some_func() {
    smplbuf_local<uint8_t, 5> b1;
    b1.init_local();

    b1 = { 0, 1, 2, 3, 4 };

    smplbuf_local<uint8_t, 5> b2{0, 1, 2, 3, 4};
}

Members can be initialized using an initializer list { ... }. Except for local declarations of smplbuf_local, you must call an initialization method first.

  • As the right-hand value of an assignment (smplbuf_local, smplbuf_attach, smplbuf_heap)
  • Constructor (for local declarations of smplbuf_local, not allowed in global scope)

Methods

append(), push_back(), pop_back()

inline bool append(T&& c)
inline bool append(const T& c)
inline void push_back(T&& c)
inline void push_back(const T& c)
inline void pop_back()

Adds the member c to the end. append() returns a bool, which is false if the buffer is full and the element cannot be added.

pop_back() removes the last entry. Note that it does not clear the content.

empty(), size(), capacity()

inline bool empty()
inline bool is_end()
inline uint16_t size()
inline uint16_t capacity()

empty() returns true if the array has no elements. Conversely, is_end() returns true when the array is full.

size() returns the number of elements in the array.

capacity() returns the maximum number of elements the array can hold.

reserve(), reserve_head(), redim()

inline bool reserve(uint16_t len)
inline void reserve_head(uint16_t len)
inline void redim(uint16_t len)

reserve() expands the array size. The newly allocated region is initialized by default.

reserve_head() reserves space at the head of the array that is not accessible via the container. This can be used, for example, when accessing a subarray after skipping the header of a packet payload. To restore access to the reserved region, provide the same negative value used during reservation.

redim() changes the size of the active region. Unlike reserve(), it does not initialize unused areas.

operator []

inline T& operator [] (int i)
inline T operator [] (int i) const

Accesses an element by index.

If a negative value is given to i, the element is accessed from the end. -1 refers to the last element, -2 to the second last, and so on.

Output to mwx::stream

Array objects of type uint8_t (smplbuf<uint8_t, *>) can be directly output to derived objects of mwx::stream.

<< Operator

template <class L_STRM, class AL>
	mwx::stream<L_STRM>& operator << (
			mwx::stream<L_STRM>& lhs, mwx::_smplbuf<uint8_t, AL>& rhs)

//例
smplbuf_u8<128> buf;
buf.push_back('a');
buf.push_back('b');
buf.push_back('c');

Serial << buf;
// Output: abc

Outputs the byte array to a derived object of mwx::stream, such as Serial.

to_stream()

inline std::pair<T*, T*> to_stream()

// Example
smplbuf_u8<128> buf;
buf.push_back('a');
buf.push_back('b');
buf.push_back('c');

Serial << buf.to_stream();
// Output: 0123

Used for stream output purposes. This method is used in the implementation of the << operator.

Generating Data with mwx::stream

mwx::stream provides functions and operators such as the << operator and printfmt() method for outputting byte arrays to a stream. You can use a smplbuf array of uint8_t as a stream output target.

There are two methods.

8.1 - get_stream_helper()

Helper object for using mwx::stream
Use operators and methods provided by mwx::stream via the stream_helper that refers to a uint8_t smplbuf array.
smplbuf_u8<32> b;
auto&& bs = b.get_stream_helper(); // helper object

// Generate data sequence
uint8_t FOURCHARS[]={'A', 'B', 'C', 'D'};
bs << FOURCHARS;
bs << ';';
bs << uint32_t(0x30313233); // "0123"
bs << format(";%d", 99);

Serial << b << crlf; // output to Serial via smplbuf_u8<32> class

// Result: ABCD;0123;99

Since the type name of the helper object can be long, it is resolved using auto&&. You can use interfaces defined by mwx::stream, such as the << operator, with this object.

The created helper object bs starts reading and writing from the head of the base array b. If at the end of the array, data is appended using append(). The position advances with each read/write operation.

The helper function supports the >> operator for reading.

// ...continued from the above example
// ABCD;0123;99 <- stored in b

// Variables to store read data
uint8_t FOURCHARS_READ[4];
uint32_t u32val_read;
uint8_t c_read[2];

// Read using >> operator
bs.rewind();                // rewind position to the start
bs >> FOURCHARS_READ;      // 4 characters
bs >> mwx::null_stream(1); // skip 1 character
bs >> u32val_read;         // 32-bit data
bs >> mwx::null_stream(1); // skip 1 character
bs >> c_read;              // 2 characters

// Display results
Serial << crlf << "4chars=" << FOURCHARS_READ;
Serial << crlf << format("32bit val=0x%08x", u32val_read);
Serial << crlf << "2chars=" << c_read;

// 4chars=ABCD
// 32bit val=0x30313233
// 2chars=99

8.2 - smplbuf_strm_u8

Type that allows direct use of stream methods
The smplbuf_strm_u8??? type for uint8_t also implements the stream interface, allowing use of several stream-related methods.
// smplbuf_strm_u8<N> : local allocation
template <int N> using smplbuf_strm_u8
  = _smplbuf_stream<uint8_t, mwx::alloc_local<uint8_t, N>>;

// smplbuf_strm_u8_attach : attaches to existing buffer
using smplbuf_strm_u8_attach
  = mwx::_smplbuf_stream<uint8_t, mwx::alloc_attach<uint8_t>>;

// smplbuf_strm_u8_heap : heap allocation
using smplbuf_strm_u8_heap
  = mwx::_smplbuf_stream<uint8_t, mwx::alloc_heap<uint8_t>>;

// Definition of << operator
template <class L_STRM, class ALOC>
mwx::stream<L_STRM>& operator << (
        mwx::stream<L_STRM>& lhs,
        mwx::_smplbuf_stream<uint8_t, ALOC>& rhs)
{
		lhs << rhs.to_stream();
		return lhs;
}

Example

smplbuf_strm_u8<128> sb1;

sb1 << "hello";
sb1 << uint32_t(0x30313233);
sb1 << format("world%d",99);
sb1.printfmt("Z!");

Serial << sb1;
// hello0123world99Z!

9 - smplque

Container class with FIFO queue structure
This is a container class with FIFO queue structure.
template <typename T, int N, class Intr> smplbuf_local
template <typename T, class Intr> smplbuf_attach
template <typename T, class Intr> smplbuf_heap

smplque is a container class that provides FIFO queue operations on a memory region specified by the element type T and the memory allocation method alloc. Since specifying alloc can be complicated, alias definitions using using are provided.

You can register a class Intr that sets interrupt disabling settings at declaration. If not specified, the default behavior will not perform any interrupt control.

Here are examples of object declarations. Initialization methods are called right after declaration. In all cases, the maximum size immediately after initialization is 128 bytes, and the initial size is 0, meaning nothing is stored. The maximum size cannot be changed.

void some_func() {
  // Uses internal fixed-size array
  smplque_local<uint8_t, 128> q1;

  // Uses an existing array
  uint8_t buf[128];
  smplque_attach<uint8_t> q2;

  // Allocates on the heap
  smplque_heap<uint8_t> q3;
}

void setup() {
  // Initialize global objects in setup()
  q1.init_local();
  q2.attach(buf, 128);
  q3.init_heap(128);
}

void some_func() {
  // Local smplque_local can omit init_local()
  smplque_local<uint8_t, 128> q_local;
  ..
}

As a FIFO queue, it is operated using methods such as push(), pop(), and front().

void begin() { // begin() runs only once at startup
	smplque_local<int, 32> q1;

	q1.push(1);
	q1.push(4);
	q1.push(9);
	q1.push(16);
	q1.push(25);

	while(!q1.empty()) {
		Serial << int(q1.front()) << ',';
		q1.pop();
	}
	// output -> 1,4,9,16,25,
}

Access using iterators is also possible.

void begin() { // begin() runs only once at startup
	smplque_local<int, 32> q1;
	q1.init_local();

	q1.push(1);
	q1.push(4);
	q1.push(9);
	q1.push(16);
	q1.push(25);

	// Using iterator
	for(int x : q1) {
		Serial << int(x) << ',';
	}

	// Using STL algorithms
	auto&& minmax = std::minmax_element(q1.begin(), q1.end());
	Serial <<  "min=" << int(*minmax.first)
		     << ",max=" << int(*minmax.second);
	// output -> 1,4,9,16,25,min=1,max=25[]
}

Declaration and Initialization

smplbuf_local<T,N>
smplbuf_local<T,N>::init_local()

smplbuf_attach<T>
smplbuf_attach<T>::attach(T* buf, uint16_t N)

smplbuf_heap<T>
smplbuf_heap<T>::init_heap(uint16_t N);

// Example
// Uses internal fixed-size array
smplque_local<uint8_t, 128> q1;
q1.init_local();

// Uses an existing array
uint8_t buf[128];
smplque_attach<uint8_t> q2;
q2.attach(buf, 128);

// Allocates on the heap
smplque_heap<uint8_t> q3;
q3.init_heap(128);

Declare a container of type T and size N. After declaration, call the initialization method.

smplque_local allocates space with an internal fixed-size array. It can also be initialized using a constructor.

smplque_attach requires the pointer to the buffer to use (T* buf), the initial size of the array, and the maximum size N. It can also be initialized using a constructor.

smplque_heap allocates memory in the HEAP area (memory that cannot be released but can be allocated at any time). Since it cannot be released once allocated, it is usually defined in the global scope. The memory allocation is performed by calling init_heap(). Memory allocation via constructor is not possible. Always call init_heap() to use it.

Methods

push(), pop(), front(), back()

inline void push(T&& c)
inline void push(T& c)
inline void pop()
inline T& front()
inline T& back()

inline T& pop_front()

push() adds an entry to the queue.

pop() removes an entry from the queue.

front() accesses the first entry (the one added first).

back() accesses the last entry (the one added last).

pop_front() returns the first entry and simultaneously removes it from the queue.

empty(), size(), is_full()

inline bool empty()
inline bool is_full()
inline uint16_t size()
inline uint16_t capacity()

empty() returns true if the queue has no elements. is_full() returns true when the queue is full.

size() returns the number of elements currently in the queue.

capacity() returns the maximum number of elements the queue can hold.

clear()

inline void clear()

Removes all elements from the queue.

operator []

inline T& operator[] (int i)

Accesses the element at index i. 0 is the first added element.

Iterator

inline smplque::iterator begin()
inline smplque::iterator end()

Returns iterators via begin() and end(). The beginning iterator points to the first element added to the queue. By using iterators, you can use range-based for loops and algorithms.

As an advanced usage, see iterator access focusing on specific members of the axis_xyzt structure.

10 - Input/Output Stream

Base class for handling input/output streams
This is a base class for handling input/output streams.
  • Provides interfaces to several classes (Serial, Wire, SPI, smplbuf) using polymorphism with the CRTP (Curiously Recurring Template Pattern) method.
    • In CRTP, derived classes are defined as template class Derived : public stream<Derived>;, allowing the base class to also refer to methods of the derived class.
  • This class defines common processing such as the print method and the << operator, and calls methods like write() implemented in derived classes, achieving an implementation similar to using virtual functions.

Interface (Implemented in Derived Classes)

Derived classes implement the following functions.

available()

int available()

// example
while(Serial.available()) {
  int c = Serial.read();
  // ... any
}

Returns 1 if input exists, 0 if not.

ParameterDescription
Return int0: No data 1: Data available

flush()

void flush()

// example
Serial.println("long long word .... ");
Serial.flush();

Flushes the output (waits until output is complete).

read()

int read()

// example
int c;
while (-1 != (c = read())) {
    // any
}

Reads one byte of data from the stream. Returns -1 if no data is available.

write()

size_t write(int c)

// example
Serial.write(0x30);

Outputs one byte to the stream.

ParameterDescription
nThe character to output.
Return size_t1 if output succeeded, 0 if failed.

vOutput()

static void vOutput(char out, void* vp)

A static function that outputs one byte. Since it is not a class method, member variables cannot be used. Instead, a pointer to the class instance is passed as the parameter vp.

This static function is used internally and passed as a function pointer for one-byte output to fctprintf(). It is used to implement methods like print.

ParameterDescription
outThe character to output
vpPointer to the class instance
Usually cast back to the original class to call the write() method

Interface

putchar()

void mwx::stream::putchar(char c)

// example
Serial.putchar('A');
// result -> A

Outputs one byte.

size_t print(T val, int base = DEC) // T: integer type
size_t print(double val, int place = 2)
size_t print(const char*str)
size_t print(std::initializer_list<int>)

// example
Serial.print("the value is ");
Serial.print(123, DEC);
Serial.println(".");
// result -> the value is 123.

Serial.print(123.456, 1);
// result -> 123.5

Serial.print({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Performs various formatted outputs.

ParameterDescription
valThe numeric type to format and output
baseOutput format BIN binary / OCT octal / DEC decimal / HEX hexadecimal
placeNumber of decimal places
Return size_tNumber of bytes written

printfmt()

size_t printfmt(const char* format, ...);

// example
Serial.printfmt("the value is %d.", 123);
// result -> the value is 123.

Outputs formatted output in printf style.

See TWESDK/TWENET/current/src/printf/README.md

operator <<

// examples
Serial << "this value is" // const char*
       << int(123)
       << '.';
       << mwx::crlf;
// result -> this value is 123.

Serial << fromat("this value is %d.", 123) << twe::crlf;
// result -> this value is 123.

Serial << mwx::flush; // flush here

Serial << bigendian(0x1234abcd);
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Serial << int(0x30) // output 0x30=48, "48"
       << '/'
       << uint8_t(0x31); // output '1', not "48"
// result -> 48/1

smplbuf<char,16> buf = { 0x12, 0x34, 0xab, 0xcd };
Serail << but.to_stream();
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Seiral << make_pair(buf.begin(), buf.end());
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.

Serial << bytelist({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
Argument TypeDescription
charOutputs one byte (does not format as a number)
intOutputs integer (printf “%d”)
doubleOutputs number (printf “%.2f”)
uint8_tOutputs one byte (same as char type)
uint16_tOutputs 2 bytes (big endian order)
uint32_tOutputs 4 bytes (big endian order)
const char*``uint8_t*``const char[S]Outputs up to the terminating character. The terminator is not included in output. (S is fixed array size)
uint8_t[S]Outputs S bytes of the array as is. (S is fixed array size)
format()Outputs in printf format
mwx::crlfOutputs newline CRLF
mwx::flushFlushes output
bigendian()Outputs numeric types in big endian order (rvalue)
std::pair<T*, T*>Pair containing begin(), end() pointers of byte type. Can be generated by make_pair. T is assumed to be uint8_t. (rvalue)
bytelist()Outputs byte sequence using std::initializer_list
smplbuf<uint8_t,AL>&Outputs contents of an array class of type uint8_t. ALC is memory allocation method.
smplbuf<uint8_t, AL>::to_stream()Outputs data of smplbuf<T>
T is uint8_t type, AL is memory allocation method.

set_timeout(), get_error_status(), clear_error_status()

uint8_t get_error_status()
void clear_error_status()
void set_timeout(uint8_t centisec)

// example
Serial.set_timeout(100); // Set timeout to 1000ms
uint8_t c;
Serial >> c;

Manages input timeout and errors using the >> operator.

Specify the timeout duration with set_timeout(), and perform input with the >> operator. If input is not obtained within the specified time, error value can be read with get_error_status(). Clear error status with clear_error_status().

Argument TypeDescription
centisecSets timeout duration in 1/10 second units. Specifying 0xff disables timeout.

Error Values

ValueMeaning
0No error
1Error status

operator >>

inline D& operator >> (uint8_t& v)
inline D& operator >> (char_t& v)
template <int S> inline D& operator >> (uint8_t(&v)[S])
inline D& operator >> (uint16_t& v)
inline D& operator >> (uint32_t& v)
inline D& operator >> (mwx::null_stream&& p)

//// Example
uint8_t c;

the_twelite.stop_watchdog(); // Stop the watchdog
Serial.set_timeout(0xFF); // No timeout

// Read one byte
Serial >> c;
Serial << crlf << "char #1: [" << c << ']';

// Discard bytes
Serial >> null_stream(3); // Discard 3 bytes
Serial << crlf << "char #2-4: skipped";

// Read 4 bytes (fixed-length array of uint8_t only)
uint8_t buff[4];
Serial >> buff;
Serial << crlf << "char #5-8: [" << buff << "]";

Performs input.

Below are the types that can be read and stored.

Argument TypeDescription
uint8_t, char_tReads one byte
uint16_tReads 2 bytes (big endian order)
uint32_tReads 4 bytes (big endian order)
uint8_t[S]Reads S bytes (S is fixed array size)
null_stream(int n)Discards n bytes

10.1 - mwx::mwx_format

printf format input
This is a helper class that writes format specifiers for the << operator of mwx::stream.

Within the library, it is aliased as Using format=mwx::mwx_format;.

Serial << format("formatted print: %.2f", (double)3123 / 100.) << mwx::crlf;

// formatted print: 31.23[newline]
  • Store the argument list received by the constructor into internal class variables using parameter pack expansion
  • When operator << is called, call fctprintf() and write data to the stream

Constructor

format(const char *fmt, ...)

The constructor saves the format pointer and parameters. The following << operator call interprets the format and performs output processing.

ParameterDescription
fmtFormat string. See TWESDK/TWENET/current/src/printf/README.md
...Parameters corresponding to the format string.
※ The maximum number is 4; using 5 or more parameters will cause a compile error. ※ Consistency with the format is not checked, so unsafe for inconsistent input.

10.2 - mwx::bigendian

Data output in big-endian order
This is a helper class that outputs numeric types as big-endian byte sequences for the << operator of mwx::stream.
Serial << mwx::bigendian(0x1234abcdUL);

// output binary -> 0x12 0x34 0xab 0xcd

Constructor

template <typename T>
bigendian::bigendian(T v)
ParameterDescription
vA value of type uint16_t or uint32_t

10.3 - mwx::crlf

Output of newline code
An instance of a helper class to output newline code (CR LF) for the << operator of mwx::stream.
Serial << "hello world!" << mwx::crlf;

10.4 - mwx::flush

Force output of the output buffer
Flushes the output buffer of mwx::stream.

An instance of a helper class that calls the flush() method.

for (int i = 0; i < 127; ++i) {
    Serial << "hello world! (" << i << ")" << twe::endl << twe::flush;
}
  • In the case of a serial port, polling wait is performed until output is complete
  • In the case of an mwx::simpbuf buffer, 0x00 is output at the end (size does not change)

10.5 - stream_helper

Helper object
stream_helper is a helper object that provides the mwx::stream interface. It generates a helper object that references a data class, and performs data input/output through the helper object.

Below, a helper object bs is generated from the array b of smplbuf, and data input is performed using the mwx::stream::operator <<() operator.

smplbuf_u8<32> b;
auto&& bs = b.get_stream_helper(); // Helper object

// Generate data sequence
uint8_t FOURCHARS[]={'A', 'B', 'C', 'D'};
bs << FOURCHARS;
bs << ';';
bs << uint32_t(0x30313233); // "0123"
bs << format(";%d", 99);

Serial << b << crlf; // Output to Serial is via smplbuf_u8<32> class

// Result: ABCD;0123;99

Overview

stream_helper behaves as if the data array is a stream.

Internally, it keeps track of the read/write position within the data array. It behaves as follows:

  • When reading or writing, the read/write position moves to the next position.
  • After reading the last data or appending data to the end, the read/write position becomes the end position.
  • When the read/write position is at the end,
    • available() returns false.
    • Reading is not possible.
    • Writing appends data if within writable range.

Creating stream_helper

stream_helper is created from member functions of data classes (smplbuf, EEPROM).

auto&& obj_helper = obj.get_stream_helper()
// obj is an object of a data class, and obj_helper's type is long, so it is received with auto&&.

Methods

rewind()

void rewind()

Moves the read/write position to the beginning.

seek()

int seek(int offset, int whence = MWX_SEEK_SET)

Sets the read/write position.

whencePosition set
MWX_SEEK_SETSets from the beginning. offset of 0 means the same as rewind().
MWX_SEEK_CURMoves by offset from the current position.
MWX_SEEK_ENDSets to the end position. offset of 0 sets to the end. -1 moves to the last character.

tell()

int tell()

Returns the read/write position. Returns -1 if at the end position.

available()

int available()

Returns 0 if the read/write position is at the end. Otherwise, returns a non-zero value.

11 - SM_SIMPLE State Machine

State management
SM_SIMPLE is used in sample code for handling state transitions, waiting for timeouts, transmission completion, and similar processing.

Here is a basic example of SM_SIMPLE.

##include <SM_SIMPLE>

enum class STATE : uint8_t {
	INIT = 0,
	SENSOR,
	TX,
	TX_WAIT_COMP,
	GO_SLEEP
};

SM_SIMPLE<STATE> step;

begin() {
  ...
  step.init(); // Initialization
}

loop() {
  do {
    switch(step.state()) {
    case STATE::INIT:
      ...
      step.next(STATE::SENSOR);
    break;

    case STATE::SENSOR:
      ...
      step.next(STATE::TX);
    break;

    case STATE::TX:
      if (/* Transmission request successful */) {
        step.set_timeout(100); // Set timeout
        step.clear_flag(); // Waiting for completion

        step.next(STATE::TX_WAIT_COMP);
      }
    break;

    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) the_twelite.reset_system(); // Timeout
      if (step.is_flag_ready()) sleepNow(); // Flag was set
    break;

    ...
    }
  } while(step.b_more_loop());
}

void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
	step.set_flag(ev.bStatus);
}

void sleepNow() {
	step.on_sleep(false); // Reset state machine
  the_twelite.sleep(10000); // 10 sec
}

Explanation

To use SM_SIMPLE, define an enum class that lists the states. In the example above, this is defined as STATE. You create a class object like SM_SIMPLE<STATE> step; using the enum as the template parameter. Then call .setup() on the object to initialize it.

enum class STATE : uint8_t {
	INIT = 0,
	SENSOR,
	TX,
	TX_WAIT_COMP,
	GO_SLEEP
};

SM_SIMPLE<STATE> step;

void setup() {
  step.init();
}

The initial state of SM_SIMPLE has a value of 0, which corresponds to STATE::INIT in the above example. To get the current state, use .state() and use it in a switch statement inside a do while loop as shown.

loop() {
  do {
    switch(step.state()) {
    case STATE::INIT: // State with value 0
    ...

To transition states, call .next(). When the state changes, b_more_loop() returns true and the do while loop executes again. In the example, calling .next(STATE::TX) from STATE::SENSOR causes another loop iteration, executing the case STATE::TX: block. If the state does not change, the loop exits, and loop() ends until the next call.

  do {
    switch(step.state()) {
    ...
    case STATE::SENSOR:
      ...
      step.next(STATE::TX); // (1) State transition
    break;

    case STATE::TX: // (3) Called on second loop
      if (/* Transmission request successful */) {
      ...
    }
  } while (b_more_loop()); // (2) Loop continuation check

To wait for completion of processing (e.g., transmission complete), call .clear_flag() and later call .set_flag(uint32_t) from a callback or similar to signal completion. You can retrieve the passed uint32_t value using .get_flag_value().

To handle timeouts, call .set_timeout(uint32_t) to store the current time, then use .is_timeout() to check if the timeout duration has elapsed.

    case STATE::TX:
      if (/* Transmission request successful */) {
        step.set_timeout(100); // Set timeout
        step.clear_flag(); // Wait for completion

        step.next(STATE::TX_WAIT_COMP);
      }
    break;

    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) ...; // Timeout
      if (step.is_flag_ready()) ...; // Flag was set
    break;
...

// Transmission complete event
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
	step.set_flag(ev.bStatus); // Set the flag
}

To continue using SM_SIMPLE after waking from sleep, be sure to call .on_sleep(bool) before going to sleep. If you pass false, the state machine resets to state 0 upon waking; if true, it resumes from the pre-sleep state.

void sleepNow() {
	step.on_sleep(false); // Reset state machine
  the_twelite.sleep(10000); // 10 sec
}

Source Code

Below is the source code for SM_SIMPLE.

// very simple class to control state used in loop().
template <typename STATE>
class SM_SIMPLE {
	uint32_t _u32_flag_value;  // optional data when flag is set.
	uint32_t _ms_start;		// system time when start waiting.
	uint32_t _ms_timeout;	// timeout duration

	STATE _step;			  // current state
	STATE _step_prev;		// previous state
	bool_t _b_flag; 		// flag control.
public:
	// init
	void setup() { memset(this, 0, sizeof(SM_SIMPLE)); }
	// call befoer sleeping (save state machine status)
	void on_sleep(bool b_save_state = false) {
		STATE save = _step;
		setup();
		if(b_save_state) _step = _step_prev = save;
	}

	// state control
	void next(STATE next) { _step = next; } // set next state
	STATE state() { return _step; } // state number
	bool b_more_loop() { // if state is changed during the loop, set true
		if (_step != _step_prev) { _step_prev = _step; return true; }
		else return false;
	}

	// timeout control
	void set_timeout(uint32_t timeout) {
		_ms_start = millis();
		_ms_timeout = timeout;
	}
	bool is_timeout() { return (millis() - _ms_start) >= _ms_timeout; }

	// flag control
	void clear_flag() { _b_flag = false; _u32_flag_value = 0; }
	void set_flag(uint32_t u32_flag_value = 0) {
		_b_flag = true;
		_u32_flag_value = u32_flag_value; }
	uint32_t get_flag_value() { return _u32_flag_value; }
	bool is_flag_ready() { return _b_flag; }
};
  • Contents may vary depending on the version.
  • The source is located in the MWX library source folder under SM_SIMPLE.hpp.