Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Slp_Wk_and_Tx is a template source code for an application which, after a regular wake-up, does something (e.g. acquires sensor data) and sends the result as a wireless packet.
In the form of setup()
and loop()
, conditional branches which are difficult to read in loop()
tend to occur. In this Act, we use SM_SIMPLE state machine in loop()
and simple state transition by switch syntax to improve the visibility of the code.
This act includes
The control structure of a typical intermittent operation (sleep -> wake -> measure -> radio transmission -> sleep)
Generation and transmission procedures for outgoing packets and waiting for completion
After starting up, the system goes through an initialization process and then goes to sleep.
setup()
Initialise
begin()
Run sleep
After waking up from sleep, the state variables are initialized and the actions are performed in the following order
wakeup()
wakes up from sleep, performs each initialization
loop()/INIT->WORK_JOB state
: does some processing (in this Act, the counter is updated every TickCount for 1ms and moves to TX state after a count determined by a random number)
loop()/TX state
: Make a request to send
loop()/WAIT_TX state
: Waiting for transmission completion
loop()/EXIT_NORMAL state
: Run sleep (back to 1.)
loop()/EXIT_FATAL state
:Resetting the module if an error occurs
To send packets, <NWK_SIMPLE>
is included. Also, basic definitions such as application ID are in "Common.h"
.
In order to describe the sequential processing in loop()
, this sample uses the concept of a state machine (state transition). It uses <SM_SIMPLE>
, which summarizes the processing of very simple state transitions.
An enum STATE
is defined in Common.h
for the following states.
Declares an SM_SIMPLE state machine (state transition) using the STATE
state enumeration.
The step
declared here contains functions for managing state, timeouts and waiting for processing.
In this sample we do not process the sensor data, but we prepare dummy data for explanation.
Initializes variables and class objects.
Initialization of the step
state machine
Initialization of the_twelite
class object
Register and initialize the network <NWK_SIMPLE>
(register DEVICE_ID
).
This is followed by the initiation of class objects and hardware.
This is the procedure to start the_twelite
, it didn't appear in act0..4, but you should call it if you set up the_twelite
or register various behaviors.
Called only once, immediately after setup()
. The SleepNow()
function is called to perform the first sleep procedure.
Called immediately after waking up. Here, it initializes the sensor data area and outputs a message on waking.
The above code is a simplified version of the actual code.
This control structure uses the SM_SIMPLE state machine. It is a loop with do..while()
syntax. Inside the loop is a _switch case
_ clause, which splits the process according to the state obtained by .state()
. State transitions call .next()
to rewrite internal variables in the state machine to the new state values.
step.b_more_loop()
is set to true if there is a state transition by .next()
. The purpose of this is to execute the code of the next state (case clause) without escaping loop()
when a state transition occurs.
Below is a description of each state.
Initialises the sensor values of the dummies. One is determined randomly by an add counter and one by a counter stop value.
In the WORK_JOB state, we work on a timer every 1ms; every Tick timer becomes TickTimer.available()
; every Tick timer adds a counter and when it reaches dummy_work_ct_max
, we move to the next state STATE::TX
.
Call the Transmit()
function to request packet transmission. If the request succeeds, the function transits to STATE::WAIT_TXEVENT
and waits for the completion of transmission. Here, we use the timeout and flag functions of the SM_SIMPLE state machine to wait for completion (it is simple to determine the value of a variable by changing its value in the waiting loop).
We don't usually expect a single request to fail, but if it does, it will go to the STATE::EXIT_FATAL
exception handling state.
You should not sleep at this point, as the packet has not yet been sent. In most cases, you should wait for transmission to complete before continuing.
The Transmit()
function returns a MWX_APIRET
object, which holds a bool type success or failure and a value of up to 31 bits. It can be evaluated as a bool
type, so an if
statement will return true
if the send request succeeds, or false
if it fails.
Waiting for completion of transmission is judged by setting the flag of the state machine function with on_tx_comp()
described later. The timeout is judged by the elapsed time since .set_timeout()
was done by calling .is_timeout()
.
Normally you will get a completion notice whether the transmission succeeds or fails, but it will time out and go to STATE::EXIT_FATAL
for exception handling.
Call SleepNow()
to start the sleep process.
As a critical error, a system reset is performed.
Periodic sleep is performed. The sleep time is set to a constant time blur using the random()
function. This is because when the transmission cycles of several devices are synchronized, the failure rate may increase significantly.
Before sleeping, the state of the SM_SIMPLE state machine is set by calling .on_sleep()
.
This function requests to send a wireless packet to the parent device with ID=0x00
. The data to be stored is a four-character identifier (FOURCC
) commonly used in the Act sample, plus the system time [ms] and a dummy sensor value (sensor.dummy_work_ct_now
).
The first step is to get an object that contains the transmitted packets. This object can then be manipulated to set the data and conditions for transmission.
In the mwx library, the if
statement is used to get an object and the bool
decision of the object is true
. Here, a board object is retrieved by the_twelite.network.use<NWK_SIMPLE>()
, and a packet object is retrieved by .prepare_tx_packet()
of the board object. Failure to retrieve the packet object is not normally expected, but when it does occur, it is when the transmit queue is full and the transmit request cannot be accepted. Since this example is for a single transmission only, the error is limited to an unexpected serious problem.
For the resulting pkt
object, set the conditions for transmission (destination, retransmission, etc.) using the <<
operator. tx_addr
specifies the destination of the packet. The tx_addr
specifies the destination of the packet, the tx_retry
specifies the number of retransmissions, and the tx_packet_delay
specifies the transmission delay.
The payload of a packet is an array of smblbuf<uint8_t>
derivatives obtained by pkt.get_payload()
. You can set the value of this array directly, but here we use pack_bytes()
to set the value.
The maximum length of the payload is 91 bytes in the above example, see NWK_SIMPLE packet structure and maximum length for more information.
This function can be specified by a variable number of arguments. The first parameter is an array object obtained from .get_payload()
.
make_pair(FOURCC,4)
: make_pair is from the C++ standard library and creates a std::pair object. It means to write out 4 bytes from the beginning for string type.(This is done to explicitly specify the number of bytes to be written, as the topic of including or excluding the end of an array of string types is confusing)
If uint32_t
type data is specified, 4 bytes of data are written in big-endian order.
The same applies to data of type uint16_t
.
It is also possible to write data using a pointer of type uint8_t
.
The array object obtained from .get_payload()
is an array of size 0 with no data stored in it, but it is expanded by writing data to it (actually, writing data to an internal fixed-length buffer and updating the data size in internal management). The final size is the data size of the payload.
Here we use .begin()
to get a pointer to uint8_t*
, write data using this pointer, and set the last written size with .redim()
.
functions (macros) such as S_OCTET()
, S_WORD()
, and S_DWORD()
are used to write data, for example, S_OCTET(p, 'H')
is the same as *p = 'H'; p++;
.
The last .redim()
is a procedure to change the size of an array without initializing the buffer. Calling .resize()
clears everything to zero.
Finally, .transmit()
is called to request sending. The return value is of the type MWX_APIRET
. After the request, the actual transmission takes place, which may take several to several tens of milliseconds to complete, depending on the transmission parameters and the size of the transmission. On completion, on_tx_comp()
is called.
``MWX_APIRET
is a wrapped class of uint32_t
type, which uses MSB as a failure success flag and 31 bits as data. It is a return type of pkt.transmit()
, and success or failure of the request (cast to bool
type) and the ID are stored in the data part (.get_value()
).
This is a system event that is called when the transmission is complete. Here, it is set to complete by .set_flag()
.
Sample Acts
To help you understand how the ACT works, we have prepared some samples.
The samples can be found in Act_samples in the folder where you installed the MWSDK.
Several samples are provided to help you understand how the act works.
act0..4 is a very simple example, without any radio functions, to give you an idea of the basic structure of Act.
This is an example of a wireless sensor implementation that connects an I2C sensor and sends wireless packets while performing a brief operation by sleeping. Since this is a relatively simple and typical structure, it is recommended that you refer to it after reviewing act0
through act4
.
Typical elements for implementing wireless sensors in TWELITE (simple relay net <NWK_SIMPLE>
, Interactive settings mode <STG_STD>
, I2C sensor handling Wire
, intermittent operation with sleep, etc.).
These are samples of sending or receiving wireless packets, each implemented from a slightly different perspective.
Scratch is a simple code that receives 1 byte commands from the UART, sends them and so on.
Slp_Wk_and_Tx uses a state machine and intermittent operation with sleep, repeating the process of sleep recovery, radio transmission and sleep.
PingPong is a sample of sending packets from one side to the other and receiving packets back by the other side. It contains basic procedures for sending and receiving.
WirelessUART interprets the UART input into ASCII format using serparser and sends it.
Note: App_Wings can also be used to receive wireless packets for the ACTs included in this sample.
Refer to this when implementing your own Receiving Parent Node application.
Parent_MONOSTICK exclusively receives and outputs the reception result to the serial port. The wireless packets in this sample, which are addressed to the parent device (0x00) or to the child device broadcast (0xFE), can be received. It also includes a procedure to add the interactive mode <STG_STD> to Act.
Rcv_Univsl is an example code for universal packets receiver (e.g. TWENET layered tree network, App_Twelite, Act, ...). It alse uses EASTL library for container or some algorithm.
The explanation of the ACT using Interactive settings mode describes the general flow of the program. There are no major differences between the explanations for any of the samples. (Here we quote BRD_I2C_TEMPHUMID)
BRD_I2C_TEMPHUMID executes I2C sensor device read/write commands and wirelessly transmits measurements obtained from I2C sensors. It also uses the Interactive settings mode <STG_STD> to Act.
Setting provides a higher degree of customisation of the interactive mode <STG_STD>.
This sample obtains sensor information from built-in peripherals and external sensor devices.
BRD_I2C_TEMPHUMID executes I2C sensor device read/write commands and wirelessly transmits measurements obtained from I2C sensors. It also uses the Interactive settings mode <STG_STD> to Act.
BRD_APPTWELITE provides bidirectional communication using digital input, analogue input, digital output and analogue output. It also contains the procedure for adding the interactive mode <STG_STD> to Act.
PulseCounter uses a pulse counter function to count the number of pulses detected on the input port, even during sleep, and to transmit this data wirelessly.
PAL_AMB_behavior is a behavioural example: in PAL_AMB the temperature and humidity sensors are called by the code inside the library, but this sample also includes its own procedure for accessing the temperature and humidity sensors via I2C bus.
Although a standard PAL application is written into TWELITE PAL, it is possible to write a description using ACT without using the PAL application. The MWX library provides standard procedures for MOTION SENSE PAL use.
This is a sample for various PAL boards. This sample acquires sensor values, transmits these valuse, and sleeps on the PAL board.
The following is an example of an advanced application, which is a little more complicated to describe than the above ACT.
PAL_AMB_usenap is a sample that aims to save more power by allowing the TWELITE microcontroller to sleep briefly during the sensor's operating time, which can take several tens of milliseconds.
PAL_AMB_behavior is a behavioural example: in PAL_AMB the temperature and humidity sensors are called by the code inside the library, but this sample also includes its own procedure for accessing the temperature and humidity sensors via I2C bus.
PAL_MOT_fifo for continuous acquisition and wireless transmission of samples without interruption, using the accelerometer's FIFO and FIFO interrupts.
Acts with names starting with Unit are intended to introduce features and APIs.
We have put the complete source on Github for the purpose of checking the latest code and the revision history between MWSDK versions. See the links below for more information.
The following items are common settings in the Act sample and are explained below.
The following settings are common to all sample acts.
Application ID 0x1234abcd
Channel 13
Both the application ID and the channel are mechanisms to ensure that the network does not mix with other networks.
Systems with different Application IDs will not mix, even if they are on the same channel. However, if a system with a different Application ID is transmitting frequently, this will cause interference and will have an effect.
The channel determines the frequency used for communication; in principle, 16 channels are available on the TWELITE radio module, and it is not possible to communicate with different channels except in very exceptional cases, which would not be the case in a normal system.
As a common specification of sample acts, the payload (data part) of the packet is prefixed with a 4-byte string (APP_FOURCHAR[]
). This is for explanatory purposes, although one byte is sufficient for species specific identifiers. The inclusion of such system-specific identifiers and data structures is another way to prevent confusion.
Template code.
Set the_twelite
to set the application ID APP_ID
, the radio channel CHANNEL
and the receive presence.
It also generates nwk
and specifies the child machine address 0xFE
. This address means that this is an unnamed child machine that has not been addressed by a child machine.
The addresses that can be set are 0x00: parent unit, 0x01~0xEF: child unit, 0xFE: unspecified child unit address range.
The address to be specified as the transmission destination is 0x00 for the parent machine, 0x01~0xEF for the specified parent machine address, 0xFE for any child machine address, and 0xFF for any address including the parent machine.
It also initializes the Buttons object. This is a chatter-suppressing algorithm by successive references, which determines HI or LOW of the target port (PIN_BTN only) if the value is the same 5 times in 10ms. pack_bits
(N1, N2, ...)
pack_bits(N1, N2, ...)' does 1UL<<N1 | 1UL << N2 | ...
to make a bitmap.
This is the procedure to start the_twelite
, it didn't appear in act0..4, but you should call it if you set up the_twelite
or register various behaviors.
Called only once after setup()
on startup. Only displays a message.
The state is determined by continuous reference to Buttons. When the button state changes, it is output serially.
If Serial.available()
is true
, the input from the serial port is stored. It reads one character from the serial and processes it according to the input character.
t
for wireless transmissionWhen 't
' is input, sending is done. In this sample, the tx_busy flag is used to prevent continuous input.
Send requests are stored in a queue up to a certain number of packets, so it is possible to stack requests in the range of the queue (3 packets).
The following is an example of what happens when you don't check if(!tx_busy)
and type 'tttt
' continuously: the queue fills up at the fourth request and the request fails. The pkt object obtained by .prepare_tx_packet()
in Transmit()
will be false
.
The timing of the transmissions is randomised, so the completion of transmissions is not in the order in which they are requested.
s
to sleep.The system will sleep for 5000ms=5 seconds. After recovery, wakeup()
is executed.
First to be called on sleep wake up. Display of message only.
This is the minimum procedure for making a transmission request.
When you leave this function, the request has not yet been executed. You need to wait a while. In this example we have set a delay of 100-200ms for the start of the transmission, so the transmission will not start until 100ms at the earliest.
Called on completion of a transmission; ev contains the transmission ID and completion status.
When a packet is received, the sender's address information is displayed.
Send a PING wireless packet from one of the two serially connected TWELITEs and receive a PONG wireless packet back from the other.
Two of any of the following.
TWELITE DIP connected to UART with TWELITE R products/TWE-Lite-DIP/index.html), etc.
Include <TWELITE>
in all ACTs. Here, the simple network <NWK_SIMPLE>
should be included.
Sample act common declarations
Prototype declarations for longer processes (sending and receiving), since they are made into functions
Variables for holding data in the application
The general flow of the program is the initial setup of each section and the start of each section.
This object is the core class object for manipulating TWENET.
the_twelite
に設定を反映するには <<
を用います。
Use <<
to reflect the setting in the_twelite
.
TWENET::appid(APP_ID)
to specify the Application ID.
TWENET::channel(CHANNEL)
to specify the channel.
TWENET::rx_when_idle()
Specifies that the receive circuit is open.
The <<, >>
operator is originally a bit shift operator, but it is used differently from its meaning.
Next, register the network.
The first line is written in the same way as the board registration, specifying <>
as <NWK_SIMPLE>
.
The second line specifies <NWK_SIMPLE>
, specifying 0xFE
(WK_SIMPLE
is a Child Node with an unset ID).
The third line specifies the maximum number of relays. This explanation does not touch on relaying, but packets are relayed when operating with multiple units.
Execute the_twelite.begin()
at the end of the setup()
function.
Class object that handles ADCs (analog-to-digital converters).
Initialization Analogue.setup()
. The parameter true
specifies to wait in place until the ADC circuit is stable.
To start the ADC, call Analogue.begin()
. The parameter is a bitmap corresponding to the pin to be ADC'd.
The pack_bits()
function is used to specify the bitmap. It is a function with variable number of arguments, each argument specifies a bit position to be set to 1. For example, pack_bits(1,3,5)
returns the binary value 101010
. This function has the constexpr
specification, so if the parameters are constants only, they are expanded to constants.
The parameters are specified as PIN_ANALOGUE::A1
(ADC0) and PIN_ANALOGUE::VCC
(module supply voltage).
The second parameter is specified as 50
, and the ADC operation is started by default with TickTimer, which is set to
Except for the first time, the ADC is started in an interrupt handler.
Detects changes in DIO (digital input) values; Buttons only detect a change in value after the same value has been detected a certain number of times in order to reduce the effects of mechanical button chattering.
Initialization is done with Buttons.setup()
. The parameter 5 is the number of detections required to determine the value, but it is the maximum value that can be set. Internally, the internal memory is allocated based on this number.
The start is done with Buttons.begin()
The first parameter is the DIO to be detected. The second parameter is the number of detections required to determine the state. The third parameter is the detection interval. Since 10
is specified, the HIGH and LOW states are determined when the same value is detected five times in a row every 10 ms.
The detection of the DIO state in Buttons is done by an event handler. Event handlers are called in the application loop after an interrupt has occurred, so there is a delay compared to interrupt handlers.
Serial objects can be used without initialization or initiation procedures.
Outputs a string to the serial port. mwx::crlf
is a newline character.
Loop function are called as callback functions from the main loop of the TWENET library. The basic description here is to wait for the object to be used to become available and then process it. This section describes the use of some objects used in ACT.
The main loop of the TWENET library processes incoming packets and interrupt information stored in the FIFO queue in advance as events, after which loop()
is called. After exiting loop()
, the CPU enters DOZE mode and waits until a new interrupt occurs with low current consumption.
Therefore, code that assumes the CPU is always running will not work well.
While Serial.available()
is true
, there is input from the serial port. The data is stored in the internal FIFO queue, so there is some leeway, but it should be read out promptly. To read data, call Serial.read()
.
Here, the vTransmit()
function is called to send a PING packet in response to a 't'
key input.
It becomes available at the timing when a change in DIO (digital IO) input is detected, and is read by Buttons.read()
.
The first parameter is a bitmap of the HIGH/LOW of the current DIO, ordered from bit0 to DIO0,1,2,... . and so on, starting from bit 0. For example, for DIO12, HIGH / LOW can be determined by evaluating btn_state & (1UL << 12)
. If the bit is set to 1, it is HIGH.
When the IO state is determined for the first time, MSB (bit31) is set to 1. The initial determination process is also performed when the device returns from sleep.
The vTransmit()
is called at the timing when the button is released except for the initial confirmation. To make the timing of the press (! (btn_state && (1UL << PIN_BTN)))
to invert the condition logically.
This function requests TWENET to send a wireless packet. At the end of this function, the wireless packet is not yet processed. The actual transmission will be completed in a few ms or later, depending on the transmission parameters. This section describes typical transmission request methods.
Get a network object with the_twelite.network.use<NWK_SIMPLE>()
. Use that object to get a pkt
object by .prepare_tx_packet()
.
Here it is declared in the conditional expression of the if statement. The declared pkt
object is valid until the end of the if clause. pkt object gives a response of type bool, which here is true
if there is a free space in TWENET's send request queue and the send request is accepted, or false
if there is no space.
Packets are configured using the <<
operator as in the_twelite
initialization setup.
Specify the destination address in the tx_addr()
parameter. If it is 0x00
, it means that you are the Child Node and broadcast to the Parent Node, and if it is 0xFE
, it means that you are the Parent Node and broadcast to any Child Node.
The tx_retry()
parameter specifies the number of retransmissions. In the example 3
means that the number of retransmissions is 3, i.e., the packet is sent 4 times in total. Sending only one wireless packet may fail a few percent of the time even under good conditions.
tx_packet_delay()
Sets the transmission delay. The first parameter is the minimum wait time to start sending and the second is the maximum wait time. The third is the retransmission interval. The third is the retransmission interval, meaning that a retransmission is performed every 20 ms after the first packet is sent.
Payload means a loaded item, but in wireless packets it is often used to mean "the main body of data to be sent". In addition to the main body of data, the data in a wireless packet also contains some auxiliary information, such as address information.
For correct transmission and reception, please be aware of the data order of the data payload. In this example, the data order is as follows. Construct the data payload according to this data order.
The data payload can contain 90 bytes (actually a few more bytes).
Every byte in an IEEE802.15.4 wireless packet is precious. There is a limit to the amount of data that can be sent in a single packet. If a packet is split, the cost of the split packet is high because it must take into account transmission failures. Also, sending one extra byte consumes energy equivalent to approximately 16 µs x current during transmission, which can be significant, especially for battery-powered applications. {endhint %}
Let's actually build the data structure of the above data payload. The data payload can be referenced as a container of type simplbuf<uint8_t>
via pkt.get_payload()
. In this container, we build the data based on the above specification.
It can be written as above, but the MWX library provides an auxiliary function pack_bytes()
for data payload construction.
The first parameter of pack_bytes
specifies the container. In this case, it is pkt.get_payload()
.
The parameters after that are variable arguments, specifying as many values of the corresponding type in pack_bytes
as needed. The pack_bytes
internally calls the .push_back()
method to append the specified value at the end.
The third line, make_pair()
, is a standard library function to generate std::pair
. This is specified to avoid confusion of string types (specifically, whether or not to include null characters when storing payloads). The first parameter of make_pair()
is the string type (char*
, uint8_t*
, uint8_t[]
, etc.) The second parameter is the number of bytes to store in the payload.
Lines 4, 5, and 6 store values of numeric types (uint8_t
, uint16_t
, uint32_t
). Numeric types such as signed, or even the same numeric type such as char
are cast to the three types listed on the left and submitted.
analogRead()
and analogRead_mv()
get the result of ADC. The former is the ADC value (0..1023) and the latter is the voltage[mv](0..2470). The supply voltage of the module is read internally from the value of the voltage divider resistor, so we use adalogRead_mv()
to perform that conversion.
This completes the packet preparation. Now all that remains is to make a request for transmission.
Packets are sent using the pkt.transmit()
method of the pkt
object.
Although not used in this ACT, the return value contains information about the success or failure of the request and the number corresponding to the request. Use this return value if the process waits until the transmission is complete.
This is the process when there is an incoming packet.
First, the data of the incoming packet is passed as parameter rx
. From rx
, the address information and data payload of the wireless packet is accessed.
In the next line, the received packet data refers to the source address (32-bit long address and 8-bit logical address) and other information.
In <NWK_SIMPLE>
, two types of addresses are always exchanged: an 8-bit logical ID and a 32-bit long address. When specifying the destination, either the long address or the logical address is specified. When receiving, both addresses are included.
The MWX library provides a function expand_bytes()
as a counterpart to pack_bytes()
used in transmit()
.
Lines 1 through 3 specify variables to store data.
The first parameter specifies the first iterator of the container (a uint8_t*
pointer), which can be retrieved by the .begin()
method. The second parameter is the next iterator after the end of the container and can be retrieved with the .end()
method.
The third and subsequent parameters enumerate variables. The payloads are read and stored in the order in which they are listed.
This ACT omits error checking, such as if the packet length is wrong. If you want to make the check strict, judge by the return value of expand_bytes()
.
The return value of expand_bytes()
is uint8_t*
, but returns nullptr (null pointer)
in case of access beyond the end.
The process sends a PONG message if the identifier of the 4-byte string read in msg
is "PING"
.
It then displays information on packets that have arrived.
The format()
is used because numeric formatting output is required. helper class that allows the same syntax as printf() for >>
operators, but limits the number of arguments to a maximum of 8 (for 32-bit parameters). (A compile error will occur if the limit is exceeded. Note that Serial.printfmt()
has no limit on the number of arguments.)
The mwx::crlf
specifies a newline character (CR LF), and mwx::flush
waits for completion of output. (mxw::flush
may be written as Serial.flush()
)
This ACT acquires several samples of acceleration data after sleep recovery and sends that data.
The ACT of the "Act" includes the following
Sending and receiving wireless packets
Configuration through Interactive settings mode - <STG_STD>
State transition control by state machine - <SM_SIMPLE>
Wake up → start acquiring accelerometer data → wait for accelerometer FIFO interrupt → retrieve accelerometer data → wireless transmission → sleep.
The accelerometer stops adding data to the FIFO queue when the FIFO queue is full.
To support MOT PAL or TWELITE CUE, the include part is a macro. Define either USE_PAL_MOT
or USE_CUE
.
If USE_PAL_MOT
is defined, the board BEHAVIOR <PAL_MOT>
is included.
Define states for sequential processing during loop()
and also state machinestep
is declared.
Data structure for storing sensor data.
Registers board, configuration, and network behavior objects.
Initialize the Interactive settings mode.
First, adjust the configuration items. Here, we set the title name SETTINGS::appname
to be displayed in menu items, the default value of Application ID SETTINGS::appid_default
, the default value of CHANNEL SETTINGS::ch_default
, the default value of logical device ID default SETTINGS::lid_default
, and .hide_items()
for hidden items.
This sample transitions to Interactive settings mode when the SET pin is LO at startup. If digitalRead(brd.PIN_SET)
confirms that the pin is LO, specify SETTINGS::open_at_start()
. This specification causes the Interactive settings mode screen to appear immediately after exiting SETUP()
. When the screen is displayed, begin()
and loop()
are executed. In this sample, the state STATE::INTERACTIVE
is set so that no action such as sleep is performed during loop()
and nothing is done.
Next, the configuration values are read. To read the configuration values, be sure to execute .reload()
. In this sample, the option bit setting .u32opt1()
is read.
the_twelite
is a class object that manages the basic behavior of the system. This object performs various initializations such as Application ID and CHANNEL within setup()
.
Here some of the settings for Interactive settings mode is reflected.
If you wish to change an item reflected in the Interactive settings mode settings to another setting, continue with the setting you wish to override.
Settings are also made for the network behavior object. The logical device ID (LID) and retransmission settings in Interactive settings mode are reflected.
LED blink settings and other settings.
Called after setup()
is finished. Here, the first sleep is performed. However, if the screen in Interactive settings mode is displayed, it does not sleep.
After waking up, the state variable eState
is set to the initial state INIT. After this, loop()
is executed.
The basic structure of loop()
is <SM_STATE>
state machine state
with switch ... . case clause. The initial state is STATE::INIT
or STATE::INTERACTIVE
.
This is the state of the Interactive settings mode screen when it is displayed. Nothing is done. Interactive settings mode is used for Serial input and output in this screen.
INIT in its initial state.
In state INIT, initialization (clearing the queue for storing results) and initialization of the data structure for storing results is performed. transition to STATE::START_CAPTURE. After setting this transition, the while loop is executed again.
In the state START_CAPTURE, the FIFO acquisition of the MC3630 sensor is started. Here, the FIFO interrupt is set to occur when 4 samples are acquired at 400 Hz.
Set timeout for exception handling and transition to the next state STATE::WAIT_CAPTURE
.
In the state WAIT_CAPTURE, it waits for a FIFO interrupt. When an interrupt occurs and data is stored in the result storage queue, sns_MC3630.available()
becomes true
. Call sns_MC3630.end()
to terminate the process.
Get the number of samples and sample sequence numbers.
Reads and averages all sample data.
Here, the maximum and minimum are obtained for the acquired samples using the iterator corresponding to each axis.
The std::mimmax_element
is introduced as an example of using the algorithm of the C++ Standard Template Library, but you can also find the maximum and minimum in the for loop as shown in the comments.
Call .sns_MC3630.get_que().clear()
to clear the data in the queue. If this is not called, subsequent sample acquisitions will not be possible. After that, it transits to the STATE::REQUEST_TX
state.
. is_timeout()
checks for timeout. If timeout occurs, it transits to STATE::EXIT_FATAL
as an error.
In state REQUEST_TX
, the locally defined function TxReq()
is called to process the obtained sensor data and generate and send a transmission packet. The transmission request may fail due to the status of the transmission queue or other factors. If the transmission request succeeds, TxReq()returns as true, but no transmission is performed yet. The
on_tx_comp()` callback is called to complete the transmission.
Also, .clear_flag()
clears the flag to indicate that transmission is complete. At the same time, a timeout is set.
In state STATE::WAIT_TX
, it waits for the completion of wireless packet transmission. The flag is set by the on_tx_comp()
callback function, and .is_flag_ready()
becomes true after it is set.
When a series of operations is completed, it transits to the state STATE::EXIT_NORMAL
and calls the locally defined function sleepNow()
to execute sleep. When an error is detected, it transits to state STATE::EXIT_FATAL
and performs a system reset.
The last step is to generate a packet and request that it be sent. The packet should include the continuation number, the number of samples, the average value of XYZ, the minimum sample value of XYZ, and the maximum sample value of XYZ.
Sleep procedure.
Serial ports should call Serial.flush()
to output all before sleep.
The <SM_SIMPLE>
state machine must do on_sleep()
.
IO communication (basic function of standard application App_Twelite)
This is a sample using board support <BRD_APPTWELITE>
, which assumes the same wiring as required by App_TweLite.
This sample cannot communicate with App_TweLite.
Read M1 to determine the parent or child unit.
Reads the value of DI1-DI4; the Buttons class reduces the effect of chattering, so that changes are notified only when the values are the same consecutively. Communication is performed when there is a change.
Reads the value of AI1-AI4.
Sends the values of DI1-4, AI1-4, and VCC to a child unit if it is the parent unit, or to the parent unit if it is a child unit, every DI change or every 1 second.
Set to DO1-4 or PWM1-4 depending on the value of the received packet.
Include <TWELITE>
in all ACTs. Here is a simple network <NWK_SIMPLE>
and board support <BRD_APPTWELITE>
should be included.
It also includes <STG_STD>
to add Interactive settings mode.
sample-act common declaration
Its prototype declarations (send and receive), since the longer process is functionalized
Variables for holding data in the application
The general flow of the program is the initial setup of each section and the start of each section.
Register BEHAVIOR objects to determine system behavior. It can be an Interactive settings mode, board support, or a network description of wireless packets.
It will not work unless registered within setup()
.
Initialize the Interactive settings mode. First, a set
object is obtained. Then, the following process is performed.
Set the application name to "BRD_APPTWELITE"
(used in the menu)
Rewrite default Application ID and CHANNEL values
Delete unnecessary items
reads configuration values saved by set.reload()
.
Copy the values of OPT_BITS
and LID
into the variables
Reflection of the read Interactive settings mode settings is described below.
Below is an example screen. + + + and + three times with a pause of 0.2 to 1 second to bring up the Interactive settings mode screen.
Option Bits are not used in this sampler, although they are displayed in the menu.
This object behaves as the core of TWENET.
Register a board (this ACT registers <BRD_APPTWELITE>
). Specify the name of the board you want to register with <>
after use
as follows.
The return value obtained by universal reference (auto&&
) is a board object of reference type. This object contains board-specific operations and definitions. The following example uses the board object to check the status of the M1 pin: if the M1 pin is LO, set LID = 0, i.e., the Parent Node address.
Initial configuration is required to make the the_twelite work. Application ID and wireless CHANNEL settings are mandatory.
Use <<
to apply the setting to the_twelite
.
set
reflects some of the settings (Application ID, radio channel, etc.) read from Interactive settings mode. The items to be reflected are described in <STG_STD>.
TWENET::rx_when_idle()
Specification to open the receive circuit.
The <<, >>
operator is originally a bit shift operator, but it is used differently from its meaning. Within the MWX library, the above settings and serial port input/output are used in the library, following the C++ standard library's use of input/output.
Next, register the network.
The first line is written in the same way as for board registration, where <>
is <NWK_SIMPLE>
.
The second and third lines are settings for <NWK_SIMPLE>
. The first one reflects the value of the settings in Interactive settings mode. The items to be reflected are LID and the number of retransmissions. In this application, LID is set again in the third line because LID=0 may be set depending on the state of the M1 pin.
Class object that handles ADCs (analog-to-digital converters).
The Initialization is performed by Analogue.setup()
. The parameter true
specifies to wait in place until the ADC circuit is stable; the second parameter specifies to start the ADC synchronously with Timer0.
To start the ADC, call Analogue.begin()
. The parameter is a bitmap corresponding to the pin of the ADC target.
The pack_bits()
function is used to specify a bitmap. It is a function with variable number of arguments, each argument specifies a bit position to be set to 1. For example, pack_bits(1,3,5)
returns the binary value 101010
. This function has the constexpr
specification, so if the parameters are constants only, they are expanded to constants.
Parameters and BRD_APPTWELITE::
specified as PIN_AI1..4
are defined, corresponding to AI1..AI4 used in App_Twelite. AI1=ADC1, AI2=DIO0, AI3=ADC2, AI4=DIO2 and so on are assigned. PIN_ANALOGUE. PIN_ANALOGUE::
defines the list of pins available for ADC.
Except for the first time, the ADC is started in the interrupt handler.
Detects changes in DIO (digital input) values; Buttons only detect a change in value after the same value has been detected a certain number of times in order to reduce the effects of chattering (sliding) on mechanical buttons.
Initialization is done with Buttons.setup()
. The parameter 5 is the number of detections required to determine the value, but it is the maximum value that can be set. Internally, the internal memory is allocated based on this number.
Start is done with Buttons.begin()
The first parameter is the DIO to be detected. The second parameter is the number of detections required to determine the state. Since 4
is specified, the HIGH and LOW states are determined when the same value is detected five times in a row every 4ms.
Detection of the DIO state in Buttons is done by an event handler. Event handlers are called in the application loop after an interrupt occurs, so there is a delay compared to interrupt handlers.
Since App_Twelite uses a timer origin to control the application, the same timer interrupt/event should be run in this ACT. Of course, you can also use the system's TickTimer, which runs every 1 ms.
The first parameter in the above example specifies a timer frequency of 32 Hz; setting the second parameter to `true' enables software interrupts.
After calling Timer0.begin()
, the timer starts running.
Execute the_twelite.begin()
at the end of the setup()
function.
Serial objects can be used without initialization or initiation procedures.
This sample displays some system configuration values as a startup message. The Serial
object is given a string of type const char*, a string of type int (no other integer types), a format()
that behaves almost identically to printf, and a crlf
that outputs newline characters to the << operator.
The namespace mwx::
may be omitted in the sample. In the above, it is described as mwx::crlf
, but may be described as crlf
. mwx::namespace is designed to be partially optional.
Loop functions are called as callback functions from the main loop of the TWENET library. The basic description here is to wait for the object to be used to become available and then process it. This section describes the use of some of the objects used in ACT.
The main loop of the TWENET library processes incoming packets and interrupt information stored in the FIFO queue in advance as events, after which loop()
is called. After exiting loop()
, the CPU enters DOZE mode and waits until a new interrupt occurs with low current consumption.
Therefore, code that assumes the CPU is always running will not work well.
It becomes available at the timing when a change in DIO (digital IO) input is detected, and is read by Buttons.read()
.
The first parameter is a bitmap of the HIGH/LOW of the current DIO, ordered from bit0 to DIO0,1,2,... in order from bit0. For example, for DIO12, HIGH / LOW can be determined by evaluating bp & (1UL << 12)
. The bit that is set to 1 is HIGH.
When the IO state is determined for the first time, MSB (bit31) is set to 1. The first time the IO status is determined is also performed when the device returns from sleep mode.
Next, the values are extracted from the bitmap and stored in u8DI_BM
. Here, the collect_bits()
function provided by the MWX library is used.
collect_bits()
takes an integer argument of bit positions similar to pack_bits()
above. It is a function with a variable number of arguments and arranges the parameters as many as necessary. In the above process, bit0 is stored in u8DI_BM as the value of DI1, bit1 as the value of DI2, bit2 as the value of DI3, and bit3 as the value of DI4.
In App_Twelite, since wireless transmission is performed when there is a change from DI1 to DI4, the transmission process is performed starting from Buttons.available()
. The details of the transmit()
process are described below.
It becomes available at loop()
immediately after the analog-to-digital conversion of the ADC is completed. Until the next ADC starts, data can be read back as if it was acquired immediately before.
Use the Analogue.read()
or Analogue.read_raw()
method to read ADC values. read()
is the value converted to mV, read_raw()
is the ADC value of 0..1023. The parameter is the pin number of ADC, which is defined in PIN_ANALOGUE::
or BRD_APPTWELITE::
.
ADC values that are executed cyclically may read more recent values prior to the AVAILABLE notification, depending on the timing.
Since this ACT processes at a relatively slow cycle of 32 Hz, it is not a problem if the processing is done immediately after the AVAILABLE decision, but if the conversion cycle is short, or if you are doing a relatively long process in loop()
, be careful.
In Analogue
, you can specify a callback function that will be called from within the interrupt handler after the conversion is complete. For example, this callback function will store the value in the FIFO queue, and the application loop will perform asynchronous processing such as sequential reading of the queue value.
The Timer0
runs at 32 Hz. It becomes available at loop()
immediately after a timer interrupt occurs. In other words, it processes 32 times per second. Here, the transmission is processed when it reaches exactly 1 second.
AppTwelite performs a periodic transmission about every second. When Timer0
becomes available, it increments u16ct
. Based on this counter value, transmit()
is called to send a radio packet when the count is completed 32 times.
The value judgment of u8DI_BM
and au16AI[]
is whether or not it is just after initialization. If the values of DI1..DI4 and AI1..AI4 are not yet stored, nothing is done.
This function requests TWENET to send a radio packet. At the end of this function, the wireless packet is not yet processed. The actual transmission will be completed in a few ms or later, depending on the transmission parameters. This section describes typical transmission request methods.
MWX_APIRET
is a class that handles return values with data members of type uint32_t
, where MSB (bit31) is success/failure and the rest are used as return values.
Get a network object with the_twelite.network.use<NWK_SIMPLE>()
. Use that object to get a pkt
object with .prepare_tx_packet()
.
Here it is declared in a conditional expression in an if statement. The declared pkt
object is valid until the end of the if clause. pkt object gives a response of type bool, which here is true
if the TWENET send request queue is free and the send request is accepted, or false
if there is no free queue.
Suppresses screen output when Interactive settings mode screen is displayed.
Packets are configured using the <<
operator as in the initialization settings of the_twelite
.
Specify the destination address in the tx_addr()
parameter. If it is 0x00
, it means that you are the Child Node and broadcast to the Parent Node, and if it is 0xFE
, it means that you are the Parent Node and broadcast to any Child Node.
The tx_retry()
parameter specifies the number of retransmissions. The 1
in the example sends one retry, i.e., a total of two packets. Sending only one wireless packet will fail a few percent of the time even under good conditions.
tx_packet_delay()
Sets the transmission delay. The first parameter is the minimum wait time to start sending, the second is the maximum wait time, in this case approximately from 0 to 50 ms after issuing a request to send. The third is the retransmission interval. The third is the retransmission interval, meaning that a retransmission is performed every 10 ms after the first packet is sent.
Payload means a loaded item, but in wireless packets it is often used to mean "the main body of data to be sent". In addition to the main body of data, the data in a wireless packet also contains some auxiliary information, such as address information.
Be aware of the data order of the data payload for correct transmission and reception. In this example, the data order is as follows. Construct the data payload according to this data order.
The data payload can contain 90 bytes (actually several more bytes).
Every byte of an IEEE802.15.4 wireless packet is precious. There is a limit to the amount of data that can be sent in a single packet. If a packet is split, the cost of the split packet is high, since it is necessary to take into account transmission failures. Also, sending one extra byte consumes energy equivalent to approximately 16 µs x the current used during transmission, which is especially significant for battery-powered applications.
The above example is a compromise to some extent for the sake of explanation. To save money, the 00: identifier should be a simple 1 byte, and the Vcc voltage value may be rounded to 8 bits. Also, each AI1..AI4 value is 10 bits, using 6 bytes for a total of 40 bits = 5 bytes.
Let's actually construct the data structure of the above data payload. The data payload can be referenced as a container of type simplbuf<uint8_t>
by pkt.get_payload()
. In this container, we build the data based on the above specification.
It can be written as above, but the MWX library provides an auxiliary function pack_bytes()
for data payload construction.
The first parameter of pack_bytes
specifies the container. In this case, it is pkt.get_payload()
.
The parameter after that is a variable number of arguments, pack_bytes
, specifying the required number of values of the corresponding type. The pack_bytes
internally calls the .push_back()
method to append the specified value at the end.
The third line, make_pair()
, is a standard library function to generate std::pair
. This is to avoid confusion of string types (specifically, including or excluding null characters when storing payloads). The first parameter of make_pair()
is the string type (char*
, uint8_t*
, uint8_t[]
, etc.) The second parameter is the number of bytes to store in the payload.
The fourth line is of type uint8_t
and writes a bitmap of DI1..DI4.
Lines 7-9 write the values of the au16AI
array in sequence. The values are of type uint16_t
and are 2 bytes, but are written in big-endian order.
The for statement in line 7 is a range for statement introduced in C++. This syntax can be used for arrays of known size or container classes that can be accessed by begin(), end()
iterators. au16AI types can also be determined at compile time, so the type specification is also omitted with auto&&
(see Universal Reference).
If rewritten as a normal for statement, it would look like this
This completes the packet preparation. Now all that remains is to make a request for transmission.
Packets are sent using the pkt.transmit()
method of the pkt
object. It returns a return value of type MWX_APIRET
, which is not used in this ACT.
The return value contains information on the success or failure of the request and the number corresponding to the request. To perform a process that waits until the completion of transmission, use this return value.
When a wireless packet is received, on_rx_packet()
is called as a receive event.
The procedure with the_twelite.receiver
stores incoming packets in an internal queue (up to 2 packets) before processing them, but on_rx_packet()
is called directly from a callback from the TWENET library, so it is more resistant to overflow. However, loop()
is called directly from a callback from the TWENET library, and is less likely to cause overflow. However, if the processing is stopped for a long time in a loop()
statement, it may cause the same kind of overflow.
Here, the values of DI1...DI4 and AI1...AI4 communicated by the other party are set to its own DO1...DO4 and PWM1...PWM4.
First, the data rx
of the received packet is passed as a parameter. From rx
the address information and data payload of the wireless packet is accessed. The parameter handled
is not normally used.
Received packet data is referenced to the source address (32-bit long address and 8-bit logical address) and other information. Output is suppressed when the Interactive settings mode screen is displayed.
In <NWK_SIMPLE>
, two types of addresses are always exchanged: an 8-bit logical ID and a 32-bit long address. When specifying the destination, either the long address or the logical address is specified. When receiving, both addresses are included.
The MWX library provides a function expand_bytes()
as a counterpart to pack_bytes()
used in transmit()
.
The first line declares an array of type char
for data storage. The size of the array is 5 bytes because the null character is included at the end for convenience in character output, etc. The trailing {}
specifies initialization. The trailing {}
specifies initialization, and although it is sufficient to set the fifth byte to 0, here the entire array is initialized in the default way, that is, to 0
.
The 4-byte string is extracted by expand_bytes()
in the second line. The reason for not specifying the container type in the parameters is that we need to know the read position to read this continuation. the first parameter specifies the first iterator of the container (uint8_t*
pointer), which can be obtained by the .begin()
. The second parameter is an iterator that points to the next to the end of the container and can be retrieved with the .end()
method. The second parameter is used to avoid reading beyond the end of the container.
The third variable to read is specified, again by make_pair
, which is a pair of string array and size.
The ACT of expand_bytes()
omits error checking, for example, if the packet length is incorrect. If you want to make the check strict, judge by the return value of expand_bytes()
.
The return value of expand_bytes()
is uint8_t*
, but returns nullptr (null pointer)
for accesses beyond the end.
If the identifier in the 4-byte string read is different from the identifier specified in this ACT, this packet is not processed.
In TWENET, if the Application ID and the physical radio CHANNEL match, any application can receive the packet, even if it is of a different type. For the purpose of avoiding unintended reception of packets created by other applications, it is recommended to check such identifiers and the structure of data payloads to prevent coincidental matches.
The requirements for packet structure on the simple network <NWK_SIMPLE>
must also be satisfied, so mixed packet reception will not occur unless other applications that do not use the simple network define packets with the same structure (which seems very rare).
The next step is to get the data part: store the values of DI1..DI4 and AI1..AI4 in separate variables.
The first parameter is the return value np
of the previous expand_bytes()
. The second parameter is the same.
The third and subsequent parameters are variables of the matching type alongside the data payload, in the same order as the sender's data structure. Once this process is complete, the specified variable will contain the value read from the payload.
Output to serial port for confirmation. Output is suppressed when the Interactive settings mode screen is displayed.
We use format()
because we need to format output of numbers. helper class that allows the same syntax as printf()
for the >>
operator, but limits the number of arguments to four. (Serial.printfmt()
has no limit on the number of arguments.)
The first line "DI:%04b"
prints a 4-digit bitmap of DI1..DI4, like "DI:0010"
The third line "/%04d"
prints the values of Vcc/AI1..AI4 as integers, like "/3280/0010/0512/1023/"
The fifth line mwx::crlf
prints a newline string.
Now that the necessary data has been extracted, all that remains is to change the values of DO1..DO4 and PWM1..PWM4 on the board.
digitalWrite()
changes the value of the digital output, the first parameter is the pin number and the second is HIGH
(Vcc level) or LOW
(GND level).
Timer?.change_duty()
changes the duty ratio of the PWM output. Specify a duty ratio of 0..1024 for the parameter. Note that the maximum value is not 1023 (the maximum value is 1024, a power of 2, due to the high cost of the division performed in the library). Setting the value to 0
will result in an output equivalent to the GND level, and setting it to 1024
will result in an output equivalent to the Vcc level.
This is an Act sample using an I2C sensor device to measure temperature and humidity and send them periodically.
In this sample, using I2C sensors on our product (AMBIENT SENSE PAL or TWELITE ARIA BLUE / RED). However you can also use generic I2C devices by editing receiving/sending procedures. Connect a generic I2C device according to the following diagram.
This Act includes following features:
Sending / Receiving wireless packets
Configuration with "interactive settings mode" - <STG_STD>
Building a state machine with "SM_SIMPLE" - <SM_SIMPLE>
Send / Receive I2C commands
Sleep periodically for running with a button cell.
Header files are <NWK_SIMPLE>
(Simple network support), <STG_STD>
(Interactive settings mode) and <SM_SIMPLE>
(Simple state machines).
For your reference, this Act is using SHTC3 (TWELITE AMB PAL) or SHT40 (TWELITE ARIA). You can switch them by editing #ifdef
and they have same function interfaces for code portability. Both codes are similar because they were designed by the same manufacturer. (Please #define
either USE_SHTC3
or USE_SHT40
.)
Below using the SHTC3.
The struct (class) SHTC3
has I2C-sensor-realted procedures. The member variable I2C_ADDR
is the I2C address and the CONV_TIME
is the waiting time for getting values. The instance created from the SHTC3
is sensor_device
.
Methods
Each procedures are shown below.
Set the I2C address and the sensor value acquisition wait time (10 ms for the above) in the member variables.
In principle, these values are fixed values, so there is no need to set them. Valid examples of how to treat them as variables: Managing the conversion time required when operating a sensor with higher precision or selecting an I2C sub-address by setting them.
Write commands to operate the sensor.
In the MWX Libary, you can choose two different ways to control the I2C line from the Wire class object. This act is using Helper functions.
Wire.get_writer(I2C_ADDR)
opens the I2C device and returns a object for reading / writing data. If the device is not available, the object wrt
is evaluated as false
.
The line wrt << 0x60;
is writing a data to the device with the stream operator <<
. The operator is often used for writing data in uint8_t
.
Return the value of CONV_TIME
.
Get values from the sensor device.
SHTC3 reads the sensor value after waiting a few ms after starting the sensor readout by begin()
. The sequence of sensor values is as follows.
In SHTC3, the order of data also changes depending on the parameters given at the start of sensor acquisition, but if you start with the 0x609C
command written by the above begin()
, temperature data comes first.
In begin()
, the data was written out, but here the data is read in. To read the data, similarly, Wire.get_reader()
creates a helper object rdr
. If there are no errors, rdr
returns true in the if clause. The second parameter 6
given to get_reader(I2C_ADDR, 6)
is the number of bytes to read. When this number of bytes have been read, the procedure terminates reading the I2C bus. (Some devices may work even if these procedures are omitted, but usually you should give an appropriate value.)
Read is done with the stream operator >>
. There are several other read methods. For details, see Helper functions for details. When using the stream operator, enter values into pre-declared variables of type uint8_t
, uint16_t
, or uint32_t
. rdr >> u16temp
reads from the 2-byte I2C bus for variables of type uint16_t
and stores them in big-endian format (first byte is upper byte).
Finally, 100 times the temperature [°C] and 100 times the humidity [%] are calculated and stored in i16Temp
and i16Humd
.Refer to the I2C device datasheet for the calculation formula.
Called once when the TWELITE is started. This function performs various initializations.
The state machine (state transition machine) is used to simplify the description in the loop()
statement that is called each time. Of course, you do not have to use SM_SMPLE
in this example to describe your application.
SM_SIMPLE is implemented in a very short code that allows simple management of transitions to states, timeouts, and flags. The states are defined in advance as enumerations. In the above example, it is enum class STATE
. The entity of the state machine is declared as SM_SMPLE<STATE> step
with the predefined enum STATE
as a parameter.
BEHAVIOR is a collection of functions used in a program. It describes how to behave when various events occur.
In this example, we use two kinds of BEHAVIOR: interactive settings mode screen <STG_STD>
and simple relay network <NWK_SMPLE>
.
In order to make the interactive settings mode configuration items suitable for the application you are describing, initialize STG_STG. /settings/stg_std.md) to set the initial settings.
SETTINGS::appname
: application name (string). It will be displayed on the first line on the interactive settings mode screen. Use a minimum string since there is not enough room for the number of characters on the screen.
SETTINGS::appid_default
: Default application ID. Execute this if you want your own application to have its own defined application ID.
SETTINGS::ch_default
: channel default. Execute if you want your own application to have its own default channel.
Then set.hide_items()
removes unnecessary settings items on the screen in the default interactive settings mode. If you do not mind displaying them all, you do not need to make this call.
This description starts in interactive settings mode when the DIO12 pin is LOW (GND level) and the device is powered up or reset. Read the pin status with digitalRead()
and reflect SETTINGS::open_at_start()
.
Set the state of the state machine to STATE::INTERACTIVE
, since it would be inconvenient if normal application processing is performed during interactive settings mode. In this state, no input or other processing is performed and the application stays in the same state.
Finally, load the data in interactive settings mode. By calling set.reload()
, data written to the EEPROM is read. If no setting is made and there is no information in the EEPROM, it can be read as the specified value.
Here, option bits set.u32opt1()
and 8-bit logical ID set.u8devid()
are read. child machine).
Finally, the_twelite
and nwk
reflect (some of) the configuration information. This reflects information essential for wireless communication, such as application ID and channel. There is no explicit read code for these settings in the above, but set.reload()
reads the settings to the specified values if they are not set, or to the set values if they are.
Initialization for the I2C device.
the_twelite.begin()
is a procedure that declares the completion of the initialization of the MWX library. Without this procedure, the MWX library will not work properly.
Messages at startup, etc. are also displayed here.
The loop()
is controlled by the SM_SIMPLE state machinestep
for control. The purpose is to concisely express the sequence of steps from sleep recovery, sensor value acquisition, wireless packet transmission, waiting for transmission to complete, and sleep.
The control structure of the above do while statement is described below. The state is determined by step.state()
. The conditional expression of while is step.b_more_loop()
. This is because there are cases where you want to process continuously without leaving loop()
when a state transitions from one state to another. In other words, if you transition to another state and leave the switch clause, the case clause of the next state will be called. Be careful with this behavior.
It is not convenient for the main loop to operate during interactive settings mode, so it is fixed in this state.
Starts acquiring sensor data. The set_timeout()
statement waits for the sensor to acquire data.
For a sensor that waits for a very long time, it is possible to extend the battery life by writing a process to sleep once here, but this example is omitted because of the complexity of the structure. If necessary, you can use Example of waiting for sleep.
Get the sensor value by sensor_device.read()
and store the value in the sensor
structure.
First, a timeout check is performed by step.is_timeout()
. The starting point of timeout is step.set_timeout()
. If the timeout does not occur, the if clause is not executed and loop()
is left as is. The next hardware event (often a system timer, TickTimer, which generates an interrupt every 1 ms. /api-reference/predefined_objs/ticktimer.md), which generates an interrupt every 1ms), the TWELITE microcontroller will be in DOZE mode, where the CPU is in low-power standby.
As a wireless sensor, there is no need to output the result to the serial port of TWELITE on the sensor side, but the acquired value is displayed on the serial port to facilitate operation check. Here, Serial.flush()
is executed to wait for output, which is a description assuming that the serial port output does not finish before TWELITE goes to sleep. This process also causes battery drain, so either do not do Serial.flush()
or do not output to the serial port.
The div100() is a low-cost function to divide by 100.
Describes a communication procedure. In this state, there is no wait processing, etc., and the program transitions to the next state immediately after processing is executed. The reason why step.next(STATE::GO_SLEEP)
is written in advance is to avoid writing the same description in all places, since errors are detected in multiple places.
In if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet())
, the process is to create an object for the packet to be sent and execute the if clause if the object is successfully created.
First, configure the transmission settings. The destination tx_addr(0x00)
is set to the parent machine 0x00
, the number of retransmissions tx_retry(0x1)
is set to 1, and the packet delay setting tx_packet_delay(0, 0, 2)
is set to 0 delay for the first transmission and 2 ms for the retransmission interval. The packet delay is set to 0 for the first transmission and 2ms for the retransmission interval.
The identifier FOURCHARS
and the sensor data are stored in the payload part of the packet. Of the values obtained, the temperature value is int16_t
, but is cast to uint16_t
because the data structure of the outgoing packet is to be stored unsigned.
Call pkt.transmit()
to request a transmission. The MWX library will process the request at the appropriate time.
If the send request is successful, ret
will be true. Initialize the flags to determine completion step.clear_flag()
, set a timeout step.set_timeout(100)
to handle unexpected errors such as failed transmissions, and set the next state to STATE::TX_WAIT_COMP
(see STATE::GO_SLEEP
is overridden).
Here, it waits for the completion of transmission. Judgment of timeout (in case of error) or transmission completion event is made.
Processes the sleepNow()
function. By calling this function, the TWELITE wireless microcontroller goes to sleep.
This is a system event called when transmission is completed. Here .set_flag()
is called to set the flag of step
.
Procedures for going to sleep.
Initialize the state of the state machine by .on_sleep(false)
before sleep. The parameter false' starts from
STATE::INIT(=0)` after returning from sleep.
Here, the time to wake up is set between 1750ms and 2250ms by a random number. This avoids continuous collisions with packets from other devices transmitting at a similar period.
If the cycles are exactly the same, packets from each other will collide and communication will be difficult. Usually, the timer cycles shift with each other over time, so that communication is restored after a short period of time, and then collisions occur again after another period of time.
Lines 8 and 9, this example goes to sleep waiting for output from the serial port. Usually, we want to minimize energy consumption, so we minimize (or eliminate) output from the serial port before sleep.
Line 12, to enter sleep, call the_twelite.sleep()
. In this call, the pre-sleep procedures of the hardware on the board are performed.
The sleep time is specified in ms as a parameter.
TWELITE PAL must always wake up once within 60 seconds and reset the watchdog timer. The sleep time must be specified not to exceed 60000
.
When the program wakes up from sleep, wakeup()
is called. After that, loop()
is called each time. Before wakeup()
, each peripheral such as UART and devices on the board are woken up. For example, it restarts the LED lighting control.
The acts starting with act0 are the ones introduced in Starting an act - Opening act, and although they are simple, we recommend you try them out first.
Name | Remark |
---|---|
Parent application (for MONOSTICK)
This act uses MONOSTICK as a parent device. It outputs the data payload of packets from the child machine to the serial port. It can display packets in many samples of sample acts.
Receives packets from the child of the sample act and outputs them to the serial port.
Role | Items |
---|---|
Please check the following default settings at first.
Application ID: 0x1234abcd
Channel: 13
Include board behavior for <MONOSTICK>
. This board support includes LED control and watchdog support.
<NWK_SIMPLE> Loads the definition of a simple relay net
<STG_STD> Loads the interactive mode definition
Declaration of default values, function prototypes, etc.
In setup()
, first load the <MONOSTICK>
board behavior, the <STG_STD>
interactive mode behavior, and the <NWK_SIMPLE>
behavior using use<>
. This procedure is always done in setup()
.
The interactive mode is then set up and the settings are read out. The <STG_STD>
interactive mode provides standard items, but allows for some customization for each act you create.
appname
→ Act name that appears in the title line of the configuration screen
appid_default
→ Default Application ID
ch_default
→ Default channel
lid_default
→ Default value of device ID (LID)
.hide_items()
→ Item Hide Settings
Always call .reload()
before reading the configuration values. Each set value has its own method for reading, such as .u32opt1()
.
Some settings can be directly reflected using <STG_STD>
objects. In addition, if you want to rewrite a specific value due to a DIP switch setting, for example, you can rewrite the value separately after it is reflected. In the above example, the application ID, channel, radio output, etc. are set in the_twelite
object, the LID and the retransmission count are set in the nwk
object, and then the LID is set to 0 again.
Procedures for controlling LED lighting are available in the <MONOSTICK>
board behaviour.
The first line sets the red LED to switch on for 200 ms after receiving a radio packet. The first parameter LED_TIMER::ON_RX
means when a radio packet is received; the second specifies the lighting time in ms.
The second line specifies the blinking of the LEDs: the first parameter LED_TIMER::BLINK
specifies the blinking, the second parameter is the blinking on/off switching time: every 500ms the LEDs are switched on and off (i.e. Repeat blinking with a 1 s cycle).
Procedure for starting the_twelite
, which did not appear in act0..4, but must be called if you have configured the_twelite
or registered various behaviours.
There is no processing during loop()
in this sample.
Callback function called when a packet is received. In this example, some output is produced for the received packet data.
The analyze_payload()
called at the end of the function contains code to interpret some sample act packets. Refer to the code in correspondence with the packet generation part in the sample act.
This function first reads the four-character identification data into the fourchars[4]
array.
Reading is done using the expand_bytes()
function.The first and second parameters of this function follow the C++ standard library's practice of giving the first pointer .begin()
and the next .end()
of the payload section of the incoming packet. The following parameters are variable arguments, giving the data variables to be read. The return value is nullptr in case of an error, otherwise the next interpretation pointer. If interpreted to the end, .end()
is returned. The parameter here is uint8_t fourchars[4]
.
This description only supports the uint8_t[N]
type, where the array length N
is specified; when using the uint8*
, char*
, char[]
types, etc., the specification must be made using make_pair(char*,int)
.
Processing is then carried out for the 4-byte header. Here, the packets of the sample act Slp_Wk_and_Tx are interpreted and the contents are displayed.
Set b_handled
to true so that the other interpreters' decisions are skipped.
"TXSP" packets contain the values of a system timer count of type uint32_t
and a dummy counter of type uint16_t
. Each variable is declared and read using the expand_bytes()
function. The difference from the above is that the first parameter is np
as the first pointer to read. The tick_ms
and u16work_ct
are given as parameters and the value stored in the payload is read as a big-endian format byte sequence.
If the readout is successful, the contents are output and the process is complete.
It is structured by ASCII format in a user-defined order.
The first line declares a buffer as a local object to store the data sequence before conversion to ASCII format.
The second line uses pack_bytes()
to store the data sequence into the buf mentioned earlier. See comments in the source code for the data structure. The pack_bytes()
parameter can also be a container of the form smplbuf_u8 (smplbuf<uint8_t, ALLOC>)
.
The sequence number of the packet is automatically set by <NWK_SIMPLE>
and is assigned in the order of the transmitted packets. This value is used for packet duplication detection.
The LQI (Link Quality Indicator) is a value corresponding to the signal strength at the time of reception; the higher the value, the stronger the field strength. However, there is no strict relationship defined between this value and the physical quantity, and even if the LQI is higher relative to the noise in the environment, more noise will also reduce the success rate of communication.
Lines 13, 14 and 17 are the declaration, configuration and output of the serial parser.
The first output (which is prevented from being executed by if(0)
) displays all data including the control data of <NWK_SIMPLE>
. There are 11 bytes of control data. Normally, the control information is not directly referenced.
The first line declares the serial parser for output as a local object. It does not have an internal buffer, but diverts an external buffer and uses the output function of the parser to output the byte sequence in the buffer in ASCII format
The second line sets the buffer for the serial parser. It specifies an already existing data array, i.e. the payload part of the incoming packet. serparser_attach pout
declares the serial parser using an already existing buffer. The first parameter of pout.begin()
specifies the corresponding format of the parser as PARSER::ASCII
, i.e. ASCII format; the second specifies the first address of the buffer; the third specifies the The length of valid data in the buffer and the fourth specifies the maximum length of the buffer. The fourth parameter has the same value as the third, as it is for output and is not used for format interpretation.
Output to the serial port in line 6 using the >>
operator.
The Serial << mwx::flush
in line 7 is a specification to wait until the output of data that has not been output here is finished. (Serial.flush()
is the same process.)
MOTION SENSE PAL is used to acquire sensor values.
The ACT of the ACT includes the following.
Sending and receiving wireless packets
Interactive settings mode configuration - <STG_STD>
State transition control by state machine - <SM_SIMPLE>
Uses the motion sensor PAL MOTION SENSE PAL to continuously measure the acceleration of the accelerometer and transmit it wirelessly.
Use the sleep function, to operate on coin cell batteries.
Role | Example |
---|---|
Board BEHAVIOR <PAL_MOT>
is included.
First, register the board BEHAVIOR <PAL_MOT>
. When the board BEHAVIOR is initialized, the sensors and DIOs are initialized. The reason for doing this first is that it is common to check the status of the board's DIP SW, etc., and then configure the network settings, etc.
Here, 3 bits of the 4-bit DIP SW on the board are read and set as the Child Node ID. 0 means no ID (0xFE
).
Set the LED settings. Here the ON/OFF blinking is set to blink every 10ms (in an application that sleeps and has a short wake-up time, this is almost equivalent to setting the LED to turn on during wake-up).
Starts accelerometer measurement. The accelerometer settings (SnsMC3630::Settings
) specify the measurement frequency and measurement range. Here we use 14HZ measurement (SnsMC3630::MODE_LP_14HZ
) with ±4G range (SnsMC3630::RANGE_PLUS_MINUS_4G
).
Once started, the accelerometer takes 14 measurements per second and the values are stored in a FIFO queue inside the sensor. The sensor is notified when 28 measurements have been taken.
The begin()
function exits the setup()
function (after which TWENET is initialized) and is called just before the first loop()
.
Call sleepNow()
after setup()
ends to perform the first sleep.
Before going to sleep, set up an interrupt for the accelerometer's DIO pin, which is generated when the FIFO queue reaches a certain number. The second parameter is PIN_MODE::WAKE_FALLING
. This is a setting to wake up when the pin state changes from HIGH to LOW.
The third line executes sleep with the_twelite.sleep()
. The parameter 60000 is the wake-up setting required to reset the watchdog on the TWELITE PAL board. If not reset, a hard reset will be performed after 60 seconds.
When the accelerometer wakes up from sleep due to a FIFO interrupt from the accelerometer, wakeup()
is called. After that, loop()
is called each time. Before wakeup()
, each peripheral such as UART and devices on the board are woken up (e.g. resetting the watchdog timer). For example, it restarts the LED lighting control.
Here we are initializing variables to be used in loop()
.
Here, the acceleration information stored in the FIFO queue in the accelerometer is retrieved and packet transmission is performed based on this information. After the packet transmission is completed, sleep is executed again.
The b_transmit
variable controls the behavior within loop()
. After a successful transmission request, this value is set to 1, and the program waits for the packet transmission to complete.
First check to see if the sensor is available. Since it is after an interrupted wake-up, it is not normal for it not to be AVAILABLE, and it will go straight to sleep.
It is not used in the wireless transmission packet, but we will check the information on the acceleration taken out.
The measurement results of the accelerometer are stored in a FIFO queue obtained by brd.sns_MC3630.get_que()
.
The structure axis_xyzt
that stores the measurement results of the accelerometer contains the information of three axes (x, y, z) and the sequence number t. The number of samples stored is the size of the queue.
The number of samples stored can be checked by reading the queue size (brd.sns_MC3630.get_que().size()
). Normally 28 samples, but it may go a little further due to processing delays, etc. The first sample is taken from front()'. The first sample can be obtained by
front(). Its sequential number is
front().t`.
Here we will take the average of the samples before taking them out of the queue. Each element of the queue can be accessed with a for statement (for (auto&& v: brd.sns_MC3630.get_que()) { ... }
), where v.x, v.y, v.z
in the for statement is each element. Here, the sum of each element is calculated, and after the for statement, the average is calculated by dividing by the number of elements.
Next, a packet is generated and a request for transmission is made, but because the data volume is large, the packet is divided into two and transmitted twice. Therefore, the sending process is performed twice in the for statement.
The number of samples to be included in the packet to be sent and the sample first sequence number are stored in the first part of the packet payload.
Finally, the acceleration data is stored. While earlier we only referenced each element of the queue to calculate the average value, here we read one sample at a time from the queue and store it in the payload of the packet.
Use .front()
to read the head of the data queue from the accelerometer. After reading, .pop()
is used to release the top of the queue.
The data acquired from the accelerometer is in milli-G units, where 1G is 1000. Since the range is set to ±4G, the data is divided by 2 to be stored within a 12-bit range. To save the number of data, the first 4 bytes store the upper 8 bits of the X, Y and Z axes, and the next 1 byte stores the lower 4 bits of the Z axis, generating a total of 5 bytes.
The transmission ID is stored in the txid[]
array to wait for two transmissions.
Then, if b_transmit
is true
during loop()
, a completion check is performed, and if completed, sleepNow()
puts the program to sleep.
The completion of transmission is confirmed by the_twelite.tx_status.is_complete()
The .txid[]
is the ID value returned on transmission.
Environmental Sensor Pal AMBIENT SENSE PAL is used to acquire sensor values.
This ACT includes
Sending and receiving wireless packets
Configuring settings via Interactive settings mode - <STG_STD>
State transition control by state machine - <SM_SIMPLE>
<PAL_AMB>Board operation with board behavior
Uses the environmental sensor PAL AMPIENT SENSE PAL to acquire sensor values.
Use the sleep function to operate with coin cell batteries.
Role | Example |
---|---|
Environment sensor pal <PAL_AMB>
) include board behavior.
The first step is to initialize variables, etc. Here we are initializing the state machine step.
First, the board support <PAL_AMB>
is registered. The sensors and DIOs are initialized when the board support is initialized. The reason for doing this first is that it is common to check the status of the board's DIP SW, etc., and then configure network settings, etc.
The next step is to initialize and read out the Interactive settings mode.
Here you can get the set object, reflect the application name, reflect the default Application ID, and delete unnecessary items in the settings menu.
Next, the state of the SET pin is read. Since this sample operates intermittently in sleep mode, Interactive settings mode transition by +++ input is not possible. Instead, the sample transitions to Interactive settings mode with the SET pin = LO state at startup. In this case, SETTINGS::open_at_start()
is specified, which means that the interactive mode screen will be displayed as soon as setup()
is finished.
Finally, .reload()
is executed to read the set values from the EEPROM. The configuration values are copied to each variable.
Next, configure the LED settings. (In an application that sleeps and wakes for a short period of time, this is almost the same as setting the LED to turn on during wake-up.)
Since this ACT exclusively transmits wireless packets, the TWENET configuration does not include a specification (TWENET::rx_when_idle()
) to open the receive circuit during operation.
The sensors on the board use the I2C bus, so start using the bus.
Starts the acquisition of a sensor on the board. See the description of startSensorCapture()
.
The loop()
is controlled by the SMSMSIMPLE_SIMPLE state machinestep
for control. This is to concisely represent the sequence of events from sleep recovery, sensor value acquisition, wireless packet transmission, waiting for transmission to complete, and sleep. In the combat of the loop, a brd
object is acquired.
It is not convenient to have the main loop running during Interactive settings mode, so it is fixed in this state.
Start sensor data acquisition.
The sensors on the board can be accessed with the name .sns_LTR308ALS
or .sns_SHTC3
, and operations are performed on this object. Wait for the sensor to complete. If the sensor has not yet been acquired (.available()
is false
), a time elapsed event (.process_ev(E_EVENT_TICK_TIMER)
) is sent to the sensor.
When the above two sensors become available, the sensor value is retrieved and a transition is made to STATE TOK_TX.
The illuminance sensor can be obtained with .get_luminance() : uint32_t
.
The temperature and humidity sensor can be obtained as follows.
.get_temp_cent()
: int16_t
: temperature with 1℃ as 100 (2560 for 25.6℃)
.get_temp()
: float
: float value (25.6 for 25.6 ℃)
.get_humid_dmil()
: int16_t
: humidity at 1% as 100 (5680 for 56.8%)
.get_temp()
: float
: float value (56.8 for 56.8%)
The transmission procedure is the same as in the other ACT samples. Here, the settings are set to minimize one retransmission and retransmission delay.
The identifier FOURCHARS
and the sensor data are stored in the payload part of the packet. Of the values obtained, the temperature value is int16_t
, but is cast to uint16_t
because the data structure of the outgoing packet is to be stored unsigned.
Requests transmission. If the send request succeeds, prepare the send completion city. Specify .clear_flag()
to wait for the completion event and set_timeout(100)
for timeout in case of emergency. The unit of 100 in the parameter is milliseconds [ms].
This section determines timeouts and transmission completion events.
Processes sleepNow()
.
This is a system event called when transmission is complete. Here, .set_flag()
is used to indicate completion.
This section includes a collection of procedures for going to sleep.
Initialize the state of the state machine by .on_sleep(false)
before sleep. The parameter false' starts from
STATE::INIT(=0)` after returning from sleep.
Here, the time to wake up is set between 1750ms and 2250ms by a random number. This avoids continuous collisions with packets from other devices transmitting at a similar period.
If the cycles are exactly the same, packets from each other will collide and communication will be difficult. Usually, the timer cycles shift with each other over time, so that communication is restored after a short period of time, and then collisions occur again after another period of time.
Lines 8 and 9, this example goes to sleep waiting for output from the serial port. Usually, we want to minimize energy consumption, so we minimize (or eliminate) output from the serial port before sleep.
Line 12, to enter sleep, call the_twelite.sleep()
. In this call, the pre-sleep procedures of the hardware on the board are performed. For example, LEDs are turned off.
The sleep time is specified in ms as a parameter.
TWELITE PAL must always wake up once within 60 seconds to reset the watchdog timer. The sleep time must be specified not to exceed 60000
.
When the program wakes up from sleep, wakeup()
is called. After that, loop()
is called each time. Before wakeup()
, each peripheral such as UART and devices on the board are woken up. For example, it restarts the LED lighting control.
ACT PAL_AMB-UseNap can operate with lower energy consumption by sleeping while waiting for sensor data acquisition.
An ACT (Act) beginning with _Unit_ is used to describe a very single function or to check the operation of a function.
Name | Content |
---|---|
is an example of an ACT.
The pulse counter counts the number of rising or falling pulses of a signal without intervening microcontroller. It can be used to count irregular pulses and send wireless packets when the count reaches a certain number of times.
Counts the pulses connected to DIO8 on the Child Node side and transmits them wirelessly after a certain period of time or when a certain number of counts are detected.
The Child Node side operates while sleeping.
Role | Example |
---|
Initializes the pulse counter.
Starts the pulse counter operation and performs the first sleep. The first parameter of PulseCounter.begin()
is the count number 100
to generate a wake-up interrupt, and the second is the falling detection PIN_INT_MODE::FALLING
.
Checks PulseCounter.available()
when waking up. available, that is, true
, indicates that the count is greater than or equal to the specified count. If it is false
, it resleeps.
If the count is more than the specified number, loop()
processes the transmission and waits for the completion of the transmission.
Reads the pulse count value. The counter is reset after the readout.
The sample in is slightly modified so that the waiting time (approx. 50 ms) during sensor data acquisition is replaced by a sleep wait.
Please see the explanation of the ACT in before the explanation of this ACT.
The begin()
function exits the setup()
function (after which TWENET is initialized) and is called just before the first loop()
.
The first sleep is performed after setup()
ends. Although sensor data acquisition is started during setup()
, this result is not evaluated and is not necessarily necessary, since it means that the sensor is run once in advance.
Procedures after waking up. The following process is performed.
If the sensor data acquisition start has not yet been performed, the sensor data acquisition is performed and a short sleep is entered.
Since the start of sensor data acquisition was performed immediately before, the data is checked and sent wirelessly.
The above branch is controlled by the global variable b_sensor_started
. If !b_sensor_started
, it starts acquiring a sensor (startSensorCapture()
) and goes into a short sleep by napNow()
. The time is 100ms.
After returning from sleep by napNow()
, the clause b_sensor_started==true
is executed. Here, the E_EVENT_START_UP
event is notified to the two sensors. This event means that enough time has passed for the sensors to finish acquiring. Based on this notification, sns_LTR308ALS
and sns_SHTC3
are made available. After this, it will go to loop()
and wireless packets will be sent.
The event that notifies the sensor is used to determine if the required time wait is over. Whether or not the actual time has elapsed depends on whether or not the correct time was set with napNow()
. If the wake-up time is short, it is expected that the time elapsed is not enough to meet the required time elapsed and that subsequent processing will result in errors such as sensor data not being available.
Perform a very short sleep.
If the second parameter of sleep is set to true, the next wake-up time is adjusted based on the previous sleep wake-up time. This is useful if you always want to wake up every 5 seconds.
If the third parameter is set to true, the sleep mode is set without memory retention. After waking up, wakup() will not be called and the process will be the same as power-on.
The fourth specifies the use of the second wake-up timer. Here, the first is used for normal sleep and the second is used for short sleep. There is no strong reason to use the second timer in this ACT, but if, for example, the user wants to wake up every 5 seconds as described above, using the first timer for a short sleep would reset the counter value, which would complicate the elapsed time correction calculation, so the second timer is used.
Setting too short a sleep time does not balance the energy cost of reinitializing the system after returning from sleep. As a rule of thumb, the minimum time should be 30-50 ms.
is used to acquire sensor values.
This ACT includes
Sending and receiving wireless packets
Configuring settings via Interactive settings mode -
State transition control by state machine -
or board manipulation via board behavior
Uses the OPEN-CLOSE SENSE PAL to interrupt and wake up when the magnetic sensor is detected and transmit wirelessly.
Uses the sleep function to operate on coin cell batteries.
Role | Example |
---|
First, register the board behavior <PAL_MAG>
. When the board behavior is initialized, the sensors and DIOs are initialized. The reason for doing this first is that it is common to check the status of the board's DIP SW, etc., and then configure the network settings and so on.
Here, 3 bits of the 4-bit DIP SW on the board are read and set as the ID of the Child Node; if 0, the Child Node without ID (0xFE
) is assumed.
Set the LED settings. Here the ON/OFF blinking is set to blink every 10ms (in an application that sleeps and has a short wake-up time, this is almost equivalent to setting the LED to turn on during wake-up).
The begin()
function exits the setup()
function (after which TWENET is initialized) and is called just before the first loop()
.
Call sleepNow()
after setup()
ends to perform the first sleep.
Before going to sleep, set the interrupt for the DIO pin of the magnetic sensor. Use pinMode()
, the second parameter is PIN_MODE::WAKE_FALLING
. This is a setting to wake up when the pin state changes from HIGH to LOW.
In line 7, the_twelite.sleep()
executes sleep. The parameter 60000 is the wake-up setting required to reset the watchdog on the TWELITE PAL board. If not reset, a hard reset will be performed after 60 seconds.
When the program wakes up from sleep, wakeup()
is called. After that, loop()
is called each time. Before wakeup()
, each peripheral such as UART and devices on the board are woken up (e.g. resetting the watchdog timer). For example, it restarts the LED lighting control.
Here, in the case of wake-up from the wake-up timer (the_twelite.is_wokeup_by_wktimer()
), sleep is performed again. This is a wake-up only for the purpose of resetting the watchdog timer described above.
In the case of wakeup upon detection of the magnetic sensor, the loop() process will continue.
Here, the DIO of the detected magnetic sensor is checked, packets are sent, and sleep is performed again after packet transmission is complete.
The b_transmit
variable controls the behavior within loop()
. After a successful transmission request, this value is set to 1, and the program waits for the packet transmission to complete.
Check the detection DIO pins of the magnetic sensor. There are two types of detection pins: N-pole detection and S-pole detection. If you simply want to know that a magnet is approaching, you need to know that one of the pins has been detected.
Use the_twelite.is_wokeup_by_dio()
to check the pin of the wakeup factor. The parameter is the pin number. The reason why the return value is stored in uint8_t is to store it in the payload of the packet.
After setting communication conditions and storing data in the payload, transmission is performed.
Then, if b_transmit
is true
during loop()
, a completion check is performed, and if completed, sleepNow()
puts the program to sleep.
The completion of transmission is confirmed by the_twelite.tx_status.is_complete(u8txid)
. The u8txid
is the ID value returned on transmission.
WirelessUART performs serial communication.
Communicate between two UART-connected TWELITEs in ASCII format.
Two of the following devices serially connected to a PC.
connected to UART with products/TWE-Lite-DIP/index.html) etc.
Packets addressed to the Parent Node can also be received by .
Interactive settings mode is initialized. This sample provides two or more devices that have different logical device IDs (LIDs) from each other.
When data input from the serial is received, one byte is input to the serial parser. When the ASCII format is accepted to the end, SerialParser.parse()
returns true
.
The SerialParser
can access the internal buffer with smplbuf
. In the example above, the first byte of the buffer is taken as the destination address, and the second byte to the end is passed to the transmit()
function.
When a packet is received, a buffer smplbuf_u8<128> buf
containing the payload followed by the source as the first byte is created and output serially from the serial parser serparser_attach pout
for output.
Test data must be entered into the terminal using the paste function. This is because there is a timeout for input.
Note: To paste in TWE Programmer or TeraTerm, use Alt+V.
CR LF is required at the end of the input.
At first try a series ending in X where CR LF can be omitted. If no terminating string is entered, the series will be ignored.
Send 00112233
to any Child Node.
Send AABBCC00112233
to Child Node #3.
Sent to any Parent Node or Child Node (0xFF
) and to the Parent Node (0x00
).
Rcv_Unvsl (Universal Reciever)
By running NWK_LAYERED
on twe_twelite.network
and NWK_SIMPLE
on twe_twelite.network2
in the MWX library, packets of different types, including packets of layered tree net (TWELITE PAL, ARIA, etc) You can receive and interpret various types of packets.
However, wireless packets must be on the same CHANNEL and have the same Application ID.
setup()
, loop()
, and the callback function on_rx_packet()
for incoming packets.
These objects are declared in pkt_handler.cpp
and initialized by pnew()
in setup()
. It mainly interprets the payload (data) of the packet.
Two network objects are created. Be sure to NWK_LAYERED
to the_twelite.network
.
In this sample, the important part of the loop()
process is the .refresh()
process, which is performed about every second. Only g_pkt_apptwelite().refresh()
does the duplicate checker timeout. Other objects do nothing.
This is the most important part of this sample code. auto type = rx.get_network_type();
determines the packet type.
mwx::NETWORK::LAYERED
: NWK_LAYERED
layer tree net ket
mwx::NETWORK::SIMPLE
: NWK_SIMPLE
packets
mwx::NETWORK::NONE
: no network (e.g. App_Twelite)
other : error or unsupported packets
In the case of mwx::NETWORK::NONE
, the MWX library does not handle duplicate checkers for the same packet that may be sent in multiple retransmissions. It is necessary to describe the handling of these. In this sample, dup_checker.hpp
and dup_checker.cpp
are provided.
Interpretation of the packet refers to the packet_rx&
object that wraps tsRxDataApp*
. The packet_rx
class itself has no special functionality, it only defines a means of accessing some information obtained from tsRxDataApp*
using get_psRxDataApp()
.
It is defined for the purpose of unifying the interface of the packet interpretation part.
analyze()
: interpret the payload of a packet.
display()
: Display packet information.
refresh()
: describe the process every second.
self()
: cast to the derived class D
.
In addition, the packet interpretation class (example pkt_handler_apptwelite
above) contains a member object pkt
. The actual packet interpretation part is done in pkt_???.cpp
.
A packet interpretation part analyze()
for each packet type and a data structure data
are defined. The member data
is a structure but inherits the common structure of PktDataCommon
. This common part is used to concisely describe the code for serial output of the packet's data.
Corresponds to PAL-related packets; the PAL packet structure has a complex data structure. The implementation here is based on the EASTL container.
_vect_pal_sensors
: pool of _pal_sensor
objects. This object is a dedicated class for use with instusive map.
_map_pal_sensors
: intrusive map structure for efficient retrieval of sensor data.
Allocate an entry in _vect_pal_sensors
for each of the multiple data in a packet as they are added and store the value. Once all the data in a packet has been interpreted, a _map_pal_sensors
is constructed with the sensor type as the key.
Implement a duplicate checker. The behavior of the checker can be customized by template arguments.
MODE
: If MODE_REJECT_SAME_SEQ
is specified, packets with the same sequence number are excluded. This is used when the packet order is reordering. The MODE_REJECT_OLDER_SEQ
adopts a more recent number.
TIMEOUT_ms
: The interval at which to initialize the duplicate database. If 1000
is specified, data after 1 second will be erased. Packets that were excluded immediately before will be adopted again when the duplicate database is initialized.
N_ENTRIES
: The maximum number of elements to be allocated in the data structure.
N_BUCKET_HASH
: Maximum number of hash values. Specifies a prime number. It is determined based on the type of wireless node being received.
_mmap_entries
: It is an intrusive hash multi-map structure. The search key is the serial number of the wireless node.
_vect_pool
: Allocates a fixed number (N_ENTRIES
) of elements used in the map structure.
_ring_vecant_idx
: Keeps track of _vect_pool
elements not used in _mmap_entries
by array index number. It is a ring buffer structure, which takes one value from the ring buffer when adding elements and returns a value to the ring buffer when removing elements.
To retrieve data from the multimap structure, call .equal_range()
. The resulting r
is an iterator that enumerates elements with the same serial number.
Each element (_dup_checker_entry
) has a timestamp and sequence number. Duplicates are checked according to these values.
Role | Example |
---|---|
Type | Example |
---|---|
Name | Description |
---|---|
Byte | Description |
---|---|
Board BEHAVIOR for open/close sensor pal is included.
Initialize .
Parent Node
TWELITE DIP At a minimum, wire M1=GND, DI1:button, DO1:LED.
Child Node
TWELITE DIP At a minimum, wire M1=open, DI1:button, DO1:LED.
Parent
Run the Act Parent_MONOSTICK on the MONOSTICK BLUE or RED
Children
setup()
Initialize the sensor instead of the constructor (uncalled due to the limitation of the MWX Library).
begin()
Begin getting values from the sensor. After starting, it is necessary to wait a certain amount of time until the appropriate sensor values are obtained.
get_convtime()
Get the waiting time to acquire the sensor value.
read(int&, int&)
Read the sensor value.
0
Temperature Value (MSB side)
1
Temperature Value (LSB side)
2
CRC8 value of the byte 0 and 1
3
Humidity Value (MSB side)
4
Humidity Value (LSB side)
5
CRC8 value of the byte 3 and 4
act0
Templates with no processing description
act1
L-tica (flashing LED)
act2
L-tica with timer
act3
L-tica with two timers
act4
LED lighting using a button (switch)
Parent Device
Child Device
Sample Act Child Setup (e.g. Slp_Wk_and_Tx
, PAL_AMB
, PAL_MAG
, PAL_MOT???, etc...
)
Parent Node
MONOSTICK BLUE or RED Act Parent_MONOSTICK to work.
Child Node
Parent Node
MONOSTICK BLUE or RED ACT Parent_MONOSTICK in action.
Child Node
Unit_ADC
This sample runs the ADC, reading and displaying about every second while continuously running the ADC every 100ms. Sleep by [s]
key.
Unit_I2Cprobe
Scan the I2C bus for responding device numbers (some devices may not respond to this procedure).
Unit_delayMicoroseconds
delayMicroseconds()
, and compare with the count of 16Mhz TickTimer.
Unit_brd_CUE
Check the operation of the acceleration sensor, magnetic sensor, and LED of TWELITE CUE. Input [a]
,[s]
,[l]
keys from the terminal.
Unit_brd_PAL_NOTICE
Try Notice Pal (NOTICE PAL) LED lighting. All lights flash at startup, then operate via serial input.
- r
,g
,b
,w
: Toggle each color lighting mode
- R
,G
,B
,W
: change the brightness of each color (disabled when lights off or all lights on)
- c
: change the cycle (when blinking)
- C
: change the duty when blinking (when blinking)
Unit_div100
Find the divisor and quotient of 10,100,1000 div10()
,div100()
,div1000()
. -9999999 to 999999 and compare the elapsed time with the usual division by /
,%
.
Unit_div_format
div10()
,div100()
,div1000()
results in string output.
Unit_UART1
Unit_Pkt_Parser
Sample use of the packet information parser <a href="../It can interpret the output of App_Wings. * You may want to connect two TWELITE radio modules serially and use one as App_Wings and the other to interpret its output. If you want to connect the other to a non-TWELITE radio module, you can use "Other Platforms".
Uint_EEPROM
EEPROM read/write test code.
Unit_Cue_MagBuz
This program uses the TWELITE CUE magnet sensor and SET pin (connected to a piezoelectric buzzer) to sound a buzzer when the magnet is released.
Unit_doint-bhv
Example of BEHAVIOR description for handling IO interrupts.
Unit_EASTL
A collection of small codes using the EASTL library.
Parent Node |
Child Node |
Parent Node |
Child Node |
ACT in action.。
1. 2. +
Act in action.
+