This is the multi-page printable view of this section. Click here to print...
Classes
- 1: MWX_APIRET
- 2: alloc
- 3: axis_xyzt
- 4: packet_rx
- 5: packet_tx
- 6: serparser
- 7: pktparser
- 7.1: E_PKT
- 7.2: identify_packet_type()
- 7.3: TwePacket
- 7.3.1: TwePacketTwelite
- 7.3.2: TwePacketIO
- 7.3.3: TwePacketUART
- 7.3.4: TwePacketPAL
- 8: smplbuf
- 8.1: get_stream_helper()
- 8.2: smplbuf_strm_u8
- 9: smplque
- 10: Input/Output Stream
- 10.1: mwx::mwx_format
- 10.2: mwx::bigendian
- 10.3: mwx::crlf
- 10.4: mwx::flush
- 10.5: stream_helper
- 11: SM_SIMPLE State Machine
1 - MWX_APIRET
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
smplbuf
, smplque
) to specify or allocate internal memory regions.Class Name | Description |
---|---|
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
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
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.
<NWK_SIMPLE>
.Methods
get_payload()
smplbuf_u8_attach& get_payload()
Retrieves the data payload of the packet.
<NWK_SIMPLE>
, the payload begins with a header specific to <NWK_SIMPLE>
. The container returned is a subarray excluding this header. If you wish to access the header as well, refer to the tsRxDataApp
structure using get_psRxDataApp()
.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).
LQI indicates the quality of wireless communication. It is expressed as a number from 0 to 255.
As a rough guideline, the values can be interpreted as follows: below 50 (poor, below -80 dBm), 50–100 (fair), 100–150 (good), and above 150 (very close to the antenna). These are merely rough indicators.
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.
Value | Description |
---|---|
The serial number is used as the destination. | Specifies the serial number as the destination. |
0x00 -0xFF | An 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.
Value | Description |
---|---|
mwx::NETWORK::LAYERED | Packet from <NWK_LAYERED> |
mwx::NETWORK::SIMPLE | Packet from <NWK_SIMPLE> |
mwx::NETWORK::NONE | Packet not transmitted via a network (e.g., App_Twelite) |
Other | Error or unidentified packet |
5 - packet_tx
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
to0xEF
indicates an 8-bit logical ID.0xFE
is broadcast to child nodes (0x01
–0xEF
), and0xFF
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 packetu8count+1
times. Theforce_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.
Due to internal processing, the transmission may not start exactly at the specified timing. There is also jitter from the IEEE802.15.4 protocol. This timing jitter is generally useful for avoiding collisions in many systems.
Strict timing control is considered an exceptional use case due to the nature of IEEE802.15.4.
<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, usetx_addr(0xFF)
for general broadcast ortx_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
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.
Constant | Format Type |
---|---|
uint8_t PARSER::ASCII = 1 | ASCII format |
uint8_t PARSER::BINARY = 2 | Binary 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
Section | Byte Count (Original) | Byte Count (Format) | Description |
---|---|---|---|
Header | 1 | : (0x3A) colon character | |
Data | N | 2N | Each byte is represented as two ASCII characters (A–F in uppercase). For example, 0x1F is represented as 1 (0x31) and F (0x46). |
Checksum | 2 | The 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. | |
Footer | 2 | [CR] (0x0D) and [LF] (0x0A) characters |
Binary Format
Normally, use ASCII format.
Binary format is more efficient for microcontroller-to-microcontroller communication, but it requires specialized terminals and checksum handling for manual testing, making it more difficult than ASCII.
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
Section | Byte Count (Original) | Byte Count (Format) | Description |
---|---|---|---|
Header | 2 | Use 0xA5 0x5A | |
Data Length | 2 | Two bytes in big-endian format with MSB (0x8000) set. For example, if the data length is 8 bytes, use 0x80 0x08 . | |
Data | N | N | Specifies the original data |
Checksum | 1 | XOR 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_attach | Use an existing buffer specified via begin() |
serparser_local<N> | Allocate an internal buffer of N bytes |
serparser_heap | Allocate 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.
parse()
again will reset the parser to the intermediate (parsing) state.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
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
The following packet types are supported:
Name | Description |
---|---|
PKT_ERROR | Used before parsing or when the packet type cannot be determined. The TwePacket does not contain valid data. |
PKT_TWELITE | Parsed result of the 0x81 command from the standard application App_Twelite. |
PKT_PAL | Parsed format of TWELITE PAL serial output. |
PKT_APPIO | Parsed UART messages from the remote control application App_IO. |
PKT_APPUART | Parsed extended format from the serial communication application App_UART. |
PKT_APPTAG | Parsed UART messages from the wireless tag application App_Tag. Sensor-specific parts are not interpreted but returned as raw payload bytes. |
PKT_ACT_STD | Output format used by samples of Act. |
7.2 - identify_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
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;
};
pktparser
, to retrieve minimal shared information such as address data.7.3.1 - TwePacketTwelite
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
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
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
.
parse<TwePacketUART>()
will return E_PKT::PKT_ERROR
. To inspect the contents, refer directly to the original byte sequence.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
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
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()
.
init_local()
, attach()
, or init_heap()
) at the start of execution (e.g., in setup()
).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.
- Use the helper object generated by
get_stream_helper()
. - Use the smplbuf class that inherits
mwx::stream
.
8.1 - get_stream_helper()
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
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
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.
init_local()
, attach()
, or init_heap()
during the early stage of execution (preferably in setup()
).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
- 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.
- In CRTP, derived classes are defined as
- This class defines common processing such as the
print
method and the<<
operator, and calls methods likewrite()
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.
Parameter | Description |
---|---|
Return int | 0: 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.
Parameter | Description |
---|---|
n | The character to output. |
Return size_t | 1 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
.
Parameter | Description |
---|---|
out | The character to output |
vp | Pointer 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.
print()
, println()
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.
Parameter | Description |
---|---|
val | The numeric type to format and output |
base | Output format BIN binary / OCT octal / DEC decimal / HEX hexadecimal |
place | Number of decimal places |
Return size_t | Number 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 Type | Description |
---|---|
char | Outputs one byte (does not format as a number) |
int | Outputs integer (printf “%d”) |
double | Outputs number (printf “%.2f”) |
uint8_t | Outputs one byte (same as char type) |
uint16_t | Outputs 2 bytes (big endian order) |
uint32_t | Outputs 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::crlf | Outputs newline CRLF |
mwx::flush | Flushes 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. |
uint8_t, uint16_t, uint32_t
types. When outputting numbers as strings, explicitly cast to int
.uint8_t[S]
type which considers size.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 Type | Description |
---|---|
centisec | Sets timeout duration in 1/10 second units. Specifying 0xff disables timeout. |
Error Values
Value | Meaning |
---|---|
0 | No error |
1 | Error 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.
- Cannot be executed inside
setup()
. - Polling wait is performed, so depending on timeout setting (e.g., no timeout), the watchdog timer may trigger and reset.
Usually, reading is done as follows inside loop()
.
void loop() {
uint8_t c;
while(Serial.available()) {
Serial >> c;
// or c = Serial.read();
switch(c) { ... } // Branch processing based on c value
}
}
Below are the types that can be read and stored.
Argument Type | Description |
---|---|
uint8_t, char_t | Reads one byte |
uint16_t | Reads 2 bytes (big endian order) |
uint32_t | Reads 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
<<
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, callfctprintf()
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.
Parameter | Description |
---|---|
fmt | Format 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. |
fmt
must remain accessible until this object is destroyed.10.2 - mwx::bigendian
<<
operator of mwx::stream
.
Serial << mwx::bigendian(0x1234abcdUL);
// output binary -> 0x12 0x34 0xab 0xcd
Constructor
template <typename T>
bigendian::bigendian(T v)
Parameter | Description |
---|---|
v | A value of type uint16_t or uint32_t |
10.3 - mwx::crlf
<<
operator of mwx::stream
.
Serial << "hello world!" << mwx::crlf;
10.4 - mwx::flush
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
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()
returnsfalse
.- 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.
whence | Position set |
---|---|
MWX_SEEK_SET | Sets from the beginning. offset of 0 means the same as rewind() . |
MWX_SEEK_CUR | Moves by offset from the current position. |
MWX_SEEK_END | Sets 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
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
.