/      日本語

PAL_MOT-fifo

Sample using motion sensor pal
The motion sensor PAL MOTION SENSE PAL is used to acquire sensor values.

Features of the Act

  • Uses the motion sensor PAL MOTION SENSE PAL to continuously measure acceleration via the accelerometer and wirelessly transmits the data.
  • Utilizes a sleep function for operation with a coin battery.

How to Use the Act

Required TWELITE

RoleExample
ParentOperate MONOSTICK BLUE or RED act Parent_MONOSTICK.
ChildBLUE PAL or RED PAL + MOTION SENSE PAL

Act Explanation

Include

##include <TWELITE>
##include <NWK_SIMPLE>
##include <PAL_MOT>

Include the board behavior <PAL_MOT> for the motion sensor PAL.

setup()

void setup() {
	/*** SETUP section */
	// board
	auto&& brd = the_twelite.board.use<PAL_MOT>();
	brd.set_led(LED_TIMER::BLINK, 100);

	// the twelite main class
	the_twelite
		<< TWENET::appid(APP_ID)
		<< TWENET::channel(CHANNEL);

	// Register Network
	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
	nwk	<< NWK_SIMPLE::logical_id(0xFE);

	/*** BEGIN section */
	the_twelite.begin(); // start twelite!
	brd.sns_MC3630.begin(SnsMC3630::Settings(
		SnsMC3630::MODE_LP_14HZ, SnsMC3630::RANGE_PLUS_MINUS_4G));

	/*** INIT message */
	Serial << "--- PAL_MOT(Cont):" << FOURCHARS
				 << " ---" << mwx::crlf;
}

First, register the board behavior <PAL_MOT>. When initializing the board behavior, the sensor and DIO are also initialized. Typically, you first check the state of the board’s DIP switches and then proceed with network settings and other processing.

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

u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE

Here, three bits out of the four DIP switches on the board are read and set as the child device’s ID. If the value is 0, it is set as a child without an ID (0xFE).

Set the LED configuration. Here, the LED is set to blink ON/OFF every 10ms (for applications with short wake times due to sleep, this is almost equivalent to lighting the LED while awake).

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

Accelerometer Initialization

	brd.sns_MC3630.begin(SnsMC3630::Settings(
		SnsMC3630::MODE_LP_14HZ, SnsMC3630::RANGE_PLUS_MINUS_4G));

Start measurement with the accelerometer. The accelerometer settings (SnsMC3630::Settings) specify the measurement frequency and range. Here, measurement is performed at 14Hz (SnsMC3630::MODE_LP_14HZ) with a ±4G range (SnsMC3630::RANGE_PLUS_MINUS_4G).

After starting, the accelerometer measures 14 times per second, and the values are stored in the sensor’s internal FIFO queue. Notification occurs when 28 measurements are completed.

begin()

The begin() function is called after the setup() function finishes (and after TWENET initialization), right before the first loop().

void begin() {
	sleepNow(); // the first time is just sleeping.
}

After setup() ends, sleepNow() is called to enter initial sleep.

sleepNow()

void sleepNow() {
	pinMode(PAL_MOT::PIN_SNS_INT, WAKE_FALLING);
	the_twelite.sleep(60000, false);
}

Before entering sleep, configure the interrupt setting for the accelerometer’s DIO pin. This interrupt occurs when the FIFO queue reaches a certain number. Use pinMode(). The second parameter specifies PIN_MODE::WAKE_FALLING, which means the device wakes up when the pin state changes from HIGH to LOW.

On the third line, the_twelite.sleep() puts the device to sleep. The parameter 60000 is required to wake up and reset the TWELITE PAL board’s watchdog. If not reset, a hard reset will occur after 60 seconds.

wakeup()

When the device wakes up from sleep due to a FIFO interrupt from the accelerometer, the wakeup() function is called. After that, loop() is called each time. Before wakeup(), peripherals such as UART and onboard devices perform their own wake-up processing (such as resetting the watchdog timer). For example, LED control is restarted.

