PAL_AMB-behavior
- Describes parent and child devices using Behavior.
- Uses
Wire
directly instead of the Board Behavior function to obtain sensor values. - The child device is described using state transitions based on a state machine.
Act Functions
- Uses the environmental sensor PAL AMBIENT SENSE PAL to acquire sensor values.
- Utilizes sleep functions to operate with coin batteries.
How to Use the Act
Preparing TWELITE
Role | Example |
---|---|
Parent | MONOSTICK BLUE or RED |
Child | BLUE PAL or RED PAL + Environmental Sensor PAL AMBIENT SENSE PAL |
File Structure
- PAL_AMB-behavior.hpp : Defines only
setup()
. Reads DIP switches, and if D1..D3 are in the up position, it operates as a parent device; otherwise, it sets the ID corresponding to the DIP switch as a child device. - Parent/myAppBhvParent.hpp : Behavior class definition for the parent device
- Parent/myAppBhvParent.cpp : Implementation
- Parent/myAppBhvParent-handlers.cpp : Handler implementation
- Parent/myAppBhvParent.hpp : Behavior class definition for the child device
- Parent/myAppBhvParent.cpp : Implementation
- Parent/myAppBhvParent-handlers.cpp : Handler implementation
The behavior name for the parent device is <MY_APP_PARENT>
, and for the child device is <MY_APP_CHILD>
.
setup()
// now read DIP sw status can be read.
u8ID = (brd.get_DIPSW_BM() & 0x07);
// Register App Behavior (set differnt Application by DIP SW settings)
if (u8ID == 0) {
// put settings to the twelite main object.
the_twelite
<< TWENET::appid(APP_ID) // set application ID (identify network group)
<< TWENET::channel(CHANNEL) // set channel (pysical channel)
<< TWENET::rx_when_idle(); // open RX channel
the_twelite.app.use<MY_APP_PARENT>();
} else {
// put settings to the twelite main object.
the_twelite
<< TWENET::appid(APP_ID) // set application ID (identify network group)
<< TWENET::channel(CHANNEL); // set channel (pysical channel)
the_twelite.app.use<MY_APP_CHILD>();
}
If the DIP switch read value is 0, the parent behavior <MY_APP_PARENT>
is registered; otherwise, the child behavior <MY_APP_CHILD>
is registered.
Parent Behavior
The parent device behaves as a receiver that does not sleep and outputs packet information to the serial port when receiving packets from child devices.
MY_APP_PARENT::receive()
void MY_APP_PARENT::receive(mwx::packet_rx& rx) {
uint8_t msg[4];
uint32_t lumi;
uint16_t u16temp, u16humid;
// expand packet payload (shall match with sent packet data structure, see pack_bytes())
auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end(), msg);
// if PING packet, respond pong!
if (!strncmp((const char*)msg, (const char*)FOURCHARS, 4)) {
// get rest of data
expand_bytes(np, rx.get_payload().end(), lumi, u16temp, u16humid);
// print them
Serial << format("Packet(%x:%d/lq=%d/sq=%d): ",
rx.get_addr_src_long(), rx.get_addr_src_lid(),
rx.get_lqi(), rx.get_psRxDataApp()->u8Seq)
<< "temp=" << double(int16_t(u16temp)/100.0)
<< "C humid=" << double(int16_t(u16humid)/100.0)
<< "% lumi=" << int(lumi)
<< mwx::crlf << mwx::flush;
}
}
When the parent device receives a packet, if the first four characters of the packet match (FOURCHARS
), the packet content is displayed.
MY_APP_PARENT::MWX_TICKTIMER_INT()
MWX_TICKTIMER_INT(uint32_t arg, uint8_t& handled) {
// blink LED
digitalWrite(PAL_AMB::PIN_LED,
((millis() >> 9) & 1) ? PIN_STATE::HIGH : PIN_STATE::LOW);
}
The parent device’s interrupt handler blinks the LED.
MY_APP_PARENT::MWX_DIO_EVENT(PAL_AMB::PIN_BTN)
MWX_DIO_EVENT(PAL_AMB::PIN_BTN, uint32_t arg) {
Serial << "Button Pressed" << mwx::crlf;
static uint32_t u32tick_last;
uint32_t tick = millis();
if (tick - u32tick_last > 100) {
PEV_Process(E_ORDER_KICK, 0UL);
}
u32tick_last = tick;
}
When the button (5) on the PAL is pressed, an E_ORDER_KICK
event is issued to the state machine.
MY_APP_PARENT::MWX_STATE(E_MWX::STATE_0 .. 3)
The state machine is described as a reference for state transitions and does not have a significant meaning for the application operation. It executes state transitions triggered by the E_ORDER_KICK
event from the button and timeouts.
Child Behavior
The operation flow of the child device is the same as PAL_AMB-usenap
. It repeats “wake up → start sensor operation → short sleep → wake up → acquire sensor values → wireless transmission → wait for transmission completion → sleep” from the initial sleep.
MY_APP_CHILD::on_begin()
void _begin() {
// sleep immediately.
Serial << "..go into first sleep (1000ms)" << mwx::flush;
the_twelite.sleep(1000);
}
The _begin()
function called from on_begin()
performs the initial sleep.
(You may write this process directly in on_begin()
without using _begin()
)
MY_APP_CHILD::wakeup()
void wakeup(uint32_t & val) {
Serial << mwx::crlf << "..wakeup" << mwx::crlf;
// init wire device.
Wire.begin();
// turn on LED
digitalWrite(PAL_AMB::PIN_LED, PIN_STATE::LOW);
// KICK it!
PEV_Process(E_ORDER_KICK, 0); // pass the event to state machine
}
Describes the wake-up process from sleep.
Here, the initial Wire.begin()
is executed. It is redundant to include this on subsequent wake-ups from sleep. This process can also be moved to on_begin()
.
MY_APP_CHILD::transmit_complete()
void transmit_complete(mwx::packet_ev_tx& txev) {
Serial << "..txcomp=" << int(txev.u8CbId) << mwx::crlf;
PEV_Process(E_ORDER_KICK, txev.u8CbId); // pass the event to state machine
}
Processes the E_ORDER_KICK
message to the state machine when transmission is complete.
MY_APP_CHILD::transmit_complete()
static const uint8_t STATE_IDLE = E_MWX::STATE_0;
static const uint8_t STATE_SENSOR = E_MWX::STATE_1;
static const uint8_t STATE_TX = E_MWX::STATE_2;
static const uint8_t STATE_SLEEP = E_MWX::STATE_3;
Defines state names.
MY_APP_CHILD::shtc3_???()
MWX_APIRET MY_APP_CHILD::shtc3_start()
MWX_APIRET MY_APP_CHILD::shtc3_read()
Example implementation for sensor acquisition of SHTC3. For details such as commands sent, please refer to the SHTC3 datasheet.
MY_APP_CHILD::ltr308als_???()
MWX_APIRET MY_APP_CHILD::ltr308als_read()
MWX_APIRET MY_APP_CHILD::ltr308als_start()
static MWX_APIRET WireWriteAngGet(uint8_t addr, uint8_t cmd)
Example implementation for sensor acquisition of LTR308ALS. For details such as commands sent, please refer to the LTR308ALS datasheet.
WireWriteAndGet()
sends one byte cmd
to the device at addr
, then receives one byte and returns the value.
MY_APP_CHILD::STATE_IDLE (0)
MWX_STATE(MY_APP_CHILD::STATE_IDLE, uint32_t ev, uint32_t evarg) {
if (PEV_is_coldboot(ev,evarg)) {
Serial << "[STATE_IDLE:START_UP(" << int(evarg) << ")]" << mwx::crlf;
// then perform the first sleep at on_begin().
} else
if (PEV_is_warmboot(ev,evarg)) {
Serial << "[STATE_IDLE:START_UP(" << int(evarg) << ")]" << mwx::crlf;
PEV_SetState(STATE_SENSOR);
}
}
State 0 has a special meaning. It is the state immediately after startup or waking from sleep.
It is called when the startup cold boot check PEV_is_coldboot(ev,evarg)
returns true
. Since on_begin()
immediately goes to sleep, no state transitions are included here. At this point, major initialization is not yet complete, so complex processes such as wireless packet transmission cannot be performed. To perform such processes, an event is sent from on_begin()
to trigger the first state transition.
After waking from sleep, PEV_is_warmboot(ev,evarg)
returns true
on the first call. It calls PEV_SetState()
to transition to STATE_SENSOR
.
MY_APP_CHILD::STATE_SENSOR
MWX_STATE(MY_APP_CHILD::STATE_SENSOR, uint32_t ev, uint32_t evarg) {
if (ev == E_EVENT_NEW_STATE) {
Serial << "[STATE_SENSOR:NEW] Start Sensor." << mwx::crlf;
// start sensor capture
shtc3_start();
ltr308als_start();
// take a nap waiting finish of capture.
Serial << "..nap for 66ms" << mwx::crlf;
Serial.flush();
PEV_KeepStateOnWakeup(); // stay this state on waking up.
the_twelite.sleep(66, false, false, TWENET::SLEEP_WAKETIMER_SECONDARY);
} else
if (PEV_is_warmboot(ev,evarg)) {
// on wakeup, code starts here.
Serial << "[STATE_SENSOR:START_UP] Wakeup." << mwx::crlf;
PEV_SetState(STATE_TX);
}
}
When transitioning from STATE_IDLE
after waking from sleep, the STATE_SENSOR
handler is called with the event E_EVENT_NEW_STATE
.
Here, the operation of two sensors, SHTC3 and LTR308ALS, is started. After a certain time, the sensors become ready for data acquisition. This wait time is performed by sleeping for 66 ms. Note that PEV_KeepStateOnWakeup()
is called before sleeping. This call keeps the state as STATE_SENSOR
after waking up, instead of returning to STATE_IDLE
.
After waking from this short sleep, the first call with PEV_is_warmboot(ev,evarg)
returns true
. At this point, wireless packet transmission can be performed. It transitions to STATE_TX
.
MY_APP_CHILD::STATE_TX
MWX_STATE(MY_APP_CHILD::STATE_TX, uint32_t ev, uint32_t evarg)
static int u8txid;
if (ev == E_EVENT_NEW_STATE) {
Serial << "[STATE_TX:NEW]" << mwx::crlf;
u8txid = -1;
auto&& r1 = shtc3_read();
auto&& r2 = ltr308als_read();
Serial << "..shtc3 t=" << int(i16Temp) << ", h=" << int(i16Humd) << mwx::crlf;
Serial << "..ltr308als l=" << int(u32Lumi) << mwx::crlf;
if (r1 && r2) {
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
Here, on the E_EVENT_NEW_STATE
event, sensor data is read and the wireless packet transmission procedure begins. For transmission procedure details, please refer to other act sample examples.
void transmit_complete(mwx::packet_ev_tx& txev) {
Serial << "..txcomp=" << int(txev.u8CbId) << mwx::crlf;
PEV_Process(E_ORDER_KICK, txev.u8CbId); // pass the event to state machine
}
// ↓ ↓ ↓ Message sending
} else if (ev == E_ORDER_KICK && evarg == uint32_t(u8txid)) {
Serial << "[STATE_TX] SUCCESS TX(" << int(evarg) << ')' << mwx::crlf;
PEV_SetState(STATE_SLEEP);
}
Waiting for transmission completion is handled differently from loop-based act descriptions. It waits for a message from transmit_complete()
via PEV_Process()
to confirm completion. When the message is received, it goes to sleep. The sleep process is done by transitioning to STATE_SLEEP
.
if (PEV_u32Elaspsed_ms() > 100) {
// does not finish TX!
Serial << "[STATE_TX] FATAL, TX does not finish!" << mwx::crlf << mwx::flush;
the_twelite.reset_system();
}
Finally, timeout processing is performed. This assumes the case where the transmission completion message does not return. PEV_u32Elaspsed_ms()
returns the elapsed time in ms since transitioning to this state. If time passes, the system resets (the_twelite.reset_system()
), assuming this timeout is a critical error.
MY_APP_CHILD::STATE_SLEEP
MWX_STATE(MY_APP_CHILD::STATE_SLEEP, uint32_t ev, uint32_t evarg) {
if (ev == E_EVENT_NEW_STATE) {
Serial << "..sleep for 5000ms" << mwx::crlf;
pinMode(PAL_AMB::PIN_BTN, PIN_MODE::WAKE_FALLING_PULLUP);
digitalWrite(PAL_AMB::PIN_LED, PIN_STATE::HIGH);
Serial.flush();
the_twelite.sleep(5000); // regular sleep
}
}
Performs sleep. This is described inside the E_EVENT_NEW_STATE
event immediately after transitioning from the previous state. Since other events might be called just before sleeping, always execute the_twelite.sleep()
inside a condition that runs only once.