PAL_AMB
This act includes the following:
- Wireless packet transmission and reception
- Settings via interactive mode -
<STG_STD>
- State transition control by state machine -
<SM_SIMPLE>
- Board operation by
<PAL_AMB>
board behavior
Act Features
- Using the environmental sensor PAL AMBIENT SENSE PAL, sensor values are acquired.
- Sleep function is used to operate on a coin battery.
How to Use the Act
Required TWELITE
Role | Example |
---|---|
Parent | MONOSTICK BLUE / REDRun the act Parent_MONOSTICK. |
Child | BLUE / RED PAL + Environmental Sensor PAL-AMBIENT SENSE PAL |
Act Explanation
Include
#include <TWELITE>
#include <NWK_SIMPLE>// Network support
#include <PAL_AMB> // PAL_AMB
#include <STG_STD> // Interactive mode
#include <SM_SIMPLE> // Simple state machine
Include the board behavior of the environmental sensor PAL <PAL_AMB>
.
setup()
void setup() {
/*** SETUP section */
step.setup(); // Initialize the state machine
// Load the board behavior of PAL_AMB
auto&& brd = the_twelite.board.use<PAL_AMB>();
// Load interactive mode
auto&& set = the_twelite.settings.use<STG_STD>();
set << SETTINGS::appname(FOURCHARS);
set << SETTINGS::appid_default(APP_ID); // set default appID
set.hide_items(E_STGSTD_SETID::POWER_N_RETRY, E_STGSTD_SETID::OPT_DWORD2, E_STGSTD_SETID::OPT_DWORD3, E_STGSTD_SETID::OPT_DWORD4, E_STGSTD_SETID::ENC_KEY_STRING, E_STGSTD_SETID::ENC_MODE);
// If the SET pin is detected, start interactive mode
if (digitalRead(brd.PIN_BTN) == PIN_STATE::LOW) {
set << SETTINGS::open_at_start();
step.next(STATE::INTERACTIVE);
return;
}
// Read data from interactive mode
set.reload();
APP_ID = set.u32appid();
CHANNEL = set.u8ch();
OPT_BITS = set.u32opt1();
// Determine LID from DIP switch and interactive mode settings
LID = (brd.get_DIPSW_BM() & 0x07); // 1st priority is DIP SW
if (LID == 0) LID = set.u8devid(); // 2nd is setting.
if (LID == 0) LID = 0xFE; // if still 0, set 0xFE (anonymous child)
// Initialize LED
brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
// the twelite main object.
the_twelite
<< TWENET::appid(APP_ID) // set application ID (identify network group)
<< TWENET::channel(CHANNEL); // set channel (pysical channel)
// Register Network
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
nwk << NWK_SIMPLE::logical_id(u8ID); // set Logical ID. (0xFE means a child device with no ID)
/*** BEGIN section */
Wire.begin(); // start two wire serial bus.
Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC)); // _start continuous adc capture.
the_twelite.begin(); // start twelite!
startSensorCapture(); // start sensor capture!
/*** INIT message */
Serial << "--- PAL_AMB:" << FOURCHARS << " ---" << mwx::crlf;
}
First, initialize variables and other settings. Here, the state machine step
is initialized.
First, the board support <PAL_AMB>
is registered. Initialization of sensors and DIO occurs during board support initialization. It is common to first check the state of the board’s DIP switches and then perform network settings and other processing.
auto&& brd = the_twelite.board.use<PAL_AMB>();
Next, initialize and read the interactive mode related settings.
// Load interactive mode
auto&& set = the_twelite.settings.use<STG_STD>();
set << SETTINGS::appname(FOURCHARS);
set << SETTINGS::appid_default(APP_ID); // set default appID
set.hide_items(E_STGSTD_SETID::POWER_N_RETRY, E_STGSTD_SETID::OPT_DWORD2, E_STGSTD_SETID::OPT_DWORD3, E_STGSTD_SETID::OPT_DWORD4, E_STGSTD_SETID::ENC_KEY_STRING, E_STGSTD_SETID::ENC_MODE);
// If the SET pin is detected, start interactive mode
if (digitalRead(brd.PIN_BTN) == PIN_STATE::LOW) {
set << SETTINGS::open_at_start();
step.next(STATE::INTERACTIVE);
return;
}
// Read data from interactive mode
set.reload();
APP_ID = set.u32appid();
CHANNEL = set.u8ch();
OPT_BITS = set.u32opt1();
// Determine LID from DIP switch and interactive mode settings
LID = (brd.get_DIPSW_BM() & 0x07); // 1st priority is DIP SW
if (LID == 0) LID = set.u8devid(); // 2nd is setting.
if (LID == 0) LID = 0xFE; // if still 0, set 0xFE (anonymous child)
Here, the set
object is obtained, the application name is reflected, the default application ID and communication channel are applied, and unnecessary items in the settings menu are removed.
Next, read the state of the SET pin. This sample performs intermittent operation using sleep, so transition to interactive mode by inputting +++
is not possible. Instead, interactive mode transitions if the SET pin is LOW at startup. At this time, SETTINGS::open_at_start()
is specified, which means to transition to the interactive mode screen immediately after setup()
finishes.
Finally, .reload()
is executed to read settings from EEPROM. The settings values are copied to variables.
Next, set up the LED. Here, it is set to blink ON/OFF every 10ms (in applications with short wake-up time due to sleep, this setting is almost the same as keeping it ON while awake).
brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
Since this act mainly transmits wireless packets, the TWENET settings do not include keeping the receiver circuit open during operation (TWENET::rx_when_idle()
).
the_twelite
<< TWENET::appid(APP_ID) // set application ID (identify network group)
<< TWENET::channel(CHANNEL); // set channel (pysical channel)
Since sensors on the board use the I2C bus, start using the bus.
Wire.begin(); // start two wire serial bus.
Start acquiring sensors on the board. See the explanation of startSensorCapture().
startSensorCapture();
loop()
void loop() {
auto&& brd = the_twelite.board.use<PAL_AMB>();
do {
switch (step.state()) {
// Behavior of each state
case STATE::INIT:
...
break;
...
}
while(step.b_more_loop());
}
loop()
performs control using the SM_SIMPLE
state machine step
. This is to simply express the sequence from waking up from sleep, acquiring sensor values, transmitting wireless packets, waiting for transmission completion, and sleeping. The brd
object is obtained in the loop body.
case STATE::INTERACTIVE:
It is inconvenient for the main loop to operate during interactive mode, so it is fixed in this state.
case STATE::INIT:
brd.sns_SHTC3.begin();
brd.sns_LTR308ALS.begin();
step.next(STATE::SENSOR);
Start acquiring sensor data.
case STATE::SENSOR:
if (!brd.sns_LTR308ALS.available()) {
brd.sns_LTR308ALS.process_ev(E_EVENT_TICK_TIMER);
}
if (!brd.sns_SHTC3.available()) {
brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
}
Sensors on the board can be accessed by the names .sns_LTR308ALS
or .sns_SHTC3
, and operations are performed on these objects. It waits for sensor completion. If the sensor acquisition is not finished yet (.available()
is false
), a time elapsed event (.process_ev(E_EVENT_TICK_TIMER)
) is sent to the sensor.
When the sensor becomes available, sensor values are acquired and transition to STATE_TX
.
// now sensor data is ready.
if (brd.sns_LTR308ALS.available() && brd.sns_SHTC3.available()) {
sensor.u32luminance = brd.sns_LTR308ALS.get_luminance();
sensor.i16temp = brd.sns_SHTC3.get_temp_cent();
sensor.i16humid = brd.sns_SHTC3.get_humid_per_dmil();
Serial << "..finish sensor capture." << mwx::crlf
<< " LTR308ALS: lumi=" << int(sensor.u32luminance) << mwx::crlf
<< " SHTC3 : temp=" << div100(sensor.i16temp) << 'C' << mwx::crlf
<< " humd=" << div100(sensor.i16humid) << '%' << mwx::crlf
;
Serial.flush();
step.next(STATE::TX);
}
Illuminance sensor is obtained by .get_luminance()
: uint32_t
.
Temperature and humidity sensor values can be obtained as follows:
.get_temp_cent()
:int16_t
: temperature in Celsius multiplied by 100 (e.g., 25.6 ℃ is 2560).get_temp()
:float
: float value (e.g., 25.6 ℃ is 25.6).get_humid_dmil()
:int16_t
: humidity in % multiplied by 100 (e.g., 56.8% is 5680).get_humid()
:float
: float value (e.g., 56.8% is 56.8)
case STATE::TX:
The transmission procedure is the same as other act samples. Here, it is set to one retry and minimum retry delay.
pkt << tx_addr(0x00) // To parent 0x00
<< tx_retry(0x1) // Retry once
<< tx_packet_delay(0, 0, 2); // Minimum delay
The payload of the packet includes the identifier FOURCHARS
and sensor data. Among the obtained values, temperature is int16_t
, but since the data structure of the transmission packet stores data as unsigned, it is cast to uint16_t
.
pack_bytes(pkt.get_payload()
, make_pair(FOURCHARS, 4)
, uint32_t(sensor.u32luminance)
, uint16_t(sensor.i16temp)
, uint16_t(sensor.i16humid)
);
Request transmission. If the transmission request succeeds, prepare for transmission completion event. To wait for completion event, .clear_flag()
is called, and a timeout of set_timeout(100)
is set. The parameter 100
is in milliseconds [ms].
// do transmit
MWX_APIRET ret = pkt.transmit();
if (ret) {
step.clear_flag(); // waiting for flag is set.
step.set_timeout(100); // set timeout
step.next(STATE::TX_WAIT_COMP);
}
case STATE::TX_WAIT_COMP:
Here, timeout and transmission completion events are checked.
if (step.is_timeout()) { // maybe fatal error.
the_twelite.reset_system();
}
if (step.is_flag_ready()) { // when tx is performed
Serial << "..transmit complete." << mwx::crlf;
Serial.flush();
step.next(STATE::GO_SLEEP);
}
STATE::GO_SLEEP:
Perform sleepNow()
processing.
on_tx_comp()
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
step.set_flag(ev.bStatus);
}
This system event is called upon transmission completion. Here, .set_flag()
marks completion.
sleepNow()
This function summarizes the procedure to enter sleep.
void sleepNow() {
step.on_sleep(false); // reset state machine.
// randomize sleep duration.
uint32_t u32ct = 1750 + random(0,500);
// output message
Serial << "..sleeping " << int(u32ct) << "ms." << mwx::crlf;
Serial.flush(); // wait until all message printed.
// do sleep.
the_twelite.sleep(u32ct);
}
Before sleeping, .on_sleep(false)
resets the state machine. The parameter false
means to start from STATE::INIT(=0)
after waking up.
Here, the sleep duration is randomized between 1750ms and 2250ms. This avoids continuous collisions of packets with other devices transmitting at the same period.
Lines 8 and 9: In this example, it waits for output from the serial port before sleeping. Normally, to minimize power consumption, serial port output before sleep is minimized or omitted.
Line 12: To enter sleep, call the_twelite.sleep()
. This call performs hardware sleep procedures on the board, such as turning off LEDs.
The parameter specifies sleep time in milliseconds.
wakeup()
When waking up from sleep, wakeup()
is called. Then loop()
is called repeatedly. Before wakeup()
, peripherals such as UART and devices on the board are woken up. For example, LED control is restarted.
void wakeup() {
Serial << mwx::crlf
<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
<< mwx::crlf
<< "..start sensor capture again."
<< mwx::crlf;
Advanced
Reducing Power Consumption
The act PAL_AMB-UseNap performs sleep while waiting for sensor data acquisition, enabling operation with lower power consumption.