void wakeup() {
	Serial << "--- PAL_MOT(Cont):" << FOURCHARS
	       << " wake up ---" << mwx::crlf;

	b_transmit = false;
	txid[0] = 0xFFFF;
	txid[1] = 0xFFFF;
}

Here, variables used in loop() are initialized.

loop()

Here, the acceleration information stored in the accelerometer’s FIFO queue is retrieved and used to transmit packets. After packet transmission is completed, the device enters sleep again.

void loop() {
	auto&& brd = the_twelite.board.use<PAL_MOT>();

	if (!b_transmit) {
		if (!brd.sns_MC3630.available()) {
			Serial << "..sensor is not available."
					<< mwx::crlf << mwx::flush;
			sleepNow();
		}

		// send a packet
		Serial << "..finish sensor capture." << mwx::crlf
			<< "  seq=" << int(brd.sns_MC3630.get_que().back().t)
			<< "/ct=" << int(brd.sns_MC3630.get_que().size());

		// calc average in the queue.
		{
			int32_t x = 0, y = 0, z = 0;
			for (auto&& v: brd.sns_MC3630.get_que()) {
				x += v.x;
				y += v.y;
				z += v.z;
			}
			x /= brd.sns_MC3630.get_que().size();
			y /= brd.sns_MC3630.get_que().size();
			z /= brd.sns_MC3630.get_que().size();

			Serial << format("/ave=%d,%d,%d", x, y, z) << mwx::crlf;
		}

		for (int ip = 0; ip < 2; ip++) {
			if(auto&& pkt =
				the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet())

				// set tx packet behavior
				pkt << tx_addr(0x00)
					<< tx_retry(0x1)
					<< tx_packet_delay(0, 0, 2);

				// prepare packet (first)
				uint8_t siz = (brd.sns_MC3630.get_que().size() >= MAX_SAMP_IN_PKT)
									? MAX_SAMP_IN_PKT : brd.sns_MC3630.get_que().size();
				uint16_t seq = brd.sns_MC3630.get_que().front().t;

				pack_bytes(pkt.get_payload()
					, make_pair(FOURCHARS, 4)
					, seq
					, siz
				);

				// store sensor data (36bits into 5byts, alas 4bits are not used...)
				for (int i = 0; i < siz; i++) {
					auto&& v = brd.sns_MC3630.get_que().front();
					uint32_t v1;

					v1  = ((uint16_t(v.x/2) & 4095) << 20)  // X:12bits
						| ((uint16_t(v.y/2) & 4095) <<  8)  // Y:12bits
						| ((uint16_t(v.z/2) & 4095) >>  4); // Z:8bits from MSB
					uint8_t v2 = (uint16_t(v.z/2) & 255);   // Z:4bits from LSB
					pack_bytes(pkt.get_payload(), v1, v2); // add into pacekt entry.
					brd.sns_MC3630.get_que().pop(); // pop an entry from queue.
				}

				// perform transmit
				MWX_APIRET ret = pkt.transmit();

				if (ret) {
					Serial << "..txreq(" << int(ret.get_value()) << ')';
					txid[ip] = ret.get_value() & 0xFF;
				} else {
					sleepNow();
				}
			}
		}

		// finished tx request
		b_transmit = true;
	} else {
		if(		the_twelite.tx_status.is_complete(txid[0])
			 && the_twelite.tx_status.is_complete(txid[1]) ) {

			sleepNow();
		}
	}
}

