PAL_AMB

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

ACT FEATURES.

  • Uses the environmental sensor PAL AMPIENT SENSE PAL to acquire sensor values.

  • Use the sleep function to operate with coin cell batteries.

how to use act

Required TWELITE

Explanation of ACT

Include

#include <TWELITE>
#include <NWK_SIMPLE>// network support
#include <PAL_AMB> // PAL_AMB
#include <STG_STD> // Interactive settings mode
#include <SM_SIMPLE> // Simple State Machine

Environment sensor pal <PAL_AMB>) include board behavior.

setup()

void setup() {
	/*** SETUP section */
	step.setup(); // State machine initialization
		
	// Load PAL_AMB board behavior
	auto&& brd = the_twelite.board.use<PAL_AMB>();
	
	// Load Interactive settings 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);

	// Activate Interactive settings mode when SET pin is detected
	if (digitalRead(brd.PIN_BTN) == PIN_STATE::LOW) {
		set << SETTINGS::open_at_start();
		step.next(STATE::INTERACTIVE);
		return;
	}

	// Read data from Interactive settings mode
	set.reload();
	APP_ID = set.u32appid();
	CHANNEL = set.u8ch();
	OPT_BITS = set.u32opt1();

	// Determine LID from DIP switches and Interactive settings mode
	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)
	
	// LED initialization
	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;
}

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.

auto&& brd = the_twelite.board.use<PAL_AMB>();

The next step is to initialize and read out the Interactive settings mode.

// Load Interactive settings 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 SET pin is detected, activate Interactive settings mode
if (digitalRead(brd.PIN_BTN) == PIN_STATE::LOW) {
	set << SETTINGS::open_at_start();
	step.next(STATE::INTERACTIVE);
	return;
}

// Read data from Interactive settings mode
set.reload();
APP_ID = set.u32appid();
CHANNEL = set.u8ch();
OPT_BITS = set.u32opt1();

// Determine LID from DIP switches and Interactive settings mode
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 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.)

	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)

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_twelite
		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
		<< TWENET::channel(CHANNEL); // set channel (pysical channel)

The sensors on the board use the I2C bus, so start using the bus.

Wire.begin(); // start two wire serial bus.

Starts the acquisition of a sensor on the board. See the description 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());
}	

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.

case STATE::INTERACTIVE:

It is not convenient to have the main loop running during Interactive settings mode, so it is fixed in this state.

case STATE::INIT:

brd.sns_SHTC3.begin();
brd.sns_LTR308ALS.begin();

step.next(STATE::SENSOR);

Start sensor data acquisition.

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);
	}

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.

// 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);
}

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%)

case STATE::TX:

The transmission procedure is the same as in the other ACT samples. Here, the settings are set to minimize one retransmission and retransmission delay.

	pkt << tx_addr(0x00)  // Parent Node 0x00
		<< tx_retry(0x1)    // 1 retry
		<< tx_packet_delay(0, 0, 2); // minimul 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.

pack_bytes(pkt.get_payload() 
	, make_pair(FOURCHARS, 4)  
	, uint32_t(sensor.u32luminance)
	, uint16_t(sensor.i16temp)
	, uint16_t(sensor.i16humid)
);

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].

// 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:

This section determines timeouts and transmission completion events.

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:

Processes sleepNow().

on_tx_comp()

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

This is a system event called when transmission is complete. Here, .set_flag() is used to indicate completion.

sleepNow()

This section includes a collection of procedures for going to 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);
}

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.

wakeup()

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.

void wakeup() {
	Serial	<< mwx::crlf
			<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
			<< mwx::crlf
			<< "..start sensor capture again."
			<< mwx::crlf;

Applications

Reduction of energy consumption

ACT PAL_AMB-UseNap can operate with lower energy consumption by sleeping while waiting for sensor data acquisition.

最終更新