The b_transmit variable controls the behavior within loop(). After a transmission request succeeds, this value is set to 1 to wait for packet transmission completion.

	if (!b_transmit) {

First, check whether the sensor is available. Since this is after waking up from an interrupt, it is unusual for it to be unavailable, so sleep is entered immediately in that case.

if (!brd.sns_MC3630.available()) {
	Serial << "..sensor is not available."
			<< mwx::crlf << mwx::flush;
	sleepNow();
}

Although not used in the wireless transmission packet, the retrieved acceleration information is checked here.

Serial << "..finish sensor capture." << mwx::crlf
	<< "  seq=" << int(brd.sns_MC3630.get_que().front().t)
	<< "/ct=" << int(brd.sns_MC3630.get_que().size());

// calc average in the queue.
{
	int32_t x = 0, y = 0, z = 0;
	for (auto&& v: brd.sns_MC3630.get_que()) {
		x += v.x;
		y += v.y;
		z += v.z;
	}
	x /= brd.sns_MC3630.get_que().size();
	y /= brd.sns_MC3630.get_que().size();
	z /= brd.sns_MC3630.get_que().size();

	Serial << format("/ave=%d,%d,%d", x, y, z) << mwx::crlf;
}

The measurement results from the accelerometer are stored in a FIFO queue, which can be obtained with brd.sns_MC3630.get_que().

The structure axis_xyzt that stores the measurement results contains information for the three axes x, y, z, as well as a sequence number t.

The number of stored samples can be checked by reading the queue size (brd.sns_MC3630.get_que().size()). Normally, there are 28 samples, but this may increase slightly due to processing delays, etc. The first sample can be obtained with front(), and its sequence number is front().t.

Here, before removing samples from the queue, the average of the samples is calculated. Each element of the queue can be accessed with a for statement (for (auto&& v: brd.sns_MC3630.get_que()) { ... }). Within the loop, v.x, v.y, v.z are the respective elements. The sum of each element is calculated, and after the loop, the average is computed by dividing by the number of elements.

Next, a packet is generated and a transmission request is made. Since the amount of data is large, transmission is performed in two parts, so the transmission processing is executed twice in a for loop.

		for (int ip = 0; ip < 2; ip++) {

The number of samples to be included in the transmitted packet and the sequence number of the first sample are stored at the beginning of the packet’s payload.

// prepare packet (first)
uint8_t siz = (brd.sns_MC3630.get_que().size() >= MAX_SAMP_IN_PKT)
? MAX_SAMP_IN_PKT : brd.sns_MC3630.get_que().size();
uint16_t seq = brd.sns_MC3630.get_que().front().t;

pack_bytes(pkt.get_payload()
	, make_pair(FOURCHARS, 4)
	, seq
	, siz
);

Finally, the acceleration data is stored. Previously, each element in the queue was referenced only for average calculation, but here, samples are read one by one from the queue and stored in the packet payload.

for (int i = 0; i < siz; i++) {
	auto&& v = brd.sns_MC3630.get_que().front();
	uint32_t v1;

	v1  = ((uint16_t(v.x/2) & 4095) << 20)  // X:12bits
		| ((uint16_t(v.y/2) & 4095) <<  8)  // Y:12bits
		| ((uint16_t(v.z/2) & 4095) >>  4); // Z:8bits from MSB
	uint8_t v2 = (uint16_t(v.z/2) & 255);   // Z:4bits from LSB
	pack_bytes(pkt.get_payload(), v1, v2); // add into pacekt entry.
	brd.sns_MC3630.get_que().pop(); // pop an entry from queue.
}

To read the head of the data queue from the accelerometer, use .front(). After reading, use .pop() to release the head of the queue.

The data obtained from the accelerometer is in units of milli-G, where 1G = 1000. Since the range is ±4G, the value is divided by 2 to fit within 12 bits. To save data size, the first 4 bytes store the X, Y axes and the upper 8 bits of the Z axis, and the next 1 byte stores the lower 4 bits of the Z axis, making a total of 5 bytes per sample.

The transmission IDs are stored in the txid[] array to wait for the completion of two transmissions.

MWX_APIRET ret = pkt.transmit();

if (ret) {
	Serial << "..txreq(" << int(ret.get_value()) << ')';
	txid[ip] = ret.get_value() & 0xFF;
} else {
	sleepNow();
}

After that, if b_transmit is true in loop(), a completion check is performed, and if complete, the device enters sleep via sleepNow().

} else {
	if(		the_twelite.tx_status.is_complete(txid[0])
		 && the_twelite.tx_status.is_complete(txid[1]) ) {

		sleepNow();
	}
}

Transmission completion is checked with the_twelite.tx_status.is_complete(). The txid[] array contains the ID values returned at the time of transmission.