arrow-left

このページのみ全てのページ
gitbookGitBook提供
triangle-exclamation
134ページのPDFを生成できませんでした、100で生成が停止しました。
さらに50ページで拡張
1 / 100

latest_en

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

The MWX Library

Mono Wireless C++ Library for TWELITE.

circle-check

Please refer to the Handling of Materialsarrow-up-right section. If you have any questions or concerns, please contact our support desk.

triangle-exclamation

This page contains information that is still under development. The contents of this page may not yet be reflected in the public source code.

The MWX library is intended to simplify the code notation of the TWELITE radio module. a program created with MWX is called an act. There are two types of acts: descriptions by loops and descriptions by events (called BEHAVIOR).

This page introduces the main features of ACT.

  • Description by loop (setup(), loop()). Suitable for writing small-scale functions.

  • Event-driven application description. It is possible to define various event and interrupt handlers, as well as a state machine within a class that is suitable for describing complex behavior of the application, and write code with good visibility. This description is called BEHAVIOR.

  • Simplify peripheral procedures. Defines class objects to handle commonly used UART, I2C, SPI, ADC, DIO, timer, and pulse counter.

  • A simple relay network is defined. This relay network is implemented in the same way as the TWELITE standard application, and is characterized by the fact that individual addresses are managed by 8-bit logical IDs, and that wireless packets can be sent to the network immediately after power-on because there is no communication to build the network.

circle-info

Although it does not allow for packet intercommunication with TWELITE standard applications, it does allow for more flexibility in the following areas

  • The logical ID is the same in that 0 is the parent device, but since 0x01..0xEF can be used as the child device address, the number of identifications can be 200 or more.

  • Board definition for PAL and MONOSTICK. Easy handling of sensors etc. on the board.

As a rule, the maximum number of times a packet can be relayed has been set to 64 times. (* If a packet returns after a certain period of time due to a detour, the duplicate packet management table will be cleared and the packet may have to be relayed again even if it has already been relayed. (Be careful when setting a large number of relays.
#include <TWELITE>
const uint8_t PIN_LED = 5;

void setup() {
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  if (TickTimer.available()) {
    uint32 t_now = millis();
    
    // blink LED every 1024ms
    digitalWrite(PIN_LED, (t_now >> 10) & 1 ? HIGH : LOW);
  }
}	
// myApp.hpp
...
class myApp : MWX_APPDEFS_CRTP(myApp) {
...
  void loop() {
    // main loop
  }
  
  void receive(mwx::packet_rx& rx) {
    // on receive
  }
};

// myApp.cpp
...
MWX_DIO_EVENT(12, uint32_t arg) {
		// on event from DIO12
}
void loop() {
  while(Serial.available() { 
    auto x = Serial.read(); ... } // serial message
  if (Analogue.available() {
    auto x = Analogue.read(...); } // adc values
  if (Buttons.available() { 
    Buttons.read(...); } // DIO changes
  if (the_twelite.receiver.available()) { 
    auto&& rx = the_twelite.receiver.read(); } // on rx packet
}
#include <TWELITE>
#include <NWK_SIMPLE>

void setup() {
  ...
  auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
	nwksmpl << NWK_SIMPLE::logical_id(0xFE) 
	           // set Logical ID. (0xFE means a child device with no ID)
	        << NWK_SIMPLE::repeat_max(3);
	           // can repeat a packet up to three times.
}

void loop() {
  ...
  vTransmit();
  ...
}

void vTransmit() {
  if (auto&& pkt =
    the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet(); 
  pkt << tx_addr(0x00)  // to parent 
	  	<< tx_retry(0x3); // set retry
	
	pack_bytes(pkt.get_payload() // prepare payload data
	    , uint8_t(0x01)
	    , uint16_t(analogRead(PIN_ANALOGUE::A1))
	    , uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)));
	
	pkt.transmit(); // transmit!
}
#include <TWELITE>
#include <PAL_AMB> // include the board support of PAL_AMB

void setup() {
	auto&& brd = the_twelite.board.use<PAL_AMB>(); // use PAL AMB
	uint8_t u8dip = brd.get_DIP_SW();   // check DIP s/w status
	brd.set_led(LED_TIMER::BLINK, 100); // LED switchs on/off every 100ms
	...
	
	// start capture of sensors
	brd.sns_SHTC3.begin();
}

void loop() {
	if (TickTime.available()) { // check every ms
		auto&& brd = the_twelite.board.use<PAL_AMB>();

		if (brd.sns_LTR308ALS.available()) {
		  Serial << brd.sns_SHTC3..get_temp();
		} else {
		  // notify sensor that 1ms passed.
			brd.sns_SHTC3.process_ev(E_EVENT_TICK_TIMER);
		}
	}
}

API

Act/behavior Programming Interface

triangle-exclamation

The MWX library API may be subject to specification changes in the future for the purpose of improvement.

License

License

hashtag
Warranty and License

The Mono Wireless Software License Agreementarrow-up-right (MW-SLA) applies to anything in this package that is not specifically described in the license.

This document is also handled under MW-SLA as a part of this library package part.

This software is not officially supported by Mono Wireless Ltd. Please note that we may not be able to respond to your inquiries. Please understand beforehand.

In response to reports of defects, Mono-Wireless, Inc. does not promise to fix or improve the problem.

There are also cases where the software may not work depending on the customer's environment, such as the package installed.

PAL_AMB-bhv

description sample. For details, please refer to for details.

mwx::flush

Flush buffered output to twe::stream.

Flush the output buffer of mwx::stream. Instance to a helper class that calls the flush() method.

  • For serial ports, wait polling until output completes.

  • For mwx::simpbuf

on_tx_comp()

on_tx_comp()

Called when transmission is complete.

This function is called from within the MWX library with data stored in ev as packet_ev_tx when the wireless packet transmission is finished. If this function is not defined in the application, a weak function that does nothing is linked.

ev.u8CbId is the ID at the time of transmission and ev.bStatus is a flag indicating success (1) or failure (0) of the transmission.

Setting b_handled

BEHAVIOR
here

Environment (OS, etc.)

DESC

circle-exclamation

In order to create a development environment, you need to install the software and agree to the license agreement. In addition, you may need to configure security settings on your PC or workstation.

  • Although we take great care when distributing the software, we ask that you also check for viruses.

  • Please check with the administrator of your environment regarding your security approach and operations (e.g., whether or not to install external applications).

In addition, the installation and operation of the development environment may require the intervention and configuration of the operating system (e.g., running an application whose developer is unknown; many of the development environments and tools introduced here do not have a built-in mechanism to prove the origin of the application) Please refer to the general information on how to configure.

To write an application using the MWX library, you will need the following:

  • MWSDK(Software Development Environment)

  • An editor for development (Microsoft's VisualStudio Code will be introduced)

hashtag
Windows10,11

Compiler toolchains, etc., are relatively less dependent on the environment, so they can be expected to work in many environments, but we recommend the Windows 10 version that is currently supported. If your system does not work due to differences in the operating environment, please prepare a separate environment by referring to the environment we have confirmed.

The following is the version we are using for development.

  • Windows11 21H2 (Visual Studio 2019)

  • FTDI driver must be running (to run MONOSTICK, TWELITE R)

hashtag
Linux

Compiler toolchains, for example, are relatively less dependent on the environment, so they can be expected to work in many environments, but we recommend distributions that are currently supported. If your system does not work due to differences in the operating environment, please prepare a separate environment by referring to the environment that we have confirmed.

The following is the version we are using for development.

  • Ubuntu 18.04 LTS 64bit

  • Ubuntu 20.04 LTS 64bit

32bit systems are not supported.

hashtag
macOS

Compiler toolchains, for example, are relatively less dependent on the environment, so they can be expected to work in many environments, but we recommend distributions that are currently supported. If your system does not work due to differences in the operating environment, please prepare a separate environment by referring to the environment that we have confirmed.

The following is the version we are using for development.

  • macOS 10.14 (Mojave, Intel)

  • macOS 12.4 (Monterey, Apple Silicon)

About Visual Studio Code and other development environments

For information on how to run and use the development environment, please refer to the information of its developer or community.

circle-info

The use of Visual Studio Code (VSCode) is recommended for efficient code writing.

The MWX library requires more PC resources to interpret the code on VSCode, etc., since there are more header files to read compared to C language development.

hashtag
Differences by build environment

circle-exclamation

The build results for Linux/macOS are different from the results for Windows 10. As far as we know, there is no difference in the normal operation, but the binary size tends to be a few percent larger, especially since the LTO of gcc is disabled.

If you have any doubts about the operation, be sure to run the build on Windows 10 and confirm that it reproduces before contacting us.

Other platforms

Other platforms

Build definitions are provided so that some features (serparser, pktparser, Serial object for console) can be built on other platforms. Only the necessary files are cut out.

The build definitions are stored in the {mwx library storage}/stdio folder. See README.mdarrow-up-right (link is on GitHub) for build instructions.

  • Must be able to compile in C++11.

  • Ability to use C++11 standard library headers (utility, algorithm, functional, iterator, etc.)

  • new/delete/virtual are not used.

  • Memory allocation by new may be used in exceptional cases.

    • In serparser/pktparser, alloc_heap which uses new operator is processed by delete.

      • (Reference) However, the mwx library has been designed on the assumption that delete is not taken into account.

Classes

SerialParser

format input parser for serial input (mwx::serial_parser)

This built-in class is defined as a built-in object, intended to be used for format input via serial port.

It is defined as a mwx::serial_parser<mwx::alloc_heap<uint8_t>> type that allocates buffer space for internal use from the heap at initialization (begin()).

For details, see class serparser for details.

class object

Class Objects

Class objects are predefined objects in the MWX library, such as the_twelite, which handles TWENET, and objects for using peripherals.

Each object must be initialized by calling the .setup() or .begin() method. (Only Serial objects that use UART0 do not require initialization.)

Functions

Functions

loop()

the main loop function.

This is the main loop of the application. After the loop ends, the CPU transitions to DOZE mode and waits for the next interrupt with low current consumption.

In the Act description, most of the processing is described in this loop.

circle-info

The back function definition is optional.

System Functions

System functions (time, random numbers)

/* Copyright (C) 2019-2022 Mono Wireless Inc. All Rights Reserved.
   Released under MW-SLA-*J,*E (MONO WIRELESS SOFTWARE LICENSE
   AGREEMENT) */
buffers, output
0x00
at the end (size is not changed)
for (int i = 0; i < 127; ++i) {
    Serial << "hello world! (" << i << ")" << twe::endl << twe::flush;
}
to
true
in this function tells the MWX library that the incoming packet has been processed in the application. If set to
processed
, it suppresses unnecessary processing. (Do not call event callback functions for
the_twelite.app
,
.board
, or
.settings
)
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) 

mwx::crlf

twe::stream に改行コードを出力する

Instance of a helper class to output a newline code (CR LF) for the << operator of mwx::stream.

Serial << "hello world!" << mwx::crlf;

TwePacket

It is a base class of packet type, but the member structure common contains address information and other common information.

class TwePacket {
	public:
		static const E_PKT _pkt_id = E_PKT::PKT_ERROR;
		
		struct {
			uint32_t tick; // system time at interpretation execution [ms]
			uint32_t src_addr; // source address (serial number)
			uint8_t src_lid; // source address (logical address)
			uint8_t lqi; // LQI
			uint16_t volt; // voltage[mV]
		} common;
};
circle-info

This is used when you want to get minimum information such as address information when you want to store mixed types as pktparser type in arrays, etc.

detachIntDio()

to unregister the interrupt handler.

Unregisters the interrupt handler.

void detachIntDio(uint8_t u8pin)

digitalReadBitmap()

Reads the values of all ports in the input settings at once.

circle-check

Included in mwx library 0.1.4 or later

Reads the values of all ports in the input settings at once.

uint32_t digitalReadBitmap()

The values are stored in the order of DIO0 ... DIO19 from the LSB side. DIO19 are stored in this order.

The pins on the HIGH side are set to 1 and the pins on the LOW side are set to 0.

delayMicroseconds()

Wait for time by polling (specified in μsec).

circle-check

It is not included in MWSDK2020_05. Supported packages will be MWSDK_2020_07_UNOFFICIAL or later.

Wait for time by polling (specified in μsec).

void delayMicroseconds(uint32_t microsec)

Wait for a given period of time in microsec.

The time is measured by the TickTimer count. When waiting for a long time, the CPU clock is reduced and polling is performed.

circle-exclamation

In the setup(), wakeup() function, TickTimer is not yet running, so it waits for a while in a while loop. In this case, the error with the specified value will be large. This loop counter is adjusted to 32Mhz. If the CPU clock is changed in these functions, the error will be proportional to the clock.

If you specify a short time, such as less than 10 for a parameter, the error may be large.

delay()

Waiting for time by polling.

Waiting for time by polling.

void delay(uint32_t ms)

The program waits for a given period of time in ms.

The time is measured by the TickTimer count. When waiting for a long period of time, the CPU clock is decreased and polling is performed.

circle-info

Every 5ms after calling delay(), TWELITE microcontroller performs internal watchdog processing.

For example, if you execute while(1) delay(1);, the watchdog processing is not performed because 5ms does not elapse inside delay(), and the reset is executed after a certain time.

circle-exclamation

In the setup(), wakeup() function, the TickTimer is not yet running, so it waits for a time by a while loop. In this case, the error with the specified value will be large. This loop counter is adjusted to 32Mhz. If the CPU clock is changed in these functions, the error will be proportional to the clock.

If you specify a short time, such as 1 or 2 as a parameter, the error may be large.

revision history

circle-info

Minor corrections will not be listed in this revision history, but only in the revision on GitHub. Please cite corrections as necessary.

hashtag
Updating

After the release of the TWELITE STAGE distribution package, fixes and additions are stored in the GitHub repository. Please replace the location of the distribution package if necessary.

Other updates to the MWSDK may be required. Please refer to the release description at the time of the update; see for information on updating the MWSDK.

hashtag
Updating MWX library code

The source code of the library is available on GitHub (). To replace the source code of the library, please follow the steps below.

  1. Click on the link for each release to clone the Git file or download the source code in zip format.

  2. Replace the contents of the following folders.

hashtag
Update before major release

Updated information prior to major releases may be posted on the above link.

hashtag
0.2.0 - 2022-03-01

Library Name
Dependent version
  • changed Wire object that reserves memory in heap area.

  • changed function name from G_OCTET() to G_BYTE()](api-reference/funcs/utility/byte-array-utils.md to avoid name conflict in utils.h.

hashtag
0.1.9 - 2021-12-15

Library Name
Dependent version

hashtag
主な改定内容

  • Added board support for TWELITE ARIA and sensor definition

  • Added an internal procedure to allow output using Serial class objects in Interactive settings mode. (Serial._force_Serial_out_during_intaractive_mode())

hashtag
0.1.8 - 2021-09-09

Library Name
Dependent version

Main revisions

  • Serial1port and alternate port were not properly defined.

  • Enabled to change the baud rate of (Serial UART0).

  • Added event callbacks to notify received packets (

circle-exclamation

Sorry. The following has not been translated.

hashtag
0.1.7 - 2020-12-03

Library Name
Dependent Version

hashtag
Major Revisions.

  • Added board behavior () for TWELITE CUE.

  • Added method to receive other packets (without network usage) that are not in NWK_SIMPLE format when using NWK_SIMPLE. Add NWK_SIMPLE::receive_nwkless_pkt() to initialize NWK_SIMPLE. When using this packet information, use only the TWENET C library layer structure by .get_psRxDataApp() and the data array obtained by .get_payload(). Information obtained from other methods of the incoming packet (auto&& rx = the_twelite.receiver.read()) is undefined.

hashtag
0.1.6 - 2020-10-09

Library Name
Dependent Version

hashtag
Major revisions

  • Modified so that div100(), which calculates the quotient and remainder, can be output to Serial, etc.

  • Changed implementation of smplbuf<> array class. The inheritance from mwx::stream is removed to reduce memory consumption, and a helper class and an inheritance class are defined separately.

hashtag
0.1.5 - 2020-08-05

Library Name
Dependent Version

hashtag
Download in bulk

()

hashtag
Major revisions

  • Added

  • Channel Manager. Implement

hashtag
0.1.4 - 2020-07-29 (MWSDK2020_07_UNOFFICIAL)

Library Name
Dependent Version

hashtag

hashtag
Download in bulk

()

hashtag
Major revisions.

  • Addition of delayMilliseconds() function

  • Addition of digitalReadBitmap() function

  • Improved accuracy of delay()

hashtag
0.1.3 - 2020-05-29

Support for MWSDK202020_05

  • Duplicate checker duplicate_checker was not removed as expected due to incomplete initialization, etc.

  • The implementation of format() was made less machine-dependent. If 64-bit arguments are included, the number of arguments is limited.

circle-exclamation

The fix assumes MWSDK2020_05.

Updates are recommended for this fix.

hashtag
0.1.2 - 2020-04-24

Support for MWSDK2020_04

  • Fixed initialization problem with Timer0..4

  • Changed internal processing of mwx::format()

  • Added experimental code to support interactive mode

circle-exclamation

This fix assumes MWSDK2020_04.

We recommend an update to this fix.

hashtag
0.1.1 - 2020-02-28

Fixed problems with handling of relay flags in packets

circle-exclamation

We recommend an update to this correction.

hashtag
0.1.0 - 2019-12-23

First release (included in SDL Dec 2019)

act0 .. 4

The acts starting with act0 are the ones introduced in Starting an act - Opening actarrow-up-right, and although they are simple, we recommend you try them out first.

Name
Remark

act0

Templates with no processing description

act1

L-tica (flashing LED)

act2

L-tica with timer

TickTimer

system timer (mwx::periph_ticktimer)

TickTimer is used for internal control of TWENET and is implicitly executed. The period of the timer is 1ms. Only the available() method is defined in loop() for the purpose of describing processing every 1ms by the TickTimer event.

circle-exclamation

Note that it is not always available in 1ms increments.

There are cases in which events are skipped due to a large delay caused by factors such as the contents of the user program description or the system's internal interrupt processing.

hashtag
Methods

available()

It is set after the TickTimer interrupt occurs and becomes true in the loop() immediately following it. It is cleared after loop() ends.

Install and Build

Install and Build

In order to write an application using the MWX library (called ACT in this document) and run it, you need to set up a development environment.

  • Enviromnents for TWELITE STAGE SDK

  • Installing TWELITE STAGE SDKarrow-up-right

SPI (using member functions)

SPI access (using member functions)

After initializing the hardware by the begin() method, the bus can be read and written by beginTransaction(). Executing beginTransaction() selects the SPI select pin. Read/write is done with the transfer() function; SPI reads and writes at the same time.

hashtag
beginTransaction()

Starts the use of the bus; sets the SPI select pin.

If called with the settings parameter given, the bus is set.

hashtag
endTransaction()

Terminates the use of the bus; releases the SPI select pin.

hashtag
transfer(), transfer16(), transfer32()

Reads and writes the bus. transfer() transfers 8 bits, transfer16() transfers 16 bits, and transfer32() transfers 32 bits.

mwx::bigendian

Output data of numeric type in big-endian order to twe::stream

Helper class for mwx::stream << operator to output numeric types as big-endian byte strings.

Serial << mwx::bigendian(0x1234abcdUL);

// output binary -> 0x12 0x34 0xab 0xcd

hashtag
constructor

template <typename T>
bigendian::bigendian(T v)
Parameters
Description

setup()

the setup function to initialize an application.

Called at the beginning of code execution to write initialization code.

circle-exclamation

TWENET initialization is also executed after the setup() function exits. Do not do anything other than initialization here, since most of the processing is done after TWENET exits.

Items to be noted are listed below.

format (mwx::mwx_format)

Enter printf format in mwx::stream

Helper class for writing format format to the << operator of mwx::stream. In the library, it is alias defined as Using format=mwx::mwx_format;.

circle-exclamation

The maximum number of arguments that can be registered in the variable number argument list is 8. 64-bit parameters such as double and uint64_t type are limited in number. If the limit is exceeded, a compile error will result due to static↵_assert.

alloc

Specified as a template argument of a container class (smplbuf, smplque) to allocate or specify an area of memory to be used internally.

circle-exclamation

This class is not called directly from user code, but is used internally to declare containers.

Class Name

smplbuf_strm_u8

The uint8_t type smplbuf_strm_u8?? is also available in the interface, so several methods for streams can be used.

Example

Call back functions

Call back functions describing an application.

This is a callback function that describes the application. Callback means called by the system (library). The user defines several callback functions to describe the behavior of the system.

The following callback functions are mandatory definitions.

  • setup()

init_coldboot()

triangle-exclamation

Usually not used.

Called at the re-initialization of code execution, with no peripheral API or initialization.

circle-info

millis()

Obtain the system time, [ms].

Obtain the system time, [ms].

The system tick time is updated by TickTimer interrupt.

Definition.

Act/behavior Programming Interface

For definitions commonly loaded in the library, cite the definition content.

hashtag
mwx_common.h

random()

Generates an random number.

Generates an random number.

The first line returns the value of 0.. (maxval-1) value is returned. Note that the value of maxval is not the maximum value.

The second line returns the value of minval..maxval-1.

init_warmboot()

DESC

triangle-exclamation

Usually not used.

Called in re-initialization when the peripheral API is not initialized after returning from sleep.

circle-info

digitalWrite()

Change the setting of the digital output pins.

Change the setting of the digital output pins.

The first parameter specifies the pin number to be set, and the second parameter specifies either HIGH or LOW.

circle-info

The input is of type E_PIN_STATE. The conversion operator from

begin()

It is called only once before the first call to the loop() function; since TWENET is already initialized, there is no need to consider constraints such as setup().

The main usage is to

  • Displaying startup messages

This callback function definition is optional.
This function can be used to detect interrupt factors.
circle-info

This callback function definition is optional.

Writing code for testing
  • Sleep transition immediately after startup

  • Processing that is inconvenient for setup() (radio packet processing, timer operation, etc.)

  • circle-info

    This callback function definition is optional.

    act3

    L-tica with two timers

    act4

    LED lighting using a button (switch)

    v

    a value of type uint16_t or uint32_t

    (Optional) Installing Visual Stdio code
    Building ACT
    Create a new project
    Makefile
    Use part of MWX code at another platoform(e.g. stdio environment)
    changed an order of
    vAHI_DioInterruptEnable()
    in the
    attachIntDio()
    for code efficiency.
  • added secondary network behavior the_twelite.network2 to support universal receiver (receive packets from NWK_LAYERED, NWK_SIMPLE or networkless packets in the same executable code.)

  • added NWK_LAYERED (At this time, only Parent Node reception is supported.)

  • introduced MWX_Set_Usder_App_Ver() function to set application version during MWX intitialization, mainly for interactive mode.

  • added mwx::pnew() to describe placement new simply.

  • added support of EASTL

    • added new[] operators for EASTL

  • pre-compled most of source codes in MWX to quicker compile.

  • fixed: DIO events were being passed on to unrelated ports.

  • ) and completed transmission (
    ).
    • If you don't define a callback function, you can use the previous procedure.

  • Wrong definition ID for interactive mode setting<STG_STD> and some default values.

  • Added support for changing the default values of channel and logical device IDs in addition to AppID in interactive mode settings<STG_STD>.

  • added support for setting the_twelite and <NWK_SIMPLE> objects in interactive mode <STG_STD> object for some settings.

  • added support for setting the default value of the number of retransmissions in <NWK_SIMPLE>.

  • Serial(UART0) input and output from the application is not performed while the interactive mode screen<STG_STD> is displayed.

  • added CUE::PIN_SET, PAL???"":PIN_SET (Since it is unnatural to use PIN_BTN for CUEs without buttons)

  • Move namespace of random() to mwx::.

  • MONOSTICK watchdog setting is now done in 32ms units.

  • When sleep was performed using BRD_TWELITE, the pins were not initialized correctly upon recovery.

  • Refine get_stream_helper() code and read/write position API.

  • Add EEPROM class object. (https://mwx.twelite.info/v/v0.1.7/api-reference/predefined_objs/eepromarrow-up-right)

    • Samples (https://github.com/monowireless/Act_samples/tree/master/Unit_EEPROMarrow-up-right)

  • Fixed bugs in smplbuf::get_stream_helper().

  • Added pktparser class (https://mwx.twelite.info/v/v0.1.7/api-reference/classes/pktparserarrow-up-right)

    • Added sample (https://github.com/monowireless/Act_samples/tree/master/Unit_PktParserarrow-up-right)

  • sample serparser/pktparser so that it can be built on other platforms (https://github.com/monowireless/mwx/tree/master/stdioarrow-up-right)

  • Added mwx_printf() mwx_snprintf()` function.
  • added the_twelite.stop_watchdog() and the_twelite.restart_watchdog() functions.

  • mwx::stream maintenance: operator bool() is obsolete. Disable timeout when .set_timeout(0xff) is set to 0xff in the read timeout setting. Add definition of << operator.

  • Added NOTICE PAL / PCA9632 support (Description https://mwx.twelite.info/v/latest/boards/pal/pal_noticearrow-up-right, sample https://github.com/monowireless/Act_samples/tree/master/Unit_using_PAL_NOTICEarrow-up-right)

  • Add scale functions between 8bit and 0..1000 with no division.

  • Added division by 10,100,1000 (quotient and remainder at the same time) div10(), div100(), div1000(). Restricted the value range and composed mainly of multiplication and bit shift.

  • Added corresponding methods for encrypted packets.

    • packet_rx::is_secure_pkt() : determine if received packet is encrypted or not.

    • STG_STD::u8encmode() : Obtain encryption setting in interactive mode.

    • STG_STD::pu8enckeystr() : Obtain encryption key byte sequence in interactive mode.

  • Serial1: Default port is DIO11(TxD), DIO9(RxD) because they are usually assigned to I2C, though DIO14,15 overlap with I2C in the semiconductor specification.

  • The calculation of the baud rate is omitted for the main baud rates.

  • Serial: The proxy functions for Serial: available() and read() are now held only by void*, and the specification memory is reduced by 8 bytes.

  • Add typedef boolean.

  • Network: added support for encryption.

    • The first parameter is the encryption key, and the second parameter is true, so that plaintext packets are also received. packets are also received.

  • Added sensor support for SHT3x and BME280

  • Sensor: added a mechanism to exchange configuration parameters and status in legacy code (C library wrapper class).

  • Sensors: I2C address can be specified for SHT3x and BME280.

  • Configuration: added hide_items(). Unnecessary items can be deleted.

  • Added `H/W UTIL menu' to display DI status, I2C probe, and PAL EEPROM contents.

  • Configuration: Add encryption related menus.

  • I2C related fixes (to improve compatibility with code implemented using TwoWire class)

    • Processing of requestFrom(false) did not work correctly because there was no code to send the NO_STOP message when processing requestFrom(false).

    • Added TwoWire class name alias.

    • Modified not to initialize multiply in begin() process.

    • Added setClock() method (but it is a dummy function and does nothing).

    • Added WIRE_CONF::WIRE_? KHZ added. Added the main configuration values for the bus clock.

  • .
  • Fixed the problem that Serial1 instance was not defined

  • Fixed problem with Analogue interrupt handler not being called

  • mwx

    0.2.0arrow-up-right

    twesettings

    0.2.6arrow-up-right

    TWENET C

    1.3.5

    mwx

    0.1.9arrow-up-right

    twesettings

    0.2.6arrow-up-right

    TWENET C

    1.3.5

    mwx

    0.1.8arrow-up-right

    twesettings

    0.2.6arrow-up-right

    TWENET C

    1.3.5

    mwx

    0.1.7arrow-up-right

    twesettings

    0.2.6arrow-up-right

    TWENET C

    1.3.4

    mwx

    0.1.6arrow-up-right

    twesettings

    0.2.5arrow-up-right

    TWENET C

    1.3.4

    mwx

    0.1.5arrow-up-right

    twesettings

    0.2.5arrow-up-right

    TWENET C

    1.3.4

    mwx

    0.1.4arrow-up-right

    twesettings

    0.2.4arrow-up-right

    TWENET C

    1.3.3

    herearrow-up-right
    https://github.com/monowireless/mwxarrow-up-right
    https://github.com/monowireless/mwx/wikiarrow-up-right
    BRD_ARIAarrow-up-right
    SHT4xarrow-up-right
    on_rx_packet()
    https://mwx.twelite.info/v/v0.1.7/boards/cuearrow-up-right
    MWSDK2020_08_UNOFFICIALarrow-up-right
    README.mdarrow-up-right
    settings behavior (interactive mode function)
    chmgr
    MWSDK2020_07_UNOFFICIALarrow-up-right
    README.mdarrow-up-right
    https://github.com/monowireless/mwx/releases/tag/0.1.3arrow-up-right
    https://github.com/monowireless/mwx/releases/tag/0.1.2arrow-up-right
    https://github.com/monowireless/mwx/releases/tag/0.1.1arrow-up-right
    https://github.com/monowireless/mwx/releases/tag/0.1.0arrow-up-right
    on_tx_comp()
    void loop() {
      if (TickTimer.available()) {
        if ((millis() & 0x3FF) == 0) { // This may not be processed (could be skipped)
          Serial << '*';
        }
      }
    }
    inline bool available()
    void beginTransaction()
    void beginTransaction(SPISettings settings)
    // smplbuf_strm_u8<N> : locally allocated
    template <int N> using smplbuf_strm_u8 
      = _smplbuf_stream<uint8_t, mwx::alloc_local<uint8_t, N>>;
    
    // smplbuf_strm_u8_attach : attach to an existing buffer
    using smplbuf_strm_u8_attach 
      = mwx::_smplbuf_stream<uint8_t, mwx::alloc_attach<uint8_t>>;
    
    // smplbuf_strm_u8_heap : allocate at HEAP
    using smplbuf_strm_u8_heap 
      = mwx::_smplbuf_stream<uint8_t, mwx::alloc_heap<uint8_t>>;
    
    // definition of operator <<
    template <class L_STRM, class ALOC>
    mwx::stream<L_STRM>& operator << (
            mwx::stream<L_STRM>& lhs, 
            mwx::_smplbuf_stream<uint8_t, ALOC>& rhs)
    {
    		lhs << rhs.to_stream();
    		return lhs;
    }
    smplbuf_strm_u8<128> sb1;
    
    sb1 << "hello";
    sb1 << uint32_t(0x30313233);
    sb1 << format("world%d",99);
    sb1.printfmt("Z!");
    
    Serial << sb1;
    // hello0123world99Z!
    stream(stream)
    uint32_t millis()
    #include <cstdint> // for type name
    typedef char char_t;
    typedef uint8_t byte;
    typedef uint8_t boolean;
    
    #ifndef NULL
    #define NULL nullptr
    #endif
    uint32_t random(uint32_t maxval)
    uint32_t random(uint32_t minval, uint32_t maxval)
    E_PIN_STATE
    to
    int
    type is not defined, so direct numeric input is not allowed.
    static inline void digitalWrite(uint8_t u8pin, E_PIN_STATE ulVal)
  • Sleep the_twenet.sleep() cannot be executed. If you want to sleep immediately after initialization, write the first sleep process in the begin() function.

  • The delay() function is replaced by the processing described below. In this case, the parameter ms does not specify milliseconds. \frz

  • * Alternative processing for delay().

    static inline void delay(uint32_t ms) {
    		volatile uint32_t ct = ms * 4096;
    		while (ct > 0) {
    			--ct;
    		}
    }

    Store the argument list received in the constructor in a variable internal to the class using the expand function of the parameter pack.

  • Call fctprintf() at the point operator << is called to write data to the stream

  • hashtag
    Constructor

    The constructor stores the format pointer and parameters. The subsequent call with the << operator interprets the format and processes the output.

    Parameters
    Description

    fmt

    Format format. See TWESDK/TWENET/current/src/printf/README.md

    ...

    Parameters according to the format format. * The maximum number of parameters is 4. 5 or more parameters will result in a compile error. * Since consistency with the format is not checked, it is not safe for inconsistent input.

    circle-exclamation

    The fmt must remain accessible until this object is destroyed.

    Contents

    alloc_attach<T>

    specify an already existing buffer

    alloc_local<T, int N>

    statically allocate a buffer of N bytes internally

    alloc_heap<T>

    allocate a buffer of the specified size in the heap

    In alloc_attach and alloc_heap, initialization methods (init_???()) must be executed according to the memory allocation class.

    hashtag
    initialization

    Initialize with buffer p and size n.

    hashtag
    Methods

    hashtag
    alloc_size()

    Returns the size of the buffer.

    hashtag
    _is_attach(), _is_local(), _is_heap()

    This method is used to generate a compile error, like static_assert, for a method call description that is different from the expected alloc class.

    loop()

    If no other functions are defined, an empty function that does nothing is linked instead.

    hashtag
    Normal callback call sequence

    circle-exclamation

    Please refer to the source code mwx_appcore.cpp if you want to see the exact behavior.

    hashtag
    Order of callback invocation on return to sleep

    circle-exclamation

    Please refer to the source code mwx_appcore.cpp if you want to see the exact behavior.

    auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    nwk << NWK_SIMPLE::logical_id(0xFE) // set Logical ID. (0xFE means a child device with no ID)
        << NWK_SIMPLE::secure_pkt((const uint8_t*)"0123456789ABCDEF");
        ;
    .../MWSTAGE/              --- TWELITE STAGE distribution dir
            .../MWSDK         --- MWSDK dir
                  .../TWENET/current/src/mwx <-- Replace this dir.
    void endTransaction()
    inline uint8_t transfer(uint8_t data)
    inline uint16_t transfer16(uint16_t data)
    inline uint32_t transfer32(uint32_t data)
    Serial << format("formatted print: %.2f", (double)3123 / 100.) << mwx::crlf;
    
    // formatted print: 31.23[改行]
    format(const char *fmt, ...)
    void attach(T* p, int n) // alloc_attach
    void init_local()        // alloc_local
    void init_heap(int n)    // alloc_heap
    uint16_t alloc_size()
    init_coldboot()
      ↓ (TWENET internal processing: Initialization 1)
    setup()
      ↓(TWENET internal processing: Initialization 2)
    begin() --- First time only
      ↓
    loop() <--+
      ↓       |Event processing, behavior processing
    CPU DOZE -+
    the_twelite.sleep()
      ↓ sleeping...
     
     
    init_warmboot()
      ↓ (TWENET internal processing: Initialization 3)
    wakeup()
      ↓(TWENET internal processing: Initialization 4)
    loop() <--+
      ↓       |Event processing, behavior processing
    CPU DOZE -+

    Serial

    UART0 port of TWELITE (mwx::serial_jen)

    Implement mwx::stream and input/output with UART0 of TWELITE.

    • The Serial object is initialized at system startup with UART0, 115200 bps, and the initialization process is performed in the library. On the user code, it is available from setup().

    • The Serial1 object is provided in the library, but no initialization process is performed; to enable UART1, perform the necessary initialization procedures Serial1.setup(), Serial1.begin().

    triangle-exclamation

    Output may become unstable during setup(), wakeup() just after startup or flush process just before sleep.

    hashtag

    hashtag
    setup()

    Initialize objects.

    • Allocate memory for FIFO buffers for TX/RX

    • Allocating memory for TWE_tsFILE structure

    circle-exclamation

    Serial(UART0) will automatically call setup() in the library. There is no need for a user call.

    Also, the buffer size of Serial (UART0) is determined at compile time. You can change it by the macro MWX_SER_TX_BUFF (768 if not specified) or MWX_SER_RX_BUFF

    Parameter
    Description

    hashtag
    begin()

    Initialize hardware.

    circle-exclamation

    The Serial (UART0) has an automatic begin() call in the library. No user call is required.

    Parameters
    Description
    circle-info

    The last two digits of the specified baud rate are rounded to zero. Also, due to hardware limitations, there may be an error from the specified baud rate.

    When specifying 9600, 38400, or 115200, the baud rate is calculated without division. See constexpr uint16_t _serial_get_hect_baud(uint32_t baud) for details of processing.

    hashtag
    end()

    (Not implemented) Stop using hardware.

    hashtag
    get_tsFile()

    Get a structure in the TWE_tsFILE* format used in the C library.

    circle-info

    In Serial (UART), the _sSerial structure is defined.

    .get_stream_helper()

    Operators and methods by mwx::stream via a stream_helper helper object that references a smplbuf array of type uint8_t.

    smplbuf_u8<32> b;
    auto&& bs = b.get_stream_helper(); // helper object
    
    // Data String Generation
    uint8_t FOURCHARS[]={'A', 'B', 'C', 'D'};
    bs << FOURCHARS;
    bs << ';';
    bs << uint32_t(0x30313233); // "0123"
    bs << format(";%d", 99);
    
    Serial << b << crlf; // Output to Serial via smplbuf_u8<32> class
    
    //Result: ABCD;0123;99

    Helper object type names are resolved by auto&& because they are long. The interfaces defined in mwx::stream, such as << operator, can be used for this object.

    The generated helper object bs starts reading/writing from the beginning of the main array b when it is created. If it is at the end of the array, data is added by append(). Each time a read/write operation is performed, the position is moved to the next one.

    Helper functions can use the >> operator for reading.

    Timer0 .. 4

    Timers, PWM (mwx::periph_timer)

    The timer has two functions: to generate a software interrupt at a specified period, and to output a PWM at a specified period. 5 timers in total are available in the TWELITE radio module, from 0...4.

    The name of the built-in object is Timer0..4, but will be referred to as TimerX on this page.

    hashtag
    Methods

    hashtag
    setup()

    Initializes the timer. This call allocates the necessary memory space.

    hashtag
    begin()

    Starts a timer; the first parameter is the period of the timer in Hz; setting the second parameter to true enables software interrupts; setting the third parameter to true true` enables PWM output.

    circle-info

    You can change the frequency with change_hz(). The change_hz() allows more detailed specification than the begin() specification.

    The duty ratio of PWM output can be changed with change_duty().

    hashtag
    end()

    Stops the timer operation.

    hashtag
    available()

    It becomes true at loop() immediately after a timer interrupt occurs, and becomes false when loop() ends.

    hashtag
    change_duty()

    Set the duty ratio. the first parameter specifies the duty ratio (a small value makes the average of the waveform closer to the GND level, a large value makes it closer to the Vcc level). the second parameter specifies the maximum duty ratio value of the duty ratio.

    circle-exclamation

    It is recommended that duty_max be one of 1024,4096,16384.

    The internal calculation of the count value involves division, but only for these three values is bit shifting used, while for other values, a computationally expensive division process is performed.

    hashtag
    change_hz()

    Sets the frequency of the timer; the second parameter is an integer with three decimal places for the frequency. For example, to set the frequency to 10.4 Hz, specify hz=10, mil=400.

    Installing VSCode

    Installing VSCode

    VisualStudio Code (VSCode) is recommended to make Act (source code) writing easier. The attached Act contains a file that has been configured to ensure that the code is interpreted properly in VSCode.

    circle-exclamation

    VSCode reads source files and header files and interprets the source code, thus providing function definition information and function and method name completion to help you write source code. The MWX library requires more header files to be loaded than the C library. In comparison to C development, the MWX library requires more header files to be loaded and the editor may be slower in some environments.

    The project settings of VSCode requires some information, such as library source code location, to analyse source codes. These inforamtion are passed by TWELITE STAGE app as environmental variable when launching VSCode. Therefore, application instance of VSCode should not be present when launching from TWELITE STAGE app, otherwise VSCode will not refer to these environmental values.

    hashtag
    Installing VSCode

    triangle-exclamation

    Please note that this support does not cover questions about how to install or use VSCode. Please refer to the information available in the public domain.

    Depending on your environment, you may need to configure security settings or other settings in order to install the software. Please check with your system administrator whether installation is possible, and refer to the distributor or general information for instructions.

    VSCode allows you to do the following:

    • Editing the source code

    • The IntelliSense based on source code interpretation (*not all definitions can be guaranteed to be interpreted correctly)

    VSCode can be downloaded from the link below.

    hashtag
    Special note for each OS

    The code command must be enabled to invoke VSCode from TWELITE STAGE.

    The following information is from code.visualstudio.com

    • - PATH must be set so that the code command can be executed.

    hashtag
    Installing plug-ins

    To enable Visual Studio Code to interpret C/C++ language descriptions, install a plugin.

    • C/C++ for Visual Studio Code

    hashtag
    Notes

    circle-exclamation

    The MWX library examples include a .vscode definition. This definition uses the MWSDK_ROOT environment variable to identify the source code of the library (under {MWSDK_ROOT}/TWENET/current).

    When launching VSCode from TWELITE STAGE, those settings such as MWSDK_ROOT will be setup automatically. In some cases, such as when you have already started VSCode, the settings may not be reflected.

    circle-exclamation

    VSCode's interpretation of source code is not always the same as the compiler's interpretation. Also, depending on how the source code is edited, the interpretation may be more incomplete.

    TwePacketUART

    The TwePacketAppUart class is the format in which the extended format of the App_UART is received by the parent and repeater application App_Wings.

    class TwePacketAppUART : public TwePacket, public DataAppUART

    Various information in the packet data is stored in DataAppUART after parse<TwePacketUART>() execution.

    circle-info

    The simple format cannot be interpreted. parse<TwePacketUART>() returns E_PKT::PKT_ERROR. To check the contents, refer to the original byte sequence directly.

    hashtag
    DataAppUART structure

    The payload is the data part, but the method of data storage changes depending on the macro definition.

    If MWX_PARSER_PKT_APPUART_FIXED_BUF is compiled with the value 0, payload refers directly to the byte sequence for packet analysis. If the value of the original byte sequence is changed, the data in payload will be destroyed.

    If you define the value of MWX_PARSER_PKT_APPUART_FIXED_BUF to be greater than 0, the payload will allocate a buffer of that value (bytes). However, if the data of the serial message exceeds the buffer size, parse<TwePacketAppUART>() fails and returns E_PKT::PKT_ERROR.

    E_PKT

    packet type definition

    Corresponds to the following packet

    circle-info

    Corresponds to the ASCII format output by the Parent Node of App_Wings.

    Name
    Description

    PKT_ERROR

    PulseCounter

    PulseCounter (mwx::periph_pulse_counter)

    The pulse counter is a circuit that reads and counts pulses even when the microcontroller CPU is not running. There are two systems of pulse counters: PC0 is assigned to PulseCounter0 and PC1 to PulseCounter1.

    PC0 is assigned to PulseCounter0 and PC1 is assigned to PulseCounter1.

    hashtag
    method

    hashtag
    begin()

    Initializes the object and starts counting. the first parameter refct is the count number on which interrupts and available decisions are based. When this number is exceeded, it is reported to the application. You can also set refct to 0. In this case, it is not a sleep wake-up factor.

    The second parameter edge specifies whether the interrupt is rising (PIN_INT_MODE::RISING) or falling (PIN_INT_MODE::FALLING).

    The third debounce takes the values 0, 1, 2, or 3. 1, 2, or 3 settings require the same consecutive value to detect a change in value to reduce the effect of noise.

    setting
    number of consecutive samples
    maximum detection frequency

    hashtag
    end()

    Discontinue detection.

    hashtag
    available()

    If the specified count (refct in begin()) is 0, true is returned when the count exceeds 1.

    If the specified count (refct in begin()) is 1 or more, true is returned when the number of detections exceeds the specified count.

    hashtag
    read()

    Reads the count value. Resets the count value to 0 after reading.

    on_rx_packet()

    on_rx_packet()

    Receives incoming packets.

    void on_rx_packet(mwx::packet_rx& pkt, bool_t &b_handled) 

    When a wireless packet is received, this function is called from within the MWX library with the data stored in pkt as packet_rx. If this function is not defined in the application, a weak function that does nothing is linked.

    Setting b_handled to true in this function tells the MWX library that the incoming packet has been processed in the application. If set to processed', it suppresses unnecessary processing. (Do not process the_twelite.receiver`)

    circle-exclamation

    When using , use the callback function in BEHAVIOR.

    circle-exclamation

    The the_twelite.receiver is not recommended.

    The receiver was previously processed by the_twelite.receiver with the intention of describing it in loop(). However, on_rx_packet() was added because it is a delayed processing by a queue, which in principle causes overflow, and also because it tends to be complicated to describe.

    MWX_APIRET

    API return value class that wraps 32-bit type. MSB (bit31) is a flag for failure or success. bit0..30 is used to store the return value.

    hashtag
    constructor

    MWX_APIRET()
    MWX_APIRET(bool b)
    MWX_APIRET(bool b, uint32_t val)

    The default constructor is constructed with a combination of false and 0.

    It can also be explicitly constructed with bool and uint32_t types as parameters.

    Since the constructor of type bool is implemented, you can use true/false as follows.

    hashtag
    Methods

    hashtag
    is_success(), operator bool()

    Return true if 1 is set in MSB.

    Return true if MSB is 0.

    Obtain the value part of bit0..30.

    About the MWX library

    The MWX library is designed to make the programming of TWELITE modules easier and more extensible. Based on the TWENET C library that has been used in the MWSDK, the MWX library has been developed as a library for the application development layer.

    The name of the MWX library is Mono Wireless C++ Library for TWELITE, where MW comes from MonoWireless, and C++ -> CXX -> double X -> WX, where MW and WX are combined to form MWX.

    The code written using this library is called "ACT".

    hashtag

    packet_rx

    This class is a wrapper class for TWENET's tsRxDataApp structure.

    This class object is a wrapper class for callback function or by .

    In packet_rx, in particular, the data payload of the packet can be handled by the smplbuf container, and utility functions such as expand_bytes() simplify the payload interpretation description.

    Installing the TWELITE SDK

    Installing the TWELITE SDK

    Download the TWELITE STAGE SDK distribution archive (e.g. ZIP) and extract it to an appropriate folder.

    circle-exclamation

    The folder names of each level of the destination folder must not contain anything other than one-byte numbers 0..9, one-byte alphabets a..zA..Z, and some symbols -_.. In short, it must not contain spaces, Kanji characters, Hiragana characters, etc.

    TwePacketTwelite

    The TwePacketTwelite class is a standard application , which is an interpretation of the TwePacketTwelite class.

    Various information in the packet data is stored in DataTwelite after parse<TwePacketTwelite>() is executed.

    hashtag

    attachIntDio()

    to enable DIO interrupt.

    Enables DIO interrupts.

    For a preconfigured pin, the first parameter is the pin number for which you want to enable interrupts, the second is the interrupt direction (.

    circle-info

    Interrupt handlers and event handlers are written in .

    pinMode()

    Sets the DIO (general-purpose digital IO) pin.

    Sets the DIO (general-purpose digital IO) pin.

    This function allows you to change the state of DIO0..19 and the pins DO0,1. The setting contents are described in the enumeration value of E_PIN_MODE, and .

    triangle-exclamation

    DO0,1 are special pins, which in principle are used for other purposes, but can also be configured as outputs. However, these pins have hardware restrictions, so care must be taken when using them.

    class MWX_APIRET {
    	uint32_t _code;
    public:
    	MWX_APIRET() : _code(0) {}
    	MWX_APIRET(bool b) {
    	  _code = b ? 0x80000000 : 0;
      }
    	MWX_APIRET(bool b, uint32_t val) {
    		_code = (b ? 0x80000000 : 0) + (val & 0x7fffffff);
    	}
    	inline bool is_success() const { return ((_code & 0x80000000) != 0); }
    	inline bool is_fail() const { return ((_code & 0x80000000) == 0); }
    	inline uint32_t get_value() const { return _code & 0x7fffffff; }
    	inline operator uint32_t() const { return get_value(); }
    	inline operator bool() const { return is_success(); }
    };

    TwePacket does not contain meaningful data such as before packet interpretation or packet type cannot be identified.

    PKT_TWELITE

    0x81 commandarrow-up-right of the standard application App_Twelite is interpreted.

    PKT_PAL

    Interpret serial format of TWELITE PALarrow-up-right

    PKT_APPIO

    UART messagearrow-up-right of remote control application App_IOarrow-up-right products/ TWE-APPS/App_IO/uart.html) interpreted by the remote control application App_IOarrow-up-right.

    PKT_APPUART

    Extended formatarrow-up-right of the serial communication application App_UART arrow-up-rightproducts/ TWE-APPS/App_Uart/mode_format.html) is interpreted.

    PKT_APPTAG

    The UART message of the wireless tag application App_Tag is interpreted. The sensor specific part is not interpreted and the byte string is reported as payload.

    PKT_ACT_STD

    Output format used in Act (Act)arrow-up-right sample, etc.

    BEHAVIOR
    (256 if not specified).

    buf_tx

    FIFO buffer size for TX

    buf_rx

    FIFO buffer size for RX

    speed

    Specifies the baud rate of the UART.

    config

    When the serial_jen::E_CONF::PORT_ALT bit is specified, UART1 is initialized with DIO14,15. If not specified, initializes UART1 with DIO11(TxD),9(RxD).

    //..Continuation of the above example
    // ABCD;0123;99 <- stored in b
    
    //Variable for storing read data
    uint8_t FOURCHARS_READ[4];
    uint32_t u32val_read;
    uint8_t c_read[2];
    
    // Read out by operator>>
    bs.rewind();                //Rewind the position to the beginning.
    bs >> FOURCHARS_READ;      //4 chars
    bs >> mwx::null_stream(1); //1 char skipping
    bs >> u32val_read;         //32bit data
    bs >> mwx::null_stream(1); //1 char skipping
    bs >> c_read;              //2 chars
    
    // Result
    Serial << crlf << "4chars=" << FOURCHARS_READ;
    Serial << crlf << format("32bit val=0x%08x", u32val_read);
    Serial << crlf << "2chars=" << c_read;
    
    // 4chars=ABCD
    // 32bit val=0x30313233
    // 2chars=99
    To describe the processing of the interrupt handler, specify it in the
    definition.
    application behavior

    8

    1.2Khz

    0

    -

    100Khz

    1

    2

    3.7Khz

    2

    4

    2.2Khz

    3

    MWX_APIRET myfunc() {
      if (...) return true;
      else false;
    }
    Both pins must be guaranteed to be at a HIGH level when power is applied. If the circuit is configured to take unstable voltages, the module will not start up.
    void pinMode(uint8_t u8pin, E_PIN_MODE mode)
    description of DIO
    Description of DO
    circle-info

    At this time, we have implemented methods and other interfaces, mainly those required for the simple relay network <NWK_SIMPLE>.

    hashtag
    Methods

    hashtag
    get_payload()

    Get the data payload of the packet.

    circle-info

    If <NWK_SIMPLE> is used, there is header data for <NWK_SIMPLE> at the beginning. The container referred to in the return will be a sub-array excluding the header. If you want to refer to the header part, refer to the tsRxDataApp structure by get_psRxDataApp().

    hashtag
    get_psRxDataApp()

    Obtain the receiving structure of the TWENET C library.

    hashtag
    get_length()

    Returns the data length of the payload. The value is the same as .get_payload().size().

    hashtag
    get_lqi()

    Obtain the LQI value (Link Quality Indicator).

    circle-info

    LQI is a value that indicates the quality of radio communication, expressed as a number from 0 to 255.

    Incidentally, if you want to evaluate it in several levels, you can classify it as follows: less than 50 (bad - less than 80 dbm), 50 to 100 (somewhat bad), 100 to 150 (good), 150 or more (near the antenna), and so on. Please note that these are only guidelines.

    hashtag
    get_addr_src_long(), get_addr_src_lid()

    Get the address of the sender.

    get_addr_src_long() is the serial number of the sender and MSB(bit31) is always 1.

    get_addr_src_lid() is the logical ID of the sender and takes values from 0x00-0xFE (the logical ID specified by <NWK_SIMPLE>).

    hashtag
    get_addr_dst()

    Gets the destination address.

    The destination address is specified by the source, and the range of values varies depending on the type of destination.

    Value
    Explanation

    MSB (bit31) is set.

    0x00-0xFF

    Logical ID (8bit) is specified as the destination.

    0x00-0xFF

    hashtag
    is_secure_pkt()

    Returns true for encrypted packets and false for plaintext.

    hashtag
    get_network_type()

    Returns network type of the packet identified by Network BEHAVIOR.

    Value
    Explanation

    mwx::NETWORK::LAYERED

    packets from <NWK_LAYERED>

    mwx::NETWORK::SIMPLE

    packets from <NWK_SIMPLE>

    mwx::NETWORK::NONE

    neworkless packets

    others

    error or unknow packet type

    behavior
    on_rx_packets()
    DataTwelite structure
    class TwePacketTwelite : public TwePacket, public DataTwelite { ... };
    0x81 command of App_Twelitearrow-up-right
    hashtag
    Example

    Set up an interrupt to be generated when the DIO5 pin changes from HIGH to LOW.

    hashtag
    myAppClass.hpp

    Basic definition of the application behavior myAppClass. Details are omitted.

    hashtag
    myAppClass.cpp

    Description of the interrupt handler of the application behavior myAppClass, which inverts the output setting of DIO12 when an interrupt of DIO5 is generated and displays * on the serial port Serial for events occurring after the interrupt handler is finished.

    void attachIntDio(uint8_t u8pin, E_PIN_INT_MODE mode)
    rising, falling
    Application BEHAVIOR
    void setup(uint16_t buf_tx, uint16_t buf_rx)
    void begin(unsigned long speed = 115200, uint8_t config = 0x06)
    TWE_tsFILE* get_tsFile();
    void setup()
    void begin(uint16_t u16Hz, bool b_sw_int = true, bool b_pwm_out = false)
    void end()
    inline bool available()
    void change_duty(uint16_t duty, uint16_t duty_max = 1024)
    void change_hz(uint16_t hz, uint16_t mil = 0) 
    struct DataAppUART {
    		/**
    		 * source address (Serial ID)
    		 */
    		uint32_t u32addr_src;
    
    		/**
    		 * source address (Serial ID)
    		 */
    		uint32_t u32addr_dst;
    
    		/**
    		 * source address (logical ID)
    		 */
    		uint8_t u8addr_src;
    
    		/**
    		 * destination address (logical ID)
    		 */
    		uint8_t u8addr_dst;
    
    		/**
    		 * LQI value
    		 */
    		uint8_t u8lqi;
    
    		/**
    		 * Response ID
    		 */
    		uint8_t u8response_id;
    
    		/**
    		 * Payload length
    		 */
    		uint16_t u16paylen;
    
    		/**
    		 * payload
    		 */
    #if MWX_PARSER_PKT_APPUART_FIXED_BUF == 0
    		mwx::smplbuf_u8_attach payload;
    #else
    		mwx::smplbuf_u8<MWX_PARSER_PKT_APPUART_FIXED_BUF> payload;
    #endif
    	};
    void begin(uint16_t refct = 0, 
               E_PIN_INT_MODE edge = PIN_INT_MODE::FALLING,
               uint8_t debounce = 0)
    void end()
    inline bool available()
    uint16_t read()
    inline bool is_success() 
    inline operator bool()
    inline bool is_fail()
    inline uint32_t get_value()
    inline operator uint32_t()
    smplbuf_u8_attach& get_payload()
    const tsRxDataApp* get_psRxDataApp() 
    uint8_t get_length()
    uint8_t get_lqi()
    uint32_t get_addr_src_long()
    uint8_t get_addr_src_lid()
    uint32_t get_addr_dst()
    bool is_secure_pkt()
    uint8_t get_network_type() 
    struct DataTwelite {
    		//Serial # of sender
    		uint32_t u32addr_src;
    		
    		// Logical ID of the sender
    		uint8_t u8addr_src;
    
    		// Destination logical ID
    		uint8_t u8addr_dst;
    
    		// Timestamp at time of transmission
    		uint16_t u16timestamp;
    
    		// Flag for low latency transmission
    		bool b_lowlatency_tx;
    
    		// Number of repeat relays
    		uint16_t u8rpt_cnt;
    
    		// LQI
    		uint16_t u8lqi;
    
    		// DI status (true is active Lo,GND)
    		bool DI1, DI2, DI3, DI4;
    		// DI status bitmap (DI1,2,3,4 in order from LSB)
    		uint8_t DI_mask;
    
    		// true if DI active (has been active in the past)
    		bool DI1_active, DI2_active, DI3_active, DI4_active;
    		// Active bitmap of DI (DI1,2,3,4 in order from LSB)
    		uint8_t DI_active_mask;
    
    		// Supply voltage of the module [mV].
    		uint16_t u16Volt;
    
    		// AD value [mV]
    		uint16_t u16Adc1, u16Adc2, u16Adc3, u16Adc4;
    		// Bitmap that is set to 1 if AD is active (AD1,2,3,4 in order from LSB)
    		uint8_t Adc_active_mask;
    };
    void setup() {
      the_twelite.app.use<myAppClass>();
      
      pinMode(PIN_DIGITAL::DIO5, PIN_MODE::INPUT_PULLUP);
      attachIntDio(PIN_DIGITAL::DIO5, PIN_INT_MODE::FALLING);
    }
    
    void loop() {
      ;
    }
    class myAppClass: public mwx::BrdPal, MWX_APPDEFS_CRTP(myAppClasslMot)
    {
    
    };
    /*****************************************************************/
    // MUST DEFINE CLASS NAME HERE
    #define __MWX_APP_CLASS_NAME myAppClass
    #include "_mwx_cbs_cpphead.hpp"
    /*****************************************************************/
    
    MWX_DIO_INT(PIN_DIGITAL::DIO5, uint32_t arg, uint8_t& handled) {
      static uint8_t ct;
      digitalWrite(PIN_DIGITAL::DIO12, (++ct & 1) ? HIGH : LOW);
    	handled = false; // if true, no further event.
    }
    
    MWX_DIO_EVENT(PIN_DIGITAL::DIO5, uint32_t arg) {
      Serial << '*';	
    }
    
    /*****************************************************************/
    // common procedure (DO NOT REMOVE)
    #include "_mwx_cbs_cpptail.cpp"
    // MUST UNDEF CLASS NAME HERE
    #undef __MWX_APP_CLASS_NAME
    } // mwx
    /*****************************************************************/
    hashtag
    Notation, etc.

    This section describes the notation used in this explanation.

    hashtag
    auto&&

    This is called a universal reference, and is often used in standard libraries. In our library, auto&& is used in most cases

    circle-info

    The "auto" is a keyword used to declare local variables (automatic variables) in the C language, but in this case it means to declare them by type inference. auto is useful in situations where the C++ template syntax often results in very cumbersome type names, and at the same time can be implemented without explicitly stating the type name.

    The following example uses std::minmax_element, a standard library algorithm for finding the maximum-minimum value for a type v, and declares the resulting return value by auto. In this case, the type deduced by auto will be std::pair<int, int>.

    For the strict meaning of && in auto &&, you will need to refer to technical books, etc. Here, please think of it as "you can declare the return value without worrying about whether it is a reference type (similar to pointer passing in C) or a value.

    hashtag
    about namespace

    The namespace, inline namespace, and using are used to redefine names and so on. Some of them are abbreviated in the explanation.

    hashtag
    Restrictions (TWENET)

    The MWX library has not been developed to support all the functions of the underlying libraries and functions (functions in the TWNET C library, microcontroller peripheral functions provided by semiconductor vendors, and IEEE802.15.4 functions).

    hashtag
    Restrictions (Using C++)

    The MWX library is written in the C++ language, and the act description is also written in C++. However, not all functions are available even in the C++ language. Please pay particular attention to the following points.

    • You can allocate memory with the new and new[] operators, but you cannot destroy the allocated memory, and most C++ libraries that allocate memory dynamically are virtually unusable.

    • The constructor of the global object is not called. Note: If necessary, initialization can be done including a call to the constructor by initializing it as in the initialization function ((void*)&obj_global) class_foo();).

    • exception cannot be used.

    • Unable to use virtual functions.

    • Due to the above limitations, only some of the C++ standard libraries such as STL can be used.

    ※ This is a description of what we know.

    circle-exclamation

    We have not conducted a comprehensive verification of the standard library as to whether it can be used or not, nor what might be available. If you find any inconvenience in operation, please try to implement it in another way.

    hashtag
    about the library source code

    The source code can be found at the followings:

    • {MWSDK install dir}/TWENET/current/src/mwx

    • https://github.com/monowireless/mwxarrow-up-right

    The following is an example folder after extracting the TWELITE STAGE SDK archive. (Windows, c:\work\MWSTAGE...)

    See Installing the TWELITE STAGE SDKarrow-up-right for more information.

    circle-exclamation

    This is the end of the normal installation process. Refer to the following "Set Environmental variables" and below as necessary.

    hashtag
    Set Environmental variables

    If you want to build with other than TWELITE STAGE application, please set the environment variables (e.g. MWSDK_ROOT).

    circle-info

    If you are using the TWELITE STAGE application, you do not need to set these environment variables. Inside the TWELITE STAGE application, those environment variables will be set appropriately.

    MWSDK_ROOT, MWSDK_ROOT_WINNAME(for Windows10) need to be set.

    hashtag
    Windows10,11

    In this example, the name of the extracted folder is C:\MWSTAGE. If you have installed the software in a different folder, please change the name.

    Run C:\MWSTAGE\Tools\SET_ENV.CMD to set the following environment variables:

    • MWSDK_ROOT

    • MWSDK_ROOT_WINNAME

    For example, the following variables are set:

    circle-exclamation

    To uninstall the TWELITE STAGE SDK from the installed PC, please do the following:

    • Run UNSET_ENV.cmd. This will unset the environment variables.

    • Delete the MWSTAGE folder.

    hashtag
    Linux

    Set the development environment and shell to reflect the MWX_ROOT environment variable.

    There are several ways to do this, but add the following setting to the .profile of your home folder (if the file does not exist, please create a new one). You can even build VSCode with this setting.

    MWSDK_ROOT=/foo/bar/MWSTAGE/MWSDK/ export MWSDK_ROOT

    To add it without using the editor, enter the command as follows. The $ is a prompt and will be displayed differently depending on your environment. The /foo/bar/MSWSDK part should be rewritten according to the installed folder.

    hashtag
    macOS

    Set the development environment and shell to reflect the MWX_ROOT environment variable.

    There are several ways to do this, but add the following settings to .profile in your home folder (create a new file if you don't have one). You can even build VSCode with this setting.

    MWSDK_ROOT=/foo/bar/MWSTAGE/MWSDK/ export MWSDK_ROOT

    To add it without using the editor, enter the command as follows. The $ is a prompt and will be displayed differently depending on your environment. The /foo/bar/MSWSDK part should be rewritten according to the installed folder.

    circle-info

    Use LaunchD to apply MWSDK_ROOT to the entire environment.

    Some settings in VSCode refer to environment variables, but they are not required for the build.

    macOSarrow-up-right
    Windowsarrow-up-right
    Linuxarrow-up-right

    EEPROM

    EEPROM

    TWELITE Reads and writes to the built-in EEPROM of the wireless microcontroller.

    The built-in EEPROM has 3480 bytes available from address 0x000 to 0xEFF.

    The first part is Settings (Interactive settings mode), so it is recommended to use the second half of the address when used together. The amount of space consumed by the settings (Interactive settings mode) depends on its implementation. Even with minimal settings, up to 256 bytes are used from the beginning, so use of the later addresses is recommended.

    hashtag
    Methods

    hashtag
    read()

    Read the data corresponding to address from EEPROM.

    circle-exclamation

    No error detection.

    hashtag
    write()

    Write value from EEPROM to address.

    circle-exclamation

    No error detection.

    hashtag
    update()

    This function is used when you want to reduce the number of rewrites in consideration of the rewrite life of EEPROM.

    hashtag
    get_stream_helper()

    Obtain a helper object to read and write using mwx::stream described below.

    hashtag
    mwx::streamインタフェースを用いた入出力

    operators and methods. Using mwx::stream, you can read and write integer types such as uint16_t and uint32_t types, read and write fixed-length array types such as uint8_t, and format them using format() objects.

    Interfaces defined in mwx::stream, such as the << operator, can be used on this object.

    .seek() is used to move the EEPROM address to 1024.

    The above writes an 8-byte string (00bc614e), a 4-byte integer (0x12ab34cd), a 16-byte byte string (HELLO WORLD!.... ), and a 1-byte terminating character.

    Move the EEPROM address to 1024 using .seek().

    Reads the data sequence written out earlier. In order, 8-byte characters, 4-byte integers, and 16-byte strings are read out using the >> operator.

    Unit_???

    An ACT (Act) beginning with _Unit_ is used to describe a very single function or to check the operation of a function.

    Name
    Content

    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

    , and compare with the count of 16Mhz TickTimer.

    SPI (using helper class)

    SPI access (using helper class)

    The helper class version is a more abstract implementation. Creating a transceiver object for reading and writing is the start of using the bus, and destroying the object is the end of using the bus.

    By creating the object in the decision expression of the if statement, the validity period of the object is limited to the scope of the if clause, and the object is destroyed when it exits the if clause, at which point the bus usage termination procedure is performed.

    In addition, read/write objects implement the mwx::stream interface, allowing the use of the << operator, etc.

    • The start and end of bus usage is aligned with the validity period of the object to improve the visibility of the source code and to prevent omissions in the termination procedure, etc.

    • Unify read/write procedures with the mwx::stream interface

    hashtag
    read/write

    Reading process and its termination procedure in scope if() { ... } to do the reading with a helper class.

    In the above, the x object created by the get_rwer() method is used to read and write one byte at a time. 1.

    1. create x object in if(...) Generate the x object in the get_rwer() method. At the same time, set the select pin of the SPI bus. (The type is resolved with the universal reference auto&& by type inference. 2.)

    2. the generated x

    hashtag
    get_rwer()

    Obtains the worker object used to read/write the SPI bus.

    hashtag
    worker object

    hashtag
    transfer() transfer16() transfer32()

    Each transfers 8-bit, 16-bit, and 32-bit data, and returns the read result with the same data width as the written data width.

    hashtag
    << operator

    The int and uint8_t types perform 8-bit transfer.

    Types uint16_t and uint32_t perform 16-bit and 32-bit transfers, respectively.

    The transfer result is stored in an internal FIFO queue of up to 16 bytes and read by the >> operator. Since the buffer is not large, it is assumed to be read out after each transfer.

    hashtag
    >> operator

    Specify a variable with the same data width as the previous transfer.

    If the result of the read is not needed, use the null_stream() object; it skips reading by the data byte specified by i. If the result of the read is not needed, use the null_stream() object.

    Wire

    Read/write two-wire serial (I2C) master (mwx::periph_wire)

    Reads and writes two-wire serial (I2C) master.

    hashtag
    Alias Definition

    The mwx::periph_wire<MWX_TWOWIRE_RCVBUFF> can be referred as TwoWire.

    hashtag
    Type Definitions

    The following definition types describe the types of arguments and return values.

    hashtag
    hint style="warning %}

    circle-exclamation

    Some APIs make calls where the STOP bit is not strictly handled.

    circle-info

    write(), writer::operator() () has several arguments defined in addition to the ones described here.

    • fixed array type uint8_t cmds[]={11,12}; ... Wire.write(cmds);

    hashtag
    Initialization and termination

    hashtag
    Wire Instance Creation

    Instance creation and necessary initialization is done in the library. In user code, it is made available by calling Wire.begin().

    When using the requestFrom() method, you can specify the size of the FIFO queue for temporary data storage. At compile time, compile the macro MWX_TWOWIRE_BUFF with the required number of bytes. The default is 32 bytes.

    Example: -DMWX_TWOWIRE_BUFF=16

    hashtag
    begin()

    Initialize hardware.

    triangle-exclamation

    Performing any Wire operation without initialization will cause the TWELITE radio module to hang.

    circle-exclamation

    When waking from sleep, if the module was operating just before sleep, it will return to the previous state.

    Parameters
    Description

    hashtag
    Example

    hashtag
    Reading and Writing

    There are two types of read/write procedures. Select and use one of them.

    • requestFrom(), beginTransmission(), endTransmission(), write()

    • reader, writer

    hashtag

    hashtag
    Others

    hashtag
    Probe(Determine presence of device)

    Checks if the device specified by address is responding. If the device exists, true is returned.

    hashtag
    setClock()

    The procedure is originally intended to change the bus frequency, but no action is taken.

    SPI

    Reads and writes the SPI bus (as Controller).

    Reads and writes the SPI bus (as Controller).

    hashtag
    Constants

    Constant
    Meaning

    const uint8_t SPI_CONF::MSBFIRST

    hashtag
    Initialization and termination

    The procedure for using the SPI bus depends on the begin() method.

    Reads and writes the SPI bus (MASTER).

    hashtag
    begin()

    Initialize hardware.

    circle-exclamation

    This process is also required after sleep recovery.

    Parameters
    Description

    hashtag

    hashtag
    Example

    hashtag
    end()

    Terminate the use of SPI hardware.

    hashtag
    Reading and Writing

    There are two types of read/write procedures. Select and use one of them.

    • beginTransaction(), endTransaction(), transfer(), transfer16(), transfer32()

    • transceiver

    Wire (using member functions))

    Wire (using member function)

    The method using member functions has a relatively low level of abstraction and follows the general API system as provided by the C library. The procedures for operating the two-wire serial bus are more intuitive.

    However, it is necessary to be explicitly aware of the start and end of bus usage.

    hashtag
    Read

    packet_tx

    This class is a wrapper class for the tsTxDataApp structure of the TWENET C library, and objects of derived classes based on this class are obtained from network behaviors or .

    hashtag
    Generating objects

    This is done by the network behavior .prepare_tx_packet().

    stream_helper

    stream_helper is a helper object that grants the mwx::stream interface. It creates a helper object that references a data class and performs data input/output via the helper object.

    The following creates a helper object bs from an array b of smplbufs and inputs data using the mwx::stream::operator <<() operator.

    hashtag

    pktparser

    pktparser(parser_packet) performs content interpretation on the byte sequence converted by performs content interpretation on the byte sequence converted by .

    The above example interprets . parser_ser object converts the message input from Serial into a byte string. This byte string is first identified by to determine the type of the message . Once the message type is determined, the message is parsed by .parse<TwePacketTwelite>(). The parsed result will be of type TwePacketTwelite, and .use<TwePacketTwelite>() is the procedure to extract this object. The TwePacketTwelite type is a class, but it refers to its member variables directly as a structure.

    PulseCounter

    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.

    hashtag
    Function of ACT

    PAL_AMB-usenap

    The sample in is slightly modified so that the waiting time (approx. 50 ms) during sensor data acquisition is replaced by a sleep wait.

    circle-check

    Please see the explanation of the ACT in before the explanation of this ACT.

    hashtag

       Act (USER APPs)... 
    +-----------------------+
    | MWX C++ LIB           |
    +---------------+       |
    | TWENET C LIB  |       |
    +------------+----------+
    | MAC LAYER  | AHI APIs |
    +-----------------------+
    | TWELITE HARDWARE      |
    +-----------------------+
    MWSDK_ROOT=C:/MWSTAGE/MWSDK/
    MW_ROOT_WINNAME=C:\MWSTAGE\MWSDK\
    $ cd $HOME
    $ echo MWSDK_ROOT=/foo/bar/MWSTAGE/MWSDK>>.profile
    $ echo export MWSDK_ROOT>>.profile
    $ cd $HOME
    $ echo MWSDK_ROOT=/foo/bar/MWSTAGE/MWSDK>>.profile
    $ echo export MWSDK_ROOT>>.profile
    uint8_t c;
    if (auto&& trs = SPI.get_rwer()) { // Object creation and device communication determination
       // Within this scope (wave brackets) is the validity period of trs.
       trs << 0x00; // Writes 0x00 with mwx::stream interface
       trs >> c;    // Store the read data in c.
    } 
    // wrt is discarded at the point where the if clause is exited, and the use of the bus is terminated
    using TwoWire = mwx::periph_twowire<MWX_TWOWIRE_RCVBUFF>;

    Unit_brd_CUE

    Check the operation of the acceleration sensor, magnetic sensor, and LED of TWELITE CUEarrow-up-right. 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

    UART1 (Serial1) is a sample of using UART0 (Serial) outputs input from UART1 to UART1 and input from UART1 to UART0.

    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.

    delayMicroseconds()
    stream_helper
    helper object
    mwx::stream
    object has a
    operator bool ()
    defined, which is used to evaluate the decision expression, which is always
    true
    for the SPI bus.
  • x object defines uint8_t transfer(uint8_t) method, which is called to perform 8bit read/write transfer to SPI. 4. 4.if() { ... } Destructor of x is called at the end of the scope to release the select pin of the SPI bus.

  • initializer_list<> 型 Wire.write({11,12})

  • u8mode

    Specifies the bus frequency. Default is 100Khz (WIRE_CONF::WIRE_100KHZ) The frequency is specified by WIRE_CONF::WIRE_? KHZ and ? KHZ can be 50,66,80,100,133,160,200,266,320,400.

    b_portalt

    Change hardware pin assignments.

    Member function (input/output using the following member functions)
    Helper class (stream function available)

    MSB as the first bit

    const uint8_t SPI_CONF::LSBFIRST

    make LSB the first bit

    const uint8_t SPI_CONF::SPI_MODE0

    set SPI MODE 0

    const uint8_t SPI_CONF::SPI_MODE1

    set to SPI MODE 1

    const uint8_t SPI_CONF::SPI_MODE2

    set to SPI MODE 2

    const uint8_t SPI_CONF::SPI_MODE3

    set to SPI MODE 3

    slave_select

    Specify the SPI slave select pin to be used. 0 : DIO19, 1 : DIO0 (DIO 19 is reserved), 2 : DIO1 (DIO 0,19 is reserved)

    settings

    Specifies the SPI bus setting. The clock[hz] specifies the SPI bus frequency. A divisor closer to the specified frequency will be selected: 16Mhz or 16Mhz divided by an even number. bitOrder specifies SPI_CONF::MSBFIRST or SPI_CONF::LSBFIRST. dataMode specifies SPI_CONF::SPIMODE0..3.

    Member function version (input/output using the following member functions)
    Helper class version (stream function available)
    hashtag
    requestFrom()

    Reads the specified number of bytes at once. Since the result of reading is stored in a queue, call the .read() method immediately afterward until the queue is empty.

    Parameters
    Description

    u8address

    I2C address to be read

    length

    Number of bytes read

    b_send_stop=true

    When true, the STOP bit is set at the end of reading.

    return type size_type

    Number of bytes read. 0 means read failure.

    hashtag
    Code Example

    hashtag
    Writing

    Writing is performed by the write() method after executing beginTransmission(). Call endTranmission() after a series of writing is finished.

    hashtag

    hashtag
    beginTransmission()

    Initialize the export transfer. Call endTransmission() as soon as the writing process is finished.

    Parameters
    Description

    u8address

    I2C address to be written out

    hashtag

    hashtag
    write(value)

    Writes one byte.

    Parameters
    Description

    Return value value

    Bytes to be written.

    Return value size_type

    Number of bytes written. A value of 0 is an error.

    hashtag
    write(*value, quantity)

    Writes a byte sequence.

    Parameters
    Description

    *value

    the byte sequence to be written

    Return value size_type

    Number of bytes to be written.

    Return value size_type

    Number of bytes written. 0 is an error.

    hashtag
    endTransmission()

    Processes the end of the export.

    Parameters
    Description

    sendStop = true

    Issue the STOP bit.

    Return value uint8_t

    0: Success 4: Failure

    In the above example, the_twelite.network.use<NWK_SIMPLE>() retrieves an object of the network behavior. The object pkt is created by .prepare_tx_packet() of this object. It is a derived class of packet_tx, although the type name is inferred by auto&&.

    This pkt object first returns true or false in the if(). A false return occurs when the queue for sending is full and no more requests can be added.

    hashtag
    Transmission Settings

    Various settings are configured in the radio packet to deliver the packet to the destination, including destination information. To configure the settings, an object containing the settings is given as the right-hand side value of the << operator.

    The following is a description of the objects used for configuration.

    circle-exclamation

    The availability and implications of each setting depend on the network behavior specification.

    hashtag
    tx_addr

    Specify the destination address addr. See the Network Behavior specification for the destination address value.

    • <NWK_SIMPLE> An address set with an MSB (bit31=0x80000000) means that it is addressed to the serial number of the radio module. 0x00. 0xFE means that the address is addressed to the child module (0x01...0xEF). 0xFE means broadcast to the child (0x01...0xEF), and 0xFF` means broadcast to both the parent and the child.

    hashtag
    tx_retry

    Specifies the number of retransmissions. The number of retransmissions is specified by u8count. force_retry is a setting to retransmit a specified number of times regardless of whether the transmission is successful or not.

    • <NWK_SIMPLE> ネットワークビヘイビア<NWK_SIMPLE>では、同じ内容のパケットをu8count+1回送信します。 force_retryの設定は無視されます。

    hashtag
    tx_packet_delay

    Sets the delay between sending packets and the retransmission interval. Specify two values, u16DelayMin and u16DelayMax, in milliseconds [ms]. Transmission will be started at some point during this interval after a transmission request is made. Specify the retransmission interval as the value of u16RetryDur in milliseconds[ms]. The retransmission interval is constant.

    circle-exclamation

    The transmission process may not start at the specified timing due to internal processing. In addition, IEEE802.15.4 processing also causes time blurring before packets are created. These timing blurs are an effective means of avoiding packet collisions in many systems.

    Strict timing of packet transmission should be considered an exception due to the nature of the IEEE802.15.4 standard.

    • <NWK_SIMPLE> This specification is valid.

    circle-exclamation

    If the same packet is retransmitted and arrives more than 1 second after the first transmission, it is not deduplicated because a new packet has arrived. The same packet may be received after 1 second due to a longer retransmission interval or packet arrival delay in relay. You can configure the handling of duplicate packets by initializing the <NWK_SIMPLE> behavior.

    hashtag
    tx_process_immediate

    This setting requests that packet transmission be performed "as quickly as possible." Packet transmission processing in TWENET is performed from the TickTimer, which runs every 1 ms. By setting this parameter, the packet transmission request is processed as soon as possible after the request is made. Of course, any setting other than tx_packet_delay(0,0,0) is a meaningless specification.

    If other packet transmission processing is in progress, it will be processed as usual.

    • <NWK_SIMPLE> This designation is valid.

    hashtag
    tx_ack_required

    In wireless packet communications, there is a transmission method in which a short radio message called ACK (ACK) is obtained from the destination after the transmission is completed to indicate that the transmission was successful. By setting this option, transmission with ACK is performed.

    • <NWK_SIMPLE>

    circle-exclamation

    This specification is VALID for <NWK_SIMPLE>. It will result in a compile error. <NWK_SIMPLE> is intended to implement a simple working relay network and does not communicate with ACK.

    hashtag
    tx_addr_broadcast

    Specify broadcast.

    • <NWK_SIMPLE>

    circle-exclamation

    This specification is VALID for <NWK_SIMPLE>. It will result in a compile error.

    Specify the destination address tx_addr(0xFF) (broadcast) or tx_addr(0xFE) (broadcast to the child) instead.

    hashtag
    tx_packet_type_id

    Specify the type ID of the TWENET radio packet that can be 0..7.

    • <NWK_SIMPLE>

    circle-exclamation

    This specification is VALID for <NWK_SIMPLE>. It will result in a compile error.

    `` uses type ID internally. Users cannot use it.

    on_tx_comp()
    Overview

    stream_helper behaves as if the data array were a stream.

    Internally, it holds read/write positions in the data array. It behaves as follows.

    • After the last data is read or written, it moves to the next read/write position.

    • After the last data is read or data is appended to the end, the read/write position becomes the end.

    • If the read/write position is the end of the line, then

      • available() becomes false.

      • Read cannot be performed.

      • Writing is appended if it is within the writable range.

    hashtag
    generating stream_helper

    stream_helper is a data class (smplbuf, EEPROM) member functions.

    hashtag
    methods

    hashtag
    rewind()

    Moves the read/write position to the beginning.

    hashtag
    seek()

    Set the read/write position.

    whence
    Set the location

    MWX_SEEK_SET

    set from the leading position. If offset is 0, it means the same as rewind().

    MWX_SEEK_CUR

    Move by offset with respect to the current position.

    MWX_SEEK_END

    end position. Setting offset to 0 sets the end position. If -1 is set, it moves to the last character.

    hashtag
    tell()

    Returns the read/write position. Returns -1 for the end position.

    hashtag
    available()

    Returns 0 if the read/write position is the end. If it is not the end, it returns any other value.

    hashtag
    parse<T>

    Parses a sequence of bytes.

    The T specifies the packet type to be parsed. For example, TwePacketTwelite is specified for 0x81 messages in standard applications.

    p and e specify the next to the beginning and the end of the byte sequence.

    The return value is of type E_PKT. In case of an error, E_PKT::PKT_ERROR is returned.

    hashtag
    user<T>

    Returns a reference to an object corresponding to the packet type of the interpreted byte sequence. It can be called if parse<T> was executed beforehand and there were no errors.

    The T can be the same type as the one executed with parse<T>, or a TwePacket from which only basic information can be obtained.

    serparser
    serparser
    standard application 0x81 messagearrow-up-right
    identify_packet_type()arrow-up-right
    E_PKT
    Explanation of ACT

    hashtag
    begin()

    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.

    hashtag
    wakeup()

    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.

    circle-exclamation

    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.

    hashtag
    napNow()

    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.

    circle-info

    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.

    PAL_AMB
    PAL_AMB
    #include <algorithm>
    int v[] = { 1, 3, -1, 5, 10 };
    auto&& result = std::minmax_element(std::begin(v), std::end(v));
    Serial << "min=" << int(result.first)
           << ",max=" << int(result.second);
    uint8_t read(uint16_t address)
    void write(uint16_t address, uint8_t value)
    void update(uint16_t address, uint8_t value)
    auto&& get_stream_helper()
    // The return type is abbreviated as auto&& due to its length.
    auto&& strm = EEPROM.get_stream_helper();
    // Helper object type names are resolved by auto&& due to their length.
    strm.seek(1024); // Move to 1024th byte
    
    strm << format("%08x", 12345678); // Record 12345678 as 8 characters in hexadecimal
    strm << uint32_t(0x12ab34cd);     // Record 4 bytes of 0x12ab34cd
    uint8_t msg_hello[16] = "HELLO WORLD!";
    strm << msg_hello;                // Record "HELLO WORLD!" byte sequence (unterminated)
    
    // result
    // 0400: 30 30 62 63 36 31 34 65 12 ab 34 cd 48 45 4c 4c
    //        0  0  b  c  6  1  4  e  0x12ab34cd  H  E  L  L
    // 0410: 4f 20 57 4f 52 4c 44 21 00 00 00 00 ff ff ff ff
    //        O SP  W  O  R  L  D  ! 
    strm.seek(1024);
    
    uint8_t msg1[8];
    strm >> msg1;
    Serial << crlf << "MSG1=" << msg1;
    // MSG1=00bc614e
    
    uint32_t var1;
    strm >> var1;
    Serial << crlf << "VAR1=" << format("%08x", var1);
    // VAR1=12ab34cd
    
    uint8_t msg2[16]; // "HELLO WORLD!"の文字数
    strm >> msg2;
    Serial << crlf << "MSG2=" << msg2;
    // MSG2=HELLO WORLD!
    inline uint8_t _spi_single_op(uint8_t cmd, uint8_t arg) {
        uint8_t d0, d1;
        if (auto&& x = SPI.get_rwer()) {
            d0 = x.transfer(cmd); (void)d0;
            d1 = x.transfer(arg);
            // (x << (cmd)) >> d0;
            // (x << (arg)) >> d1;
        }
    
        return d1;
    }
    periph_spi::transceiver get_rwer()
    uint8_t transfer(uint8_t val)
    uint16_t transfer16(uint16_t val)
    uint32_t transfer32(uint32_t val)
    operator << (int c)
    operator << (uint8_t c)
    operator << (uint16_t c) 
    operator << (uint32_t c)
    operator >> (uint8_t& c)
    operator >> (uint16_t& c)
    operator >> (uint32_t& c)
    
    null_stream(size_t i = 1)
    operator >> (null_stream&& p)
    typedef uint8_t size_type;
    typedef uint8_t value_type;
    void begin(
        const size_type u8mode = WIRE_100KHZ,
        bool b_portalt = false)
    void setup() {
        ...
        Wire.begin();
        ...
    }
    
    void wakeup() {
        ...
        Wire.begin();
        ...
    }
    bool probe(uint8_t address)
    void setClock(uint32_t speed)
    void begin(uint8_t slave_select, SPISettings settings)
    SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
    void setup() {
      ...
      SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
      ...
    }
    
    void wakeip() {
      ...
      SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
      ...
    }
    void end()
    size_type requestFrom(
        uint8_t u8address,
        size_type length,
        bool b_send_stop = true)
    int len = Wire.requestFrom(0x70, 6);
    for (int i = 0; i < 6; i++) {
      if (Wire.available()) {
    		  au8data[i] = Wire.read();
        Serial.print(buff[i], HEX);
      }
    }
    // skip the rest (just in case)
    // while (Wire.available()) Wire.read(); // normally, not necessary.
    	#define DEV_ADDR (0x70)
    	const uint8_t msg[2] = 
    	  {SHTC3_SOFT_RST_H, SHTC3_SOFT_RST_L};
    
    	Wire.beginTransmission(DEV_ADDR);
    	Wire.write(msg, sizeof(msg));
    	Wire.endTransmission();
    void beginTransmission(uint8_t address)
    size_type write(const value_type value)
    size_type write(
      const value_type* value,
      size_type quantity)
    uint8_t endTransmission(bool sendStop = true)
    if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    	pkt << tx_addr(0x00)
    		<< tx_retry(0x1)
    		<< tx_packet_delay(0,50,10);
    		
    	pack_bytes(pkt.get_payload()
    		, make_pair("APP1", 4)
    		, uint8_t(u8DI_BM)
    	);
      
      pkt.transmit();
    }
    if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
      ...
    }
    pkt << tx_addr(0x00)
    	  << tx_retry(0x1)
      	<< tx_packet_delay(0,50,10);
    tx_addr(uint32_t addr)
    tx_retry(uint8_t u8count, bool force_retry = false)
    tx_packet_delay(uint16_t u16DelayMin,
                    uint16_t u16DelayMax,
                    uint16_t u16RetryDur)
    tx_process_immediate()
    tx_ack_required()
    tx_addr_broadcast()
    tx_packet_type_id(uint8_t)
    smplbuf_u8<32> b;
    auto&& bs = b.get_stream_helper(); // helper object
    
    // Data String Generation
    uint8_t FOURCHARS[]={'A', 'B', 'C', 'D'};
    bs << FOURCHARS;
    bs << ';';
    bs << uint32_t(0x30313233); // "0123"
    bs << format(";%d", 99);
    
    Serial << b << crlf; // Output to Serial via smplbuf_u8<32> class
    
    //result: ABCD;0123;99
    auto&& obj_helper = obj.get_stream_helper()
    // obj is an object of data class, obj_helper type is accepted by auto&& because it is long.
    void rewind()
    int seek(int offset, int whence = MWX_SEEK_SET)
    int tell()
    int available()
    serparser_heap parser_ser;
    
    void setup() {
        // init ser parser (heap alloc)
        parser_ser.begin(PARSER::ASCII, 256);
    }
    
    void loop() {
        int c;
        while ((c = Serial.read()) >= 0) {
            parser_ser.parse(c);
            if (parser_ser.available()) {
                // get buffer object
                auto&& payl = parser_ser.get_buf();
                // identify packet type
                auto&& typ = identify_packet_type(payl.begin(), payl.end());
                
                // if packet type is TWELITE standard 0x81 message
                if (typ == E_PKT::PKT_TWELITE) {
                    pktparser pkt; // packet parser object
                    // analyze packet data
                    typ = pkt.parse<TwePacketTwelite>(payl.begin(), payl.end());
                    
                    if (typ != E_PKT::PKT_ERROR) { // success!
                        // get data object
                        auto&& atw = pkt.use<TwePacketTwelite>();
                        
                        // display packet inforamtion
                        Serial << crlf << format("TWELITE: SRC=%08X LQI=%03d "
                            , app.u32addr_src, app.u8lqi);
    	                  Serial << " DI1..4="
    	                      << atw.DI1 ? 'L' : 'H' << atw.DI2 ? 'L' : 'H' 
                            << atw.DI3 ? 'L' : 'H' << atw.DI4 ? 'L' : 'H';
                    }
                }
            }
        }
    }
    template <class T>
    E_PKT parse(const uint8_t* p, const uint8_t* e)
    template <class T> 
    T& use()
    void begin() {
    	sleepNow(); // the first time is just sleeping.
    }
    void wakeup() {
    	if (!b_senser_started) {
    		// delete/make shorter this message if power requirement is harder.	
    		Serial	<< mwx::crlf
    				<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
    				<< mwx::crlf
    				<< "..start sensor capture again."
    				<< mwx::crlf;
    
    		startSensorCapture();
    		b_senser_started = true;
    
    		napNow(); // short period sleep.
    	} else {
    		Serial << "..wake up from short nap.." << mwx::crlf;
    
    		auto&& brd = the_twelite.board.use<PAL_AMB>();
    
    		b_senser_started = false;
    
    		// tell sensors waking up.
    		brd.sns_LTR308ALS.process_ev(E_EVENT_START_UP);
    		brd.sns_SHTC3.process_ev(E_EVENT_START_UP);
    	}
    }
    void napNow() {
    	uint32_t u32ct = 100;
    	Serial << "..nap " << int(u32ct) << "ms." << mwx::crlf;
    	the_twelite.sleep(u32ct, false, false, TWENET::SLEEP_WAKETIMER_SECONDARY);
    }
    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.

  • hashtag
    How to use ACT

    hashtag
    Required TWELITE

    Role
    Example

    Parent Node

    ACT in action.。

    Child Node

    1. 2. +

    hashtag
    Explanation of ACT

    hashtag
    setup()

    Initializes the pulse counter.

    hashtag
    begin()

    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.

    hashtag
    wakeup()

    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.

    hashtag
    loop()

    Reads the pulse count value. The counter is reset after the readout.

    pulse counter

    Build definition Makefile

    Build definition Makefile

    The Makefile is stored in build/Makefile and is pre-defined to build the act by running the make command.

    circle-info

    MWSDK 2020-04 automatically detects the .cpp file in the project folder, so there is usually no need to modify the Makefile.

    If the source file is to be stored in a subfolder, it will need to be edited.

    MWSDK 2019-12 requires you to edit the Makefile if you have more than one .cpp file.

    circle-exclamation

    After copying the project folder from another environment, make sure to delete the build/objs_??? folder. If any intermediate files from the other environment remain, make will fail.

    (MWSDK 2020-04) You can avoid errors by adding USE_APPDEPS=0 to clean and then running make again.

    hashtag
    Parameters for make.

    hashtag
    TWELITE=

    Specify the build target as BLUE or RED; for TWELITE BLUE, use make TWELITE=BLUE.

    hashtag
    all

    Run the build. Usually, you can omit this and use make TWELITE=BLUE.

    hashtag
    clean

    Remove intermediate files from the build. Do this as make TWELITE=BLUE clean.

    hashtag
    cleanall

    Remove all intermediate files. Do this as make cleanall, the same as removing all of the objs_??? folder in the build folder.

    hashtag
    USE_APPDEPS=0 or 1

    When set to 1 (the default), the build file is determined based on file dependencies. For example, if there is a change in a header file, the associated source file will be recompiled.

    If set to 0, the makefile will not error if there are inconsistent intermediate files left.

    hashtag
    Makefile definition

    Depending on the size of the act, and when defining behaviours, the source files are usually split and built separately.

    One of the build files is {project folder name.cpp}.

    If you want to define other files, edit the build/Makefile in your project folder.

    The above is an example Makefile with sample PAL_AMB-bhv.

    hashtag
    VERSION_???

    Specify the version number. This will be reflected in the build result file name.

    During compilation, it is passed as a definition like -DVERSION_MAIN=0 -DVERSION_SUB=1 -DVERSION_VAR=0.

    hashtag
    Adding source files

    circle-check

    (MWSDK 2020-04) If you do not place files in subfolders, you no longer need to specify additions. All .c .cpp files in the project file will be added.

    When you add a source file, you need APPSRC_CXX and APP_COMMON_SRC_DIR_ADD?.

    circle-info

    If you place source files in a subfolder, you must specify the folder APP_COMMON_SRC_DIR_ADD?.

    Append the name of the source file to `APPSRC_CXX'. This file name must not contain a folder name. Anything in a subfolder should also be specified without a folder (i.e. if the same filename is in a subfolder, the build will fail)

    Next, specify the search path if the source files are stored in a location other than the project folder. You can set up to four.

    The folder specification is relative to the Makefile.

    hashtag
    Compile and linker options

    A number of other options can be passed to the compiler linker.

    Designation
    Remarks

    Analogue

    ADC (mwx::periph_analogue.hpp)

    Analogue performs ADC and acquires values. It can continuously acquire multiple channels at a time, and can do so sequentially according to a timer or other cycle.

    hashtag
    Constant.

    hashtag
    Pin Definition.

    constant
    classification
    Pin name in standard apps
    circle-exclamation

    In the standard application (App_Twelite), the pin names ADC2/ADC3 in the semiconductor data sheet are AI3/AI2 to match the TWELITE DIP. Please note this.

    triangle-exclamation

    *1 ADC2/ADC3 pins for both digital and analog are subject to usage procedures and restrictions.

    Assume no pull-up on the pin to be used before the ADC starts. If this is not done, the pull-up voltage will always be observed in the ADC.


    In a normal circuit configuration, current leakage occurs during sleep.

    hashtag
    method.

    hashtag
    setup()

    Initializes ADCs. setup() starts the regulator inside the semiconductor, specifies the timer device for periodic execution, and specifies the callback function to be called when all ADCs on the specified channel have completed.

    parameter
    explanation

    hashtag
    begin()

    The first parameter specifies the port where the ADC is to be made. The port specification is a bitmap with the bits set corresponding to the port numbers mentioned in the pin definition.For example, if you want to get the values of two pins PIN_ANALOGUE::A2 and PIN_ANALOGUE::VCC, specify (1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC ). You can also use and write pack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC).

    After calling begin(), the first ADC processing starts immediately, and the processing of the next pin starts from its end interrupt. When all processing is finished (if specified), the callback function is called. It waits until the next timer interrupt occurs before starting a new ADC process.

    The second parameter specifies the number of timer interrupts before AC starts. For example, TickTimer is called every 1 ms, but if you specify 16 for the parameter, it will be processed every 16 ms.

    Starts ADC processing with default ADC pins (PIN_ANALOGUE::A1, PIN_ANALOGUE::A2). end() resumes the interrupted ADC processing.

    hashtag
    end()

    ADC processing is terminated and the regulator inside the semiconductor is stopped.

    hashtag
    available()

    The value of the ADC is set to true after the acquisition. After confirmation by this function, it is false until the next ADC completion.

    hashtag
    read(), read_raw()

    Reads ADC values. The parameter specifies the ADC pin to be read. read() returns the read value converted to mV and read_raw() returns the ADC value (0..1023).

    circle-info

    It is recommended to read Vcc with read(). This is because a special conversion formula must be applied to convert from read_raw() values to mV.

    circle-exclamation

    After ADC completion (available), if the value is read after a delay until near the timing when the next ADC process is executed, the next ADC value may be returned, because the ADC process is executed by an interrupt handler and the value is updated even during the loop() process.

    hashtag
    ADC interrupt handler.

    The ADC interrupt handler is set to periph_analogue::ADC_handler() when setup() is called.

    If a different handler is specified by the semiconductor peripheral library, it will not work properly.

    hashtag
    Sleep behavior

    If the ADC is in cyclic execution state by begin(), ADC processing is resumed after returning from sleep.

    DIO General purpose IO

    API for DIO (General-purpose digital IO)

    The following functions are used for general-purpose digital IO (DIO) operations.

    • pinMode()

    • digitalWrite()

    • digitalRead()

    • attachIntDio()

    • detachIntDio()

    hashtag
    Constants

    hashtag
    Pin name and number

    Definition
    Name

    hashtag
    Mode of pin(DIO0..19)

    The following enumeration values are handled with the type name E_PIN_MODE.

    Definition
    Pull-up
    Name

    hashtag
    Mode of the pin (DO0,1)

    The following enumeration values are handled with the type name E_PIN_MODE.

    Definition
    Name

    hashtag
    pin_state

    The following enumeration values are handled with the type name E_PIN_STATE.

    Definition
    Value
    Name

    hashtag
    Rising and falling edge of pin

    The following enumeration values are handled with the type name E_PIN_INT_MODE.

    Definition
    Name

    WirelessUART

    WirelessUART performs serial communication.

    hashtag
    ACT Features.

    • Communicate between two UART-connected TWELITEs in ASCII format.

    hashtag
    how to use act

    hashtag
    Required TWELITE

    Two of the following devices serially connected to a PC.

    • connected to UART with products/TWE-Lite-DIP/index.html) etc.

    circle-info

    Packets addressed to the Parent Node can also be received by .

    hashtag
    Explanation of ACT

    hashtag
    setup()

    Interactive settings mode is initialized. This sample provides two or more devices that have different logical device IDs (LIDs) from each other.

    Initialize .

    hashtag
    loop()

    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.

    hashtag
    on_rx_packet()

    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.

    hashtag
    command for testing

    circle-exclamation

    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.

    circle-info

    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.

    hashtag
    example

    Send 00112233 to any Child Node.

    hashtag
    example

    Send AABBCC00112233 to Child Node #3.

    hashtag
    example

    Sent to any Parent Node or Child Node (0xFF) and to the Parent Node (0x00).

    TwePacketPAL

    The TwePacketPal class interprets TWELITE PAL packet data. This class handles TWELITE PAL (sensor data and other upstream data) commonly.

    PAL common data is defined in DataPal.

    Generator functions are provided to retrieve data specific to each PAL sensor board.

    hashtag

    Buttons

    Digital input management class (mwx::periph_buttons)

    Detects changes in digital inputs. This class detects changes when the same detected value is obtained multiple times. This is useful for reducing the effect of chattering on mechanical buttons.

    hashtag
    Methods

    hashtag

    // Pulse Counter setup
    PulseCounter.setup();
    void begin() {
    	// start the pulse counter capturing
    	PulseCounter.begin(
    		  100 // 100 count to wakeup
    		, PIN_INT_MODE::FALLING // falling edge
    		);
    
    	sleepNow();
    }
    void wakeup() {
    	Serial	<< mwx::crlf
    			<< "--- Pulse Counter:" << FOURCHARS << " wake up ---"
    			<< mwx::crlf;
    
    	if (!PulseCounter.available()) {
    		Serial << "..pulse counter does not reach the reference value." << mwx::crlf;
    		sleepNow();
    	}
    }
    uint16_t u16ct = PulseCounter.read();
    MONOSTICK BLUE or REDarrow-up-right
    Parent_MONOSTICK
    TWELITE DIParrow-up-right
    BLUE PAL or RED PALarrow-up-right
    environmental sensor pal AMBIENT SENSE PALarrow-up-right

    None

    Output(init HIGH)

    PIN_MODE::OUTPUT_INIT_LOW

    None

    Output(init LOW)

    PIN_MODE::WAKE_FALLING

    None

    Input, raised pin, falling

    PIN_MODE::WAKE_RISING

    None

    Input, rising pin, rising

    PIN_MODE::WAKE_FALLING_PULLUP

    Yes

    Input, raised pin, falling

    PIN_MODE::WAKE_RISING_PULLUP

    Yes

    Input, rising pin, rising

    PIN_MODE::DISABLE_OUTPUT

    Yes

    return to the input state

    const uint8_t PIN_DIGITAL::DIO0 .. 19

    DIO pins 0 to 19

    const uint8_t PIN_DIGITAL::DO0 .. 1

    DO pin 0,1

    PIN_MODE::INPUT

    None

    Input

    PIN_MODE::OUTPUT

    None

    Output

    PIN_MODE::INPUT_PULLUP

    Yes

    Input

    PIN_MODE::OUTPUT

    Contribute

    PIN_MODE::OUTPUT_INIT_HIGH

    Output (initial state HIGH)

    PIN_MODE::OUTPUT_INIT_LOW

    Output (initial state LOW)

    PIN_MODE::DISABLE_OUTPUT

    Stop setting output

    PIN_STATE::HIGH

    1

    HIGH(=Vcc) level

    PIN_STATE::LOW

    0

    LOW(=GND) level

    PIN_INT_MODE::FALLING

    falling edge

    PIN_INT_MODE::RISING

    rising edge

    PIN_MODE::OUTPUT_INIT_HIGH

    https://code.visualstudio.comcode.visualstudio.comchevron-right
    $ make USE_APPDEPS=0 TWELITE=BLUE clean
    ...
    $ make TWELITE=BLUE

    ``

    CXXFLAGS

    Specify compilation options for C++ source files.

    CFLAGS

    Specify compile options for C/C++ source files.

    INCFLAGS

    Specify the include file for the header file.

    OPTFLAGS

    Define this if you have a special reason for wanting to apply a compile option other than -Os.

    LDFLAGS

    Specify linker options. (This is not mentioned in the comments of the Makefile above, but can be specified)

    ADC4 pin (DIO1) *1

    AI4

    uint8_t PIN_ANALOGUE::VCC = 4

    Vcc Supply voltage

    It cannot be circumvented by software description alone.

    To avoid current leakage during sleep, the GND of the analog circuit section should be disconnected with a FET switch or the like and left floating during sleep. Also, before sleep, set the pin to input and pull-up state.

    uint8_t PIN_ANALOGUE::A1 = 0

    ADC1 pin

    AI1

    uint8_t PIN_ANALOGUE::A2 = 1

    ADC2 pin

    AI3

    uint8_t PIN_ANALOGUE::A3 = 2

    uint8_t PIN_ANALOGUE::D0 = 2

    ADC3 pin (DIO0) *1

    AI2

    bWaitInit

    If true is specified, waits for initialization of the regulator inside the semiconductor.

    kick_ev

    Specify the timer device to be specified for cyclic execution. The following five types of devices can be specified, and AD is started in the interrupt handler except for the first time.

    E_AHI_DEVICE_TICK_TIMER (TickTimer)

    E_AHI_DEVICE_TIMER0 .. 4 (Timer0 .. 4)

    fp_on_finish

    This callback function is called from within the interrupt handler after all ADCs on the specified ports have finished, and is used when ADC measurement values are to be stored separately in a FIFO queue, etc.

    pack_bits

    uint8_t PIN_ANALOGUE::A4 = 3

    uint8_t PIN_ANALOGUE::D1 = 3

    MONOSTICK BLUE or REDarrow-up-right
    TWELITE DIParrow-up-right
    TWELITE Rarrow-up-right
    Parent_MONOSTICK
    serial parser
    DataPal structure

    Although the packet data structure of PAL differs depending on the connected sensors, etc., DataPal holds the data structure of the common part.

    The PAL packet data structure consists of two blocks: the common part for all PALs and the individual data part. The individual data part does not interpret the packet, but stores it as is. To simplify handling, data exceeding 32 bytes is stored in dynamically allocated uptr_snsdata.

    The individual data parts are stored in a structure whose base class is PalBase. This structure is generated by the generator function defined in TwePacketPal.

    If the size fits in MWX_PARSER_PKT_APPPAL_FIXED_BUF when parse<TwePacketPAL>() is executed, a separate sensor object is generated.

    If it does not fit, a reference to the byte sequence used for analysis is stored in au8snsdata. In this case, if the data in the byte string used for analysis is rewritten, sensor-specific objects cannot be generated.

    hashtag
    PalBase

    All data structures for each sensor in PAL inherit from PalBase. The sensor data storage status u32StoredMask is included.

    hashtag
    PalEvent

    PAL events are information that is sent when certain conditions are met by processing sensor information, rather than directly from sensors. For example, when an acceleration sensor detects a certain level of acceleration from a stationary state.

    If event data exists, it can be determined by .is_PalEvent() of TwePacketPal being true, and .get_PalEvent() will yield a PalEvent data structure.

    hashtag
    Generator functions

    Generator functions are used to extract various types of data from sensor PAL.

    To use the generator function, first determine if pkt is an event (.is_PalEvent()). If it is an event, it has get_PalEvent(). Otherwise, it creates an object according to u8palpcb.

    hashtag
    get_PalMag()

    If .u8palpcb==E_PAL_PCB::MAG, the data PalMag of the open/close sensor pal is taken.

    hashtag
    get_PalAmb()

    If .u8palpcb==E_PAL_PCB::AMB, the data PalAmb of the environmental sensor pal is taken.

    hashtag
    get_PalMot()

    If .u8palpcb==E_PAL_PCB::MOT, the data PalMot of the operating sensor pal is taken.

    hashtag
    get_PalEvent()

    Extracts a PalEvent (PAL event) if .is_PalEvent() is true.

    setup()

    The parameter max_history is the maximum number of references that can be set with begin(). Memory is allocated and initialized here.

    hashtag
    begin()

    Starts the Buttons operation, the first parameter bmPortMask specifies the bitmap of digital inputs to be monitored. bit 0 corresponds to DIO 0, ... , bit N corresponds to DIO N. More than one can be specified: the second u8HistoryCount is the number of times required to confirm the value; the third tick_delta specifies the interval in ms between value checks; the fourth u8HistoryCount is the number of times to confirm the value.

    It will take u8HistoryCount*tick_delta[ms] to confirm the value. For example, if u8HistoryCount=5 and tick_delta=4, it will take at least 20ms to confirm the state.

    The confirmation is done in the TickTimer event handler. Since it is not an interrupt handler, it is affected by delays in processing, etc., but it is sufficient to suppress chattering of mechanical buttons, etc.

    hashtag
    end()

    Terminates the Buttons operation.

    hashtag
    available()

    Returns true when a change is detected. It is cleared when read() is executed.

    hashtag
    read()

    Called when u32port becomes available. u32portis the bitmap of the current input DIO andu32changed` is the bitmap of the DIO where the change was detected.

    Returns false if Buttons is not working.

    hashtag
    About operation

    hashtag
    Initial value determination

    When Buttons starts to operate, the input status of DIO is not yet determined. When the value is determined, it becomes available. At this time, the MSB (bit31) of the bitmap read by read() is set to 1.

    Since this function requires the operation to be determined, it cannot be used to monitor ports whose input values change constantly.

    hashtag
    Sleep

    If Buttons is in operation before Sleep, it will resume after it is restored. After resumption, initial determination is performed.

    ##############################################################################
    # Copyright (C) 2019 Mono Wireless Inc. All Rights Reserved.
    # Released under MW-SLA-*J,*E (MONO WIRELESS SOFTWARE LICENSE
    # AGREEMENT). 
    ##############################################################################
    # USER PROJECT BUILD DEFINITION.
    ##############################################################################
    
    #####################################################################
    ### set TWELITE model
    TWELITE ?= BLUE
    #TWELITE = RED
    
    #####################################################################
    ### set application version (MUST SET THIS.)
    VERSION_MAIN = 0
    VERSION_SUB  = 1
    VERSION_VAR  = 0
    
    #####################################################################
    ### set an additional source file
    ###   the default file name is dirname.
    
    ### for C++ files compiled with g++ (must have .cpp suffix)
    APPSRC_CXX += myAppBhvParent.cpp
    APPSRC_CXX += myAppBhvParent-handlers.cpp
    APPSRC_CXX += myAppBhvChild.cpp
    APPSRC_CXX += myAppBhvChild-handlers.cpp
    
    ### for C files compiled with gcc (must have .c suffix)
    #APPSRC += my_c_file.c
    
    ### Additional Src/Include Path
    # if set, find source files from given dirs.
    #
    APP_COMMON_SRC_DIR_ADD1 = ../Parent
    APP_COMMON_SRC_DIR_ADD2 = ../Child
    #APP_COMMON_SRC_DIR_ADD3 = 
    #APP_COMMON_SRC_DIR_ADD4 =
    
    #####################################################################
    ### set misc option for compiler
    
    ### C++ flags passed to g++
    # e.g. CXXFLAGS += -DMY_DEFS
    #CXXFLAGS +=
    
    ### C++/C flags passed to g++/gcc
    # e.g. CFLAGS += -DMY_DEFS
    #CFLAGS +=
    
    ### include opts
    # e.g. INCFLAGS += -I../my_common_src/
    #INCFLAGS +=
    
    ### optimize flag (default is -Os, normally no need to change)
    #OPTFLAG=-O2
    
    #####################################################################
    ### must include mwx.mk (the makefile body part.)
    MWSDK_PATH?=$(realpath $(MWSDK_ROOT))
    include $(MWSDK_PATH)/MkFiles/mwx.mk
    #####################################################################
    ### set application version (MUST SET THIS.)
    VERSION_MAIN = 0
    VERSION_SUB  = 1
    VERSION_VAR  = 0
    APPSRC_CXX += myAppBhvParent.cpp
    APPSRC_CXX += myAppBhvParent-handlers.cpp
    APPSRC_CXX += myAppBhvChild.cpp
    APPSRC_CXX += myAppBhvChild-handlers.cpp
    APP_COMMON_SRC_DIR_ADD1 = ../Parent
    APP_COMMON_SRC_DIR_ADD2 = ../Child
    pinMode(PIN_DIGITAL::DIO0, PIN_MODE::INPUT); 
    pinMode(PIN_DIGITAL::DIO1, PIN_MODE::INPUT);
    void setup(
            bool bWaitInit = false,
            uint8_t kick_ev = E_AHI_DEVICE_TICK_TIMER,
            void (*fp_on_finish)() = nullptr) 
    void begin(uint8_t bmPorts, uint8_t capt_tick = 1)
    void begin()
    void end()
    inline bool available()
    inline int16_t read(uint8_t s)
    inline int16_t read_raw(uint8_t s)
    void setup() {
    	auto&& set = the_twelite.settings.use<STG_STD>();
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    
    	/*** INTERACTIE MODE */
    	// settings: configure items
    	set << SETTINGS::appname("WirelessUART");
    	set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
    	set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
    	set << SETTINGS::lid_default(DEFAULT_LID); // set default lid
    	set.hide_items(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);
    	set.reload(); // load from EEPROM.
    	
    	/*** SETUP section */
    	// the twelite main class
    	the_twelite
    		<< set                      // from iteractive mode (APPID/CH/POWER)
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    
    	// Register Network
    	nwk	<< set;						// from interactive mode (LID/REPEAT)
    
    	/*** BEGIN section */
    	SerialParser.begin(PARSER::ASCII, 128); // Initialize the serial parser
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial << "--- WirelessUart (id=" << int(nwk.get_config().u8Lid) << ") ---" << mwx::crlf;
    }
    SerialParser.begin(PARSER::ASCII, 128); 
    while(Serial.available())  {
    	if (SerialParser.parse(Serial.read())) {
    		Serial << ".." << SerialParser;
    		const uint8_t* b = SerialParser.get_buf().begin();
    		uint8_t addr = *b; ++b; // the first byte is destination address.
    		transmit(addr, b, SerialParser.get_buf().end());
    	}
    }
    void on_rx_packet(packet_rx& rx, bool_t &handled) {
    	// check the packet header.
    	const uint8_t* p = rx.get_payload().begin();
    	if (rx.get_length() > 4 && !strncmp((const char*)p, (const char*)FOURCHARS, 4)) {
    		Serial << format("..rx from %08x/%d", rx.get_addr_src_long(), rx.get_addr_src_lid()) << mwx::crlf;
    
    		smplbuf_u8<128> buf;
    		mwx::pack_bytes(buf			
    				, uint8_t(rx.get_addr_src_lid())            // src addr (LID)
    				, make_pair(p+4, rx.get_payload().end()) );	// data body
    
    		serparser_attach pout;
    		pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
    		Serial << pout;
    	}
    }
    :FE00112233X
    
    :FE001122339C
    :03AABBCC00112233X
    
    :03AABBCC0011223366
    :FF00112233X
    
    :00112233X
    class TwePacketPal : public TwePacket, public DataPal { ... };
    struct DataPal {
    	uint8_t u8lqi; // LQI value
    
    	uint32_t u32addr_rpt; // address of the relay
    
    	uint32_t u32addr_src; // source address
    	uint8_t u8addr_src; // source logical address
    
    	uint16_t u16seq; // sequence number
    
    	E_PAL_PCB u8palpcb; // PAL board type
    	uint8_t u8palpcb_rev; // Revision of PAL board
    	uint8_t u8sensors; // Number of sensor data in the data (MSB=1 is an error)
    	uint8_t u8snsdatalen; // sensor data length (in bytes)
    
    	union {
    		const uint8_t *au8snsdata; // reference to sensor data part
    		uint8_t _pobj[MWX_PARSER_PKT_APPPAL_FIXED_BUF]; // each sensor object
    	};
    };
    	struct PalBase {
    		uint32_t u32StoredMask; // Data acquisition flags used internally
    	};
    	struct PalEvent {
    		uint8_t b_stored; // true if stored
    		uint8_t u8event_source; // spare
    		uint8_t u8event_id; // event ID
    		uint32_t u32event_param;// event parameter
    	};
    void print_pal(pktparser& pkt) {
    	auto&& pal = pkt.use<TwePacketPal>();
    	if (pal.is_PalEvent()) {
    		PalEvent obj = pal.get_PalEvent();
    	} else
    	switch(pal.u8palpcb) {
    	case E_PAL_PCB::MAG:
    	  {
    		  // generate pal board specific data structure.
    		  PalMag obj = pal.get_PalMag();
    	  } break;
      case E_PAL_PCB::AMB:
    	  {
    		  // generate pal board specific data structure.
    		  PalAmb obj = pal.get_PalAmb();
    	  } break;
    	  ...
    	default: ;
    	}
    }
    PalMag get_PalMag()
    
    // MAG
    struct PalMag : public PalBase {
        uint16_t u16Volt; // module voltage [mV]
        uint8_t u8MagStat; // state of magnetic switch [0:no magnet,1,2].
        uint8_t bRegularTransmit; // MSB flag of u8MagStat
    };
    PalAmb get_PalAmb()
    
    // AMB
    struct PalAmb : public PalBase {
        uint16_t u16Volt; // module voltage [mV]
        int16_t i16Temp; // temperature (100x value)
        uint16_t u16Humd; // humidity (100x value)
        uint32_t u32Lumi; // illuminance (equivalent to Lux)
    };
    PalMot get_PalMot()
    
    // MOT
    struct PalMot : public PalBase {
        uint16_t u16Volt; // module voltage [mV]
        uint8_t u8samples; // number of samples
        uint8_t u8sample_rate_code; // sample rate (0: 25Hz, 4:100Hz)
        int16_t i16X[16]; // X axis
        int16_t i16Y[16]; // Y axis
        int16_t i16Z[16]; // Z axis
    };
    PalEvent get_PalEvent()
    
    // PAL event
    struct PalEvent {
        uint8_t b_stored; // if true, event information is available
        uint8_t u8event_source; // event source
        uint8_t u8event_id; // event ID
        uint32_t u32event_param; // 24bit, event parameter
    };
    void setup(uint8_t max_history);
    void begin(uint32_t bmPortMask,
    				   uint8_t u8HistoryCount,
    				   uint16_t tick_delta);
    void end()
    inline bool available()
    bool read(uint32_t& u32port, uint32_t& u32changed)

    SM_SIMPLE state machine

    SM_SIMPLE is provided to wait for processing such as state transitions, waiting for timeouts, and completion of transmission in the sample code.

    The following is a basic code excerpt from SM_SIMPLE.

    hashtag
    Explanation

    To use SM_SIMPLE, you need to define an enum class as a list of states. In the above, it is defined as STATE. The class object is generated as SM_SIMPLE<STATE> step; with this stage as a parameter. The generated class object should be initialized by .setup().

    The initial state of SM_SIMPLE has the value 0, corresponding to STATE::INIT in the above example. To get the current state, use .state() and use it as a judgment expression in the switch clause of the do while statement as in the above example.

    Call .next() for state transitions. If the state changes, b_more_loop() is set to true and the loop in the do while clause is executed again. In the example, calling .next(STATE::TX) from the STATE::SENSOR state will cause the loop to be executed again and the case STATE::TX: clause will also be executed. If the state is not changed, the do while loop is escaped and the loop() is terminated once. Wait until the next call to loop().

    If you want to wait for processing such as completion of transmission, call .clear_flag(), and then signal the completion of processing by .set_flag(uint32_t) in another callback function or the like. Parameters of type uint32_t specified here can be read from .get_flag_value().

    If you want to process a timeout, you can record the time when you call .set_timeout(uint32_t) and check if the timeout time has elapsed with .is_timeout().

    SM_SIMPLE will be used again when returning from sleep, but it should always be called .on_sleep(bool) before sleep. If you put false in the parameter, it will start from the 0 state after recovery, and if you put true, it will resume from the state just before sleep.

    hashtag
    Source Code

    The following is the source code for SM_SIMPLE.

    • Contents may change depending on the version.

    • The main body will be stored in SM_SIMPLE.hpp in the mwx library resource folder.

    Sample ACTs

    Sample Acts

    To help you understand how the ACT works, we have prepared some samples.

    circle-info

    The samples can be found in Act_samples in the folder where you installed the MWSDK.

    hashtag
    Introduction to sample ACTs

    Several samples are provided to help you understand how the act works.

    hashtag
    Short ACT with no wireless communication and only microcontroller functions

    • is a very simple example, without any radio functions, to give you an idea of the basic structure of Act.

    hashtag
    Example of ACT description with I2C sensor

    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 (, , , intermittent operation with sleep, etc.).

    hashtag
    Basic ACT for wireless communication

    These are samples of sending or receiving wireless packets, each implemented from a slightly different perspective.

    • is a simple code that receives 1 byte commands from the UART, sends them and so on.

    • uses a state machine and intermittent operation with sleep, repeating the process of sleep recovery, radio transmission and sleep.

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

    hashtag
    ACT on Parent Node side

    circle-info

    Note: 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.

    • 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 to Act.

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

    hashtag
    ACT to add Interactive settings mode

    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)

    • executes I2C sensor device read/write commands and wirelessly transmits measurements obtained from I2C sensors. It also uses the Interactive settings mode to Act.

    • Setting provides a higher degree of customisation of the interactive mode .

    hashtag
    ACT to operate sensors and other devices

    This sample obtains sensor information from built-in peripherals and external sensor devices.

    • executes I2C sensor device read/write commands and wirelessly transmits measurements obtained from I2C sensors. It also uses the Interactive settings mode to Act.

    • provides bidirectional communication using digital input, analogue input, digital output and analogue output. It also contains the procedure for adding the interactive mode to Act.

    hashtag
    ACT to use TWELITE PAL

    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.

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

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

    • for continuous acquisition and wireless transmission of samples without interruption, using the accelerometer's FIFO and FIFO interrupts.

    hashtag
    ACT, which introduced the stand-alone function

    Acts with names starting with are intended to introduce features and APIs.

    hashtag
    Get the latest version.

    circle-info

    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.

    hashtag
    Common description

    The following items are common settings in the Act sample and are explained below.

    circle-info

    The following settings are common to all sample acts.

    • Application ID 0x1234abcd

    smplque

    Container class with FIFO queue structure.

    Container class with FIFO queue structure.

    smplque is a container class that provides FIFO queue operations for memory areas specified by element type T and memory allocation method alloc. Since the specification of alloc is complicated, an alias definition using using is used.

    circle-exclamation

    As a rule, element types are assumed to be structures that store numbers, numerical values, etc. It is not assumed to store objects that need to be destroyed by the destructor (since there is no process to delete the object when the element is deleted from the queue).

    A class Intr can be registered to set interrupt disabling at declaration time. If this class is not specified, normal operation without interrupt disable control is performed.

    Example of object declaration. Immediately after the declaration, a method call is made for initialization. The maximum size of any of these objects is 128 bytes immediately after initialization, and the initial size is 0 and nothing is stored. The maximum size cannot be changed.

    Since it is a FIFO queue, it is operated using methods such as push(),pop(),front().

    Access by iterator is also possible.

    hashtag
    Declaration and initialization

    Declares a container of type T and size N. After the declaration, call the initialization methods.

    The smplque_local allocates the area by a fixed array inside. Initialization by the constructor is also possible.

    In smplque_attach, specify the first pointer T* buf of the buffer to be used, the initial size size and the maximum size N of the array. Initialization by the constructor is also possible.

    smplque_heapallocates memory in the HEAP area (an area of memory that cannot be released but can be allocated at any time). Once allocated, this area cannot be released, so it is usually defined in the global area. Allocation is done byinit_heap(). Memory allocation by the constructor is not allowed. Please call init_heap()` to use this function.

    triangle-exclamation

    When creating a global object, initialization by the constructor is not possible (due to compiler limitation). Please call initialization functions init_local(),attach(),init_heap() at the beginning of execution (setup() is recommended).

    hashtag
    Methods

    hashtag
    push(), pop(), front(), back()

    push()` adds an entry to the queue.

    pop()` removes an entry from the queue.

    front()` refers to the first entry (the first one added).

    back()` refers to the last entry (the last one added).

    pop_front()` refers to the first entry as a return value and deletes it from the queue.

    hashtag
    empty(), size(), is_full()

    empty() returns true if the array contains no elements. is_full() returns true when the array is full.

    size() returns the number of elements stored in the queue.

    capacity() returns the maximum number of elements stored in the queue.

    hashtag
    clear()

    Erase all elements of the queue.

    hashtag
    operator []

    element. 0 is the first element added.

    hashtag
    Iterator

    You can get an iterator by begin() and end(). The beginning of the iterator is the first registered element of the queue. By using the iterator, range for statements and algorithms can be used.

    One application is .

    axis_xyzt

    This structure is used to store the values of 3-axis accelerometers, but procedures have been added to increase convenience when stored in a container class.

    struct axis_xyzt {
        int16_t x;
        int16_t y;
        int16_t z;
        uint16_t t;
    };

    hashtag
    get_axis_{x,y,z}_iter()

    Generates an iterator that accesses an element on the X, Y, or Z axis using the iterator of the container class containing axis_xyzt as a parameter.

    In the example below, buf.begin() and buf.end() are used as iterators for the X axis in the algorithm std::minmax_element.

    hashtag
    get_axis_{x,y,z}()

    This function generates a virtual container class from which one of the XYZ axes of the container class containing axis_xyzt is taken. Only the begin() and end() methods are implemented in this generated class. The iterator that can be obtained by these begin() and end() methods is the same as the iterator in the previous section .

    Rcv_Univsl

    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.

    TwePacketIO

    The TwePacketAppIO class interprets the standard app The TwePacketAppIO class is the one that interprets the

    Various information in the packet data is stored in DataTwelite after parse<TwePacketIO>() execution.

    hashtag

    wakeup()

    the function called when waking up from sleep.

    Called before loop() when waking up from sleep, and includes procedures for initialization after returning from sleep and for branching processing depending on the state of return.

    circle-info

    If there is no processing in loop() but only reading of sensors, etc., sleep can be performed again in this function.

    #include <SM_SIMPLE>
    
    enum class STATE : uint8_t {
    	INIT = 0,
    	SENSOR,
    	TX,
    	TX_WAIT_COMP,
    	GO_SLEEP
    };
    
    SM_SIMPLE<STATE> step;
    
    begin() {
      ...
      step.init(); //initialize
    }
    
    loop() {
      do {
        switch(step.state()) {
        case STATE::INIT:
          ...
          step.next(STATE::SENSOR);
        break;
        
        case STATE::SENSOR:
          ...
          step.next(STATE::TX);
        break;
        
        case STATE::TX:
          if (/*success on tx request*/) {
            step.set_timeout(100); // set timeout as 100ms
            step.clear_flag();
              
            step.next(STATE::TX_WAIT_COMP);
          }
        break;
        
        case STATE::TX_WAIT_COMP:
          if (step.is_timeout()) the_twelite.reset_system(); // is timeout?
          if (step.is_flag_ready()) sleepNow(); // is set the flag?
        break;
        
        ...
        }
      } while(step.b_more_loop());
    }
    
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	step.set_flag(ev.bStatus);
    }
    
    void sleepNow() {
    	step.on_sleep(false); // reset state machine.
      the_twelite.sleep(10000); // 10sec
    }
    template <typename T, int N, class Intr> smplbuf_local
    template <typename T, class Intr> smplbuf_attach
    template <typename T, class Intr> smplbuf_heap
    /*The return type is described as auto&& because it is a long template type name.*/
    auto&& get_axis_x_iter(Iter p)
    auto&& get_axis_y_iter(Iter p)
    auto&& get_axis_z_iter(Iter p)
    circle-info

    This callback function definition is optional.

    WirelessUART interprets the UART input into ASCII format using serparser and sends it.

    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.

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

    act0..4
    BRD_I2C_TEMPHUMID
    simple relay net <NWK_SIMPLE>
    Interactive settings mode <STG_STD>
    I2C sensor handling Wire
    Scratch
    Slp_Wk_and_Tx
    PingPong
    App_Wingsarrow-up-right
    Parent_MONOSTICK
    <STG_STD>
    Rcv_Univsl
    BRD_I2C_TEMPHUMID
    <STG_STD>
    <STG_STD>
    BRD_I2C_TEMPHUMID
    <STG_STD>
    BRD_APPTWELITE
    <STG_STD>
    PulseCounter
    PAL_AMB
    PAL_MOT-single
    PAL_MAG
    PAL_AMB_usenap
    PAL_AMB_behavior
    PAL_MOT_fifo
    Unit
    https://github.com/monowireless/Act_samplesarrow-up-right
    Access by iterator focusing on a specific member of the structure axis_xyzt
    #include <algorithm>
    
    void myfunc() {  
      // container class
      smplbuf_local<axis_xyzt, 10> buf;
      
      // Submit data for testing
      buf[0] = { 1, 2, 3, 4 };
      buf[1] = { 2, 3, 4, 5 };
      ...
      
      // Algorithm to obtain maximum and minimum values
      auto&& minmax = std::minmax_element(
        get_axis_x_iter(buf.begin()),
        get_axis_x_iter(buf.end()));
      
      Serial << "min=" << int(*minmax.first)
            << ",max=" << int(*minmax.second) << mwx::crlf;
    }
    get_axis_{x,y,z}_iter()

    idenify_packet_type()

    Determines the type of packet using the packet data byte sequence as input. The return value is E_PKT.

    E_PKT identify_packet_type(uint8_t* p, uint8_t u8len)

    If the packet cannot be interpreted as a specific packet, E_PKT::PKT_ERROR is returned.

    hashtag
    main.cpp

    setup(), loop(), and the callback function on_rx_packet() for incoming packets.

    hashtag
    setup()

    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.

    hashtag
    loop()

    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.

    hashtag
    on_rx_packet()

    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().

    hashtag
    pkt_common.hpp

    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.

    hashtag
    pkt_???.hpp, 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.

    hashtag
    pkt_pal

    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.

    hashtag
    dup_checker

    Implement a duplicate checker. The behavior of the checker can be customized by template arguments.

    hashtag
    template argument

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

    hashtag
    container

    • _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.

    hashtag
    duplicate check

    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.

    DataAppIO structure
    class TwePacketAppIO : public TwePacket, public DataAppIO { ... };
    serial message (0x81) of App_IOarrow-up-right
    struct DataAppIO {
    		// Serial # of sender
    		uint32_t u32addr_src;
    		
    		// Logical ID of the sender
    		uint8_t u8addr_src;
    
    		// Destination logical ID
    		uint8_t u8addr_dst;
    
    		// Timestamp at the time of transmission
    		uint16_t u16timestamp;
    
    		// Flag for low latency transmission
    		bool b_lowlatency_tx;
    
    		// Number of repeat relays
    		uint16_t u8rpt_cnt;
    
    		// LQI value
    		uint16_t u8lqi;
    
    		// DI status bitmap (DI1,2,3,4,...) in order from LSB
    		uint8_t DI_mask;
    
    		// Active (1 if used) bitmap of DI (DI1,2,3,4,...) in order from LSB
    		uint8_t DI_active_mask;
    		
    		// Bitmap of whether DI is interrupt-derived or not (DI1,2,3,4,...) in order from LSB
    		uint16_t DI_int_mask;
    };
    enum class STATE : uint8_t {
    	INIT = 0,
    	SENSOR,
    	TX,
    	TX_WAIT_COMP,
    	GO_SLEEP
    };
    
    SM_SIMPLE<STATE> step;
    
    void setup() {
      step.init();
    }
    loop() {
      do {
        switch(step.state()) {
        case STATE::INIT: // State with value 0
        ...
      do {
        switch(step.state()) {
        ...
        case STATE::SENSOR:
          ...
          step.next(STATE::TX); // (1)state transition
        break;
        
        case STATE::TX: // (3) Called in the second loop
          if (/*success on tx request*/) {
          ...
        }
      } while (b_more_loop()); // (2) loop continue check
        case STATE::TX:
          if (/*success on tx request*/) {
            step.set_timeout(100); // set timeout
            step.clear_flag();
              
            step.next(STATE::TX_WAIT_COMP);
          }
        break;
        
        case STATE::TX_WAIT_COMP:
          if (step.is_timeout()) ...; // timeout
          if (step.is_flag_ready()) ...; // is set the flag?
        break;
    ...
    
    // an event of tx completion
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	step.set_flag(ev.bStatus); // set the flag
    }
    void sleepNow() {
    	step.on_sleep(false); // reset state machine.
      the_twelite.sleep(10000); // 10sec
    }
    // very simple class to control state used in loop().
    template <typename STATE>
    class SM_SIMPLE {
    	uint32_t _u32_flag_value;  // optional data when flag is set.
    	uint32_t _ms_start;		// system time when start waiting.
    	uint32_t _ms_timeout;	// timeout duration
    
    	STATE _step;			  // current state
    	STATE _step_prev;		// previous state
    	bool_t _b_flag; 		// flag control.
    public:
    	// init
    	void setup() { memset(this, 0, sizeof(SM_SIMPLE)); }
    	// call befoer sleeping (save state machine status)
    	void on_sleep(bool b_save_state = false) {
    		STATE save = _step;
    		setup();
    		if(b_save_state) _step = _step_prev = save;
    	}
    
    	// state control
    	void next(STATE next) { _step = next; } // set next state
    	STATE state() { return _step; } // state number
    	bool b_more_loop() { // if state is changed during the loop, set true
    		if (_step != _step_prev) { _step_prev = _step; return true; }
    		else return false;
    	}
    
    	// timeout control
    	void set_timeout(uint32_t timeout) {
    		_ms_start = millis();
    		_ms_timeout = timeout;
    	}
    	bool is_timeout() { return (millis() - _ms_start) >= _ms_timeout; }
    
    	// flag control
    	void clear_flag() { _b_flag = false; _u32_flag_value = 0; }
    	void set_flag(uint32_t u32_flag_value = 0) {
    		_b_flag = true;
    		_u32_flag_value = u32_flag_value; }
    	uint32_t get_flag_value() { return _u32_flag_value; }
    	bool is_flag_ready() { return _b_flag; }
    };
    const uint32_t APP_ID = 0x1234abcd;
    const uint8_t CHANNEL = 13;
    const char APP_FOURCHAR[] = "BAT1";
    void some_func() {
    
    // alloc locally
    smplque_local<uint8_t, 128> q1;
    
    // attach to existing buffer
    uint8_t buf[128];
    smplque_attach<uint8_t> q2;
    
    // alloc from the HEAP
    smplque_heap<uint8_t> q3;
    
    void setup() {
      // Globally defined objects are initialized with setup()
      q1.init_local();
      q2.attach(buf, 128);
      q3.init_heap(128);
    }
    
    void some_func() {
      // Local definition of smplque_local can omit init_local()
      smplque_local<uint8_t, 128> q_local;
      ..
    }
    
    }
    void begin() { // begin() runs only once on startup
    	smplque_local<int, 32> q1;
    	
    	q1.push(1);
    	q1.push(4);
    	q1.push(9);
    	q1.push(16);
    	q1.push(25);
    	
    	while(!q1.empty()) {
    		Serial << int(q1.front()) << ',';
    		q1.pop();
    	}
    	// output -> 1,4,9,16,25,
    }
    void begin() { // begin() runs only once on startup
    	smplque_local<int, 32> q1;
    	q1.init_local();
    	
    	q1.push(1);
    	q1.push(4);
    	q1.push(9);
    	q1.push(16);
    	q1.push(25);
    	
    	// Use iterators
    	for(int x : q1) {
    		Serial << int(x) << ',';
    	}
    	
    	// Application of STL Algorithm
    	auto&& minmax = std::minmax_element(q1.begin(), q1.end());
    	Serial <<  "min=" << int(*minmax.first)
    		     << ",max=" << int(*minmax.second);
    	// output -> 1,4,9,16,25,min=1,max=25[]
    }
    smplbuf_local<T,N>
    smplbuf_local<T,N>::init_local()
    
    smplbuf_attach<T>
    smplbuf_attach<T>::attach(T* buf, uint16_t N)
    
    smplbuf_heap<T>
    smplbuf_heap<T>::init_heap(uint16_t N); 
    
    // Example
    // Fixed array inside
    smplque_local<uint8_t, 128> q1;
    q1.init_local();
    
    // Use an already existing array
    uint8_t buf[128];
    smplque_attach<uint8_t> q2;
    q2.attach(buf, 128);
    
    // Allocate to heap
    smplque_heap<uint8_t> q3;
    q3.init_heap(128); 
    inline void push(T&& c)
    inline void push(T& c)
    inline void pop()
    inline T& front()
    inline T& back()
    
    inline T& pop_front()
    inline bool empty()
    inline bool is_full()
    inline uint16_t size()
    inline uint16_t capacity()
    inline void clear()
    inline T& operator[] (int i)
    inline smplque::iterator begin()
    inline smplque::iterator end()
    /*The return type is described as auto&& because it is a long template type name.*/
    auto&& get_axis_x(T& c)
    auto&& get_axis_y(T& c)
    auto&& get_axis_z(T& c)
    #include <algorithm>
    
    void myfunc() {
      // container class
      smplbuf_local<axis_xyzt, 10> buf;
      
      // Submit data for testing
      buf[0] = { 1, 2, 3, 4 };
      buf[1] = { 2, 3, 4, 5 };
      ...
      
      // Extract the X axis in the queue
      auto&& vx = get_axis_x(que);
      
      // Use of ranged for statement
      for (auto&& e : vx) { Serial << int(e) << ','; }
      
      // Algorithm to obtain maximum and minimum values
      auto&& minmax = std::minmax_element(
          vx.begin(), vx.end());
                              
      Serial << "min=" << int(*minmax.first)
            << ",max=" << int(*minmax.second) << mwx::crlf;
    }
    	mwx::pnew(g_pkt_pal);
    	mwx::pnew(g_pkt_apptwelite);
    	mwx::pnew(g_pkt_actsamples);
    	mwx::pnew(g_pkt_unknown);
    	auto&& nwk_ly = the_twelite.network.use<NWK_LAYERED>();
    	auto&& nwk_sm = the_twelite.network2.use<NWK_SIMPLE>();
    	if (TickTimer.available()) {
    		static unsigned t;
    		if (!(++t & 0x3FF)) {
    			g_pkt_pal.refresh();
    			g_pkt_apptwelite.refresh();
    			g_pkt_actsamples.refresh();
    			g_pkt_unknown.refresh();
    		}
    	}
    void on_rx_packet(packet_rx& rx, bool_t &handled) {
    	auto type = rx.get_network_type();
    	bool b_handled = false;
    
    	// PAL
    	if (!b_handled
    		&& type == mwx::NETWORK::LAYERED
    		&& g_pkt_pal.analyze(rx, b_handled)
    	) {
    		g_pkt_pal.display(rx);
    	}
    
    	// Act samples
    	if (!b_handled
    		&& type == mwx::NETWORK::SIMPLE
    		&& g_pkt_actsamples.analyze(rx, b_handled)
    	) {
    		g_pkt_actsamples.display(rx);
    	}
    
    	// Standard application (e.g. App_Twelite)
    	if (!b_handled
    		&& type == mwx::NETWORK::NONE
    		&& g_pkt_apptwelite.analyze(rx, b_handled)
    	) {
    		g_pkt_apptwelite.display(rx);
    	}
    
    	// unknown
    	if (!b_handled) {
    		g_pkt_unknown.analyze(rx, b_handled);
    		g_pkt_unknown.display(rx);
    	}
    }
    template <class D>
    struct pkt_handler {
    	D& self() { return static_cast<D&>(*this); }
    	bool analyze(packet_rx& rx, bool &b_handled) {
    		return self().pkt.analyze(rx, b_handled);
    	}
    	void display(packet_rx& rx) {
    		Serial
    			<< crlf
    			<< format("!PKT_%s(%03d-%08x/S=%d/L=%03d/V=%04d)"
    					, self().get_label_packet_type()
    					, self().pkt.data.u8addr_src
    					, self().pkt.data.u32addr_src
    					, rx.get_psRxDataApp()->u8Seq
    					, rx.get_lqi()
    					, self().pkt.data.u16volt
    					);
    
    		self().disp_detail(rx);
    	}
    	void refresh() {
    		self()._refresh();
    	}
    };
    
    // packet analyzer for App_Twelite
    class pkt_handler_apptwelite : public pkt_handler<pkt_handler_apptwelite> {
    	friend class pkt_handler<pkt_handler_apptwelite>;
    	pkt_apptwelite pkt;
    	void disp_detail(packet_rx& rx);
    	const char* get_label_packet_type() { return "AppTwelite"; }
    	void _refresh() { pkt.refresh(); }
    public:
    	pkt_handler_apptwelite() : pkt() {}
    };
    	bool check_dup(uint32_t u32ser, uint16_t u16val, uint32_t u32_timestamp) {
    		// find entry by key:u32ser.
    		auto r = _mmap_entries.equal_range(u32ser);
    
            ...
        }

    smplbuf

    This is a container class with an array structure inside. The maximum size of the buffer is determined at initialization, and it behaves as a variable-length array up to that maximum size.

    smplbuf is a container class that provides array operations on memory areas specified by element type T and memory allocation method allocarrow-up-right. Since the specification of alloc is complicated, alias definitions using using are used.

    Here is an example of object declaration. Immediately after the declaration, a method call is made for initialization. The maximum size of each object is 128 bytes immediately after initialization, and the size is 0. Use while extending the size as needed.

    Alias classes are defined for uint8_t type.

    Elements can be accessed like normal arrays, using the [] operator, etc., and iterators can also be used to access elements.

    The class also has push_back() method, which enables some type of algorithm from standard C++ library.

    hashtag
    Declaration, Initialize

    Declares a container of type T and size N. After the declaration, call the initialization method.

    smplbuf_local allocates an area by a fixed array inside. It can also be initialized by the constructor.

    In smplbuf_attach, specify the first pointer T* buf of the buffer to be used, the initial size size and maximum size N of the array. Initialization by the constructor is also possible.

    The smplbuf_heap allocates memory in the HEAP area (a memory area that cannot be released but can be allocated at any time). Once allocated, this area cannot be released, so it is usually defined as a global area. Allocation is done by init_heap(). Memory allocation by the constructor is not allowed. Please call init_heap() to use this function.

    triangle-exclamation

    When creating a global object, initialization by the constructor is not possible. Please call initialization functions init_local(),attach(),init_heap() at the beginning of execution (setup() is recommended).

    hashtag
    List of initializers

    Initializer list { ... } can be used to initialize members. Except for use in the constructor in a local declaration of smplbuf_local, it is valid after calling the initialization method.

    • right side value of assignment operator (smplbuf_local, smplbuf_attach, smplbuf_heap)

    • constructor (local declaration of smplbuf_local, global declaration is not allowed)

    hashtag
    Methods

    hashtag
    append(), push_back(), pop_back()

    Add a member c at the end. The return value of append() is bool, which returns false if the buffer is full and cannot be added.

    The pop_back() deletes the trailing entry. However, it does not clear the entry.

    hashtag
    empty(), size(), capacity()

    empty() returns true when no elements are stored in the array. is_end() returns true when the array is full.

    size() returns the number of array elements.

    capacity() returns the maximum number of elements in the array.

    hashtag
    reserve(), reserve_head(), redim()

    reserve() extends the size of the array. The area where the array is not stored is initialized by default.

    reserve_hear() allocates an area of the specified size at the top of the array. This area cannot be referenced by the container object. For example, it can be used as a container to access a sub-array of a packet payload whose header part is skipped. To return the container to the allocated area so that all of it can be accessed again, give it the same negative value as when it was allocated.

    redim() resizes the allocated area. Unlike reserve(), it does not initialize the unused area.

    hashtag
    operator []

    element.

    A negative value for i makes the element from the end of the buffer. For -1, the element is the last element, and for -2, it is one before the end.

    hashtag
    Output to mwx::stream

    Array objects of type uint8_t (smplbuf<uint8_t, *>) can be output as is to derived objects of mwx::stream.

    hashtag
    << operator

    Outputs a sequence of bytes for mwx::stream derived objects such as Serial.

    hashtag
    to_stream()

    Used for output to stream purposes. Used to implement the << operator.

    hashtag
    Data generation with `mwx::stream

    mwx::streamdefines functions and operators to output bytes to a stream, such as<<operator andprintfmt()method. You can use the stream output procedure with a smplbuf array of typeuint8_t` as the output destination.

    There are two methods.

    • Using helper object generated by .

    • Use the smplbuf class that .

    Scratch

    Template code.

    circle-check

    This act includes

    • Sending radio packets ('t' key)

    • Sleep ('s' key)

    • Input from serial port -

    • Digital (button) input -

    hashtag
    setup()

    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.

    circle-info

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

    hashtag
    begin()

    Called only once after setup() on startup. Only displays a message.

    hashtag
    loop()

    hashtag
    Input detection of buttons (switches).

    The state is determined by continuous reference to . When the button state changes, it is output serially.

    hashtag
    Input from serial.

    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.

    hashtag
    Enter t for wireless transmission

    When 't' is input, sending is done. In this sample, the tx_busy flag is used to prevent continuous input.

    circle-info

    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.

    hashtag
    Type s to sleep.

    The system will sleep for 5000ms=5 seconds. After recovery, wakeup() is executed.

    hashtag
    wakeup()

    First to be called on sleep wake up. Display of message only.

    hashtag
    Transmit()

    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.

    hashtag
    on_tx_comp()

    Called on completion of a transmission; ev contains the transmission ID and completion status.

    hashtag
    on_rx_packet()

    When a packet is received, the sender's address information is displayed.

    Creating a new project

    Creating a new project

    To create a new project, copy the folder of an already existing sample act with a different name and edit the file name.

    circle-exclamation

    The destination folder does not have to be a folder under the MWSDK. However, the folder name must not contain any whitespace characters or Japanese names.

    The file structure of the project is as follows (we'll use PingPong as an example)

    Copy this PingPong folder to another location (but without Japanese characters or spaces in the folder name).

    The only thing you need to edit is the file name PingPong.cpp. Change it to AlphaBravo.cpp, the same as the folder name.

    circle-check

    Run build\build-BLUE.cmd and if a BIN file is generated, it' is done (Windows 10).

    On Linux/macOS, run make TWELITE=BLUE to see if the build succeeds.

    hashtag
    Edit Build Definition

    To add files to be built, edit build/Makefile. The .c .cpp files directly under the project will be added automatically, but other files will need to be edited.

    See for how to edit the file.。

    hashtag
    Configuration for VSCode

    If you use VSCode, edit the definition under .vscode as necessary.

    Most of the examples included in the TWELITE STAGE SDK are as follows

    • The source code for the TWELITE STAGE SDK library cites ${env:MWSDK_TWENET_LIBSRC}/include/** ${env:MWSDK_TWENET_LIBSRC}/src/**. This environment variable MWSDK_TWENET_LIBSRC is automatically set when the project is opened in VSCode from the TWELITE STAGE app.

    • For the build task, no additional options such as -D are set by default.

    PAL_MAG

    is used to acquire sensor values.

    circle-check

    This ACT includes

    template <typename T, int N> smplbuf_local
    template <typename T> smplbuf_attach
    template <typename T> smplbuf_heap
    // Array area is a fixed array of class member variables
    smplbuf_local<uint8_t, 128> b1;
    
    // Refer to buffer memory that already exist
    uint8_t buf[128];
    smplbuf_attach<uint8_t> b2;
    
    // Allocate buffer memory at heap
    smplbuf_heap<uint8_t> b3;
    
    // Initialize (must do initlialize at setup() for global objects)
    void setup() {
        b1.init_local();
        b2.attach(buf, 0, 128);
        b3.init_heap(128);
    } 
    
    // In some function
    void some_func() {
        smplbuf_local<uint8_t, 128> bl;
        // bl.init_local(); // It can omit if smplbuf_local is declared locally.
        
        bl.push_back('a');
    }
    Act_samples
      +-PingPong
        +-PingPong.cpp   : ACT file
        +-build          : build dir
        +-.vscode        : setting fils for VSCode    
    .get_stream_helper()
    inherits mwx::stream
    Makefile Description
    The timing of the transmissions is randomised, so the completion of transmissions is not in the order in which they are requested.
    Serial
    Buttons
    pack_bits
    Buttons
    template <int N>
    smplbuf_u8
    // smplbuf<uint8_t, alloc_local<uint8_t, N>>
    
    smplbuf_u8_attach
    // smplbuf<uint8_t, alloc_attach<uint8_t>>
    
    smplbuf_u8_heap
    // smplbuf<uint8_t, alloc_heap<uint8_t>>
    void begin() { // begin() runs only once at startup
      smplbuf_u8<32> b1; // initially b1 behaves empty buffer (32bytes is allocated).
      b1.reserve(5); // reserves 5bytes in b1.
      
      b1[0] = 1;
      b1[1] = 4;
      b1[2] = 9;
      b1[3] = 16;
      b1[4] = 25;
      
      for(uint8_t x : b1) { // can use ranged for.
        Serial << int(x) << ",";
      }
    }
    smplbuf_local<T,N>()
    smplbuf_local<T,N>::init_local()
    
    smplbuf_attach<T>(T* buf, uint16_t size, uint16_t N)
    smplbuf_attach<T>::attach(T* buf, uint16_t size, uint16_t N)
    
    smplbuf_heap<T>()
    smplbuf_heap<T>::init_heap(uint16_t N)
    
    // Example
    // Allocated internally as fixed length array.
    smplbuf_local<uint8_t, 128> b1;
    b1.init_local();
    
    // attach to an existing array.
    uint8_t buf[128];
    smplbuf_attach<uint8_t> b2;
    b2.attach(buf, 0, 128);
    
    // allocate array at heap area.
    smplbuf_heap<uint8_t> b3;
    b3.init_heap(128); 
    void in_some_func() {
        smplbuf_local<uint8_t, 5> b1;
        b1.init_local();
        
        b1 = { 0, 1, 2, 3, 4 };
        
        smplbuf_local<uint8_t, 5> b2{0, 1, 2, 3, 4};
    }
    inline bool append(T&& c)
    inline bool append(const T& c)
    inline void push_back(T&& c)
    inline void push_back(const T& c)
    inline void pop_back()
    inline bool empty()
    inline bool is_end()
    inline uint16_t size()
    inline uint16_t capacity()
    inline bool reserve(uint16_t len)
    inline void reserve_head(uint16_t len)
    inline void redim(uint16_t len)
    inline T& operator [] (int i) 
    inline T operator [] (int i) const
    template <class L_STRM, class AL>
    	mwx::stream<L_STRM>& operator << (
    			mwx::stream<L_STRM>& lhs, mwx::_smplbuf<uint8_t, AL>& rhs) 
    			
    //例
    smplbuf_u8<128> buf;
    buf.push_back('a');
    buf.push_back('b');
    buf.push_back('c');
    
    Serial << buf;
    // 出力: abc
    inline std::pair<T*, T*> to_stream()
    
    //Example
    smplbuf_u8<128> buf;
    buf.push_back('a');
    buf.push_back('b');
    buf.push_back('c');
    
    Serial << buf.to_stream();
    // Output: 0123
    SomeDir
      +-AlphaBravo
        +-PingPong.cpp -> AplhaBravo.cpp (Note: Changed file name)
        +-build          : build dir
        +-.vscode        : setting files for VSCode
    void setup() {
    	/*** SETUP section */
    	tx_busy = false;
    
    	// the twelite main class
    	the_twelite
    		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
    		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    
    	// Register Network
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    	nwk	<< NWK_SIMPLE::logical_id(0xFE); // set Logical ID. (0xFE means a child device with no ID)
    
    	/*** BEGIN section */
    	Buttons.begin(pack_bits(PIN_BTN), 5, 10); // check every 10ms, a change is reported by 5 consequent values.
    
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial << "--- Scratch act ---" << mwx::crlf;
    }
    the_twelite.begin(); // start twelite!
    void begin() {
    	Serial << "..begin (run once at boot)" << mwx::crlf;
    }
    if (Buttons.available()) {
    	uint32_t bm, cm;
    	Buttons.read(bm, cm);
    
    	if (cm & 0x80000000) {
    		// the first capture.
    	}
    
    	Serial << int(millis()) << ":BTN" << format("%b") << mwx::crlf;
    }
    while(Serial.available()) {
      int c = Serial.read();
    
    	Serial << '[' << char(c) << ']';
    
      switch(c) {
      case 'p': ... // display millis()
      case 't': ... // transmit radio packet (vTransmit)
            if (!tx_busy) {
    					tx_busy = Transmit();
    					if (tx_busy) {
    						Serial  << int(millis()) << ":tx request success! (" 
    										<< int(tx_busy.get_value()) << ')' << mwx::crlf;
     					} else {
    						Serial << int(millis()) << ":tx request failed" << mwx::crlf;;
    					}
    				}
      case 's': ... // sleeping
    				Serial << int(millis()) << ":sleeping for " << 5000 << "ms" << mwx::crlf << mwx::flush;
    				the_twelite.sleep(5000);
    				break;
      }
    }
    the_twelite.sleep(5000);
    void wakeup() {
    	Serial << int(millis()) << ":wake up!" << mwx::crlf;
    }
    MWX_APIRET Transmit() {
    	Serial << int(millis()) << ":Transmit()" << mwx::crlf;
    
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    		// set tx packet behavior
    		pkt << tx_addr(0xFF)  // Broadcast communications
    			<< tx_retry(0x1)    // 1 resend
    			<< tx_packet_delay(100,200,20); // Transmission delay between 100-200 ms, retransmission interval 20 ms
    
    		// Specification of the data to be sent (to be determined for each application)
    		pack_bytes(pkt.get_payload()
    			, make_pair("SCRT", 4) // 4-character identifier
    			, uint32_t(millis())   // Timestamp
    		);
    		
    		// Make a request to send
    		return pkt.transmit(); 
    	} else {
    		// Failed at .prepare_tx_packet() (send queue is full)
    		Serial << "TX QUEUE is FULL" << mwx::crlf;
    	  return MWX_APIRET(false, 0);
    	}
    }
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	Serial 	<< int(millis()) << ":tx completed!"
    			<< format("(id=%d, stat=%d)", ev.u8CbId, ev.bStatus) << mwx::crlf;
    	tx_busy = false; // clear tx busy flag.
    }
    void on_rx_packet(packet_rx& rx, bool_t &handled) {
    	Serial << format("rx from %08x/%d", 
    					rx.get_addr_src_long(), rx.get_addr_src_lid()) << mwx::crlf;
    }
    --- Scratch act ---
    ..begin (run once at boot)
    [t]11591:Transmit()
    11592:tx request success! (1)
    [t]11593:Transmit()
    11593:tx request success! (2)
    [t]11594:Transmit()
    11595:tx request success! (3)
    [t]11595:Transmit()
    TX QUEUE is FULL
    11596:tx request failed
    11654:tx completed!(id=2, stat=1)
    11719:tx completed!(id=3, stat=1)
    11745:tx completed!(id=1, stat=1)
    Sending and receiving wireless packets
  • Configuring settings via Interactive settings mode - <STG_STD>

  • State transition control by state machine - <SM_SIMPLE>

  • <PAL_MAG> or <CUE> board manipulation via board behavior

  • hashtag
    ACT functions

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

    hashtag
    How to use ACT

    hashtag
    Required TWELITE

    Role
    Example

    Parent Node

    Act in action.

    Child Node

    +

    hashtag
    Explanation of ACT

    hashtag
    Include

    Board BEHAVIOR for open/close sensor pal <PAL_MAG> is included.

    hashtag
    setup()

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

    hashtag
    begin()

    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.

    hashtag
    sleepNow()

    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.

    hashtag

    hashtag
    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 (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.

    hashtag
    loop()

    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.

    OPEN-CLOSE SENSE PALarrow-up-right

    serparser

    serial format input-output (mwx::serial_parser)

    Used for serial format input/output. It has an internal buffer that holds the interpreted binary series. On input, the series is stored in the internal buffer one byte at a time according to the read format, and becomes complete when the interpretation of the series is complete. Conversely, on output, the buffer is output from the internal buffer according to the specified output format.

    Three class names are defined according to the memory buffer handling method (alloc).

    hashtag
    constants (format type)

    The type of format to pass in the initialization parameter of begin(). There are two types here: ASCII format and binary format.

    constant
    type
    circle-exclamation

    Binary format is generally more complicated to handle than ASCII format, including the necessary tools and confirmation methods. Normally, ASCII format should be used.

    hashtag
    About format

    hashtag
    ASCII format

    The ASCII format is a way to represent a sequence of data in binary as a string.

    For example, 00A01301FF123456 in ASCII format is expressed as follows. The beginning is :, B1 is the checksum, and the end is [CR:0x0d][LF:0x0a].

    :00A01301FF123456B1[CR][LF]

    You can omit the terminating checksum. Replace the CRLF series from the checksum with X. This is useful when you want to send data for experiments, etc., although it is less vulnerable to wrong data series due to garbled characters.

    :00A01301FF123456X

    hashtag
    Definition.

    ======
    Bytes of original data
    Bytes
    Description

    hashtag
    Binary Format

    circle-exclamation

    Normally, ASCII format should be used.

    However, to check the transmission and reception in experiments, it is necessary to prepare a special terminal that supports binary communication, and checksum calculation is also required. It is more difficult to use than the ASCII format.

    Binary format is a method of sending a sequence of data consisting of binary data with a header and a checksum.

    For example, 00A01301FF123456 is expressed in binary format as follows.

    0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D

    hashtag
    Definition.

    ======
    Bytes of original data
    Bytes in format
    Description

    hashtag
    Methods

    hashtag
    declaration, begin()

    The declaration specifies the memory allocation class. Since this specification is complicated, an alias definition is used as described above.

    Calls the begin() method according to the memory allocation class.

    hashtag
    serparser_attach

    The buffer specified by p is used in the [format] (ser_parser.md#nitsuite) specified by ty. The maximum length of the buffer is specified by max_siz and the effective data length of the buffer by siz.

    This definition is used especially when you want to format output data columns (see >> operator).

    hashtag
    serparser_local<N> - Allocate internal buffer

    Initialize with the specified by ty.

    hashtag
    serparser_heap - Allocated to heap

    Initialize the heap by allocating the size specified by siz to the heap in the specified by ty.

    circle-exclamation

    Once allocated, heap space cannot be released.

    hashtag
    get_buf()

    Returns an internal buffer. The buffer will be of type smplbuf<uint8_t, alloc>.

    hashtag
    parse()

    Processes input characters. Receives a single byte of input string of formatted input and interprets it according to the format. For example, in ASCII format, it receives a series like :00112233X as input. X, one byte at a time, and when the last X` is entered, the interpretation of the format is completed and reported as done.

    The parameter to parse() is the input byte, and the return value is true if the interpretation is complete.

    circle-info

    When parse() reports that reading is complete, the next parse() will return to the reading-in-progress status.

    hashtag
    例

    hashtag
    operator bool()

    If true, reading is completed by parse(); if false, interpretation is in progress.

    hashtag
    Example (parse() example can be rewritten as follows)

    hashtag
    << operator

    Outputs the internal buffer to the stream (Serial) in a formatted format.

    hashtag
    Example

    #include <TWELITE>
    #include <NWK_SIMPLE>
    #include <PAL_MAG>
    void setup() {
    	/*** SETUP section */
    	// use PAL_AMB board support.
    	auto&& brd = the_twelite.board.use<PAL_MAG>();
    	// now it can read DIP sw status.
    	u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
    	if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
    
    	// LED setup (use periph_led_timer, which will re-start on wakeup() automatically)
    	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 */
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial << "--- PAL_MAG:" << FOURCHARS << " ---" << mwx::crlf;
    }
    auto&& brd = the_twelite.board.use<PAL_MAG>();
    
    u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
    if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
    	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
    void begin() {
    	sleepNow(); // the first time is just sleeping.
    }
    void sleepNow() {
    	uint32_t u32ct = 60000;
    	
    	pinMode(PAL_MAG::PIN_SNS_OUT1, PIN_MODE::WAKE_FALLING);
    	pinMode(PAL_MAG::PIN_SNS_OUT2, PIN_MODE::WAKE_FALLING);
    
    	the_twelite.sleep(u32ct);
    }
    void wakeup() {
    	if (the_twelite.is_wokeup_by_wktimer()) {
    		sleepNow();
    	}
    }
    void loop() {
    	if (!b_transmit) {
    		if (auto&& pkt = 
          the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet())
    
    			uint8_t b_north = 
    			  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_NORTH);
    			uint8_t b_south = 
    			  the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_SOUTH);
    	
    			Serial << "..sensor north=" << int(b_north) 
    			       << " south=" << int(b_south) << mwx::crlf;
    	
    			// set tx packet behavior
    			pkt << tx_addr(0x00)
    				<< tx_retry(0x1)
    				<< tx_packet_delay(0, 0, 2);
    	
    			// prepare packet payload
    			pack_bytes(pkt.get_payload()
    				, make_pair(FOURCHARS, 4) 
    				, b_north
    				, b_south
    			);
    	
    			// do transmit
    			MWX_APIRET ret = pkt.transmit();
    	
    			if (ret) {
    				u8txid = ret.get_value() & 0xFF;
    				b_transmit = true;
    			}
    			else {
    				// fail to request
    				sleepNow();
    			}
    		} else {
    		  sleepNow();
    		}
    	} else { 
    		if (the_twelite.tx_status.is_complete(u8txid)) {		
    			b_transmit = 0;
    			sleepNow();
    		}
    	}
    }
    	if (!b_transmit) {
    uint8_t b_north = 
      the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_NORTH);
    uint8_t b_south = 
      the_twelite.is_wokeup_by_dio(PAL_MAG::PIN_SNS_SOUTH);
    // do transmit
    MWX_APIRET ret = pkt.transmit();
    if (the_twelite.tx_status.is_complete(u8txid)) {		
    	b_transmit = 0;
    	sleepNow();
    }
    // serparser_attach : Use existing buffers
    serparser_attach
    
    // serparser : Allocate N bytes of buffer internally
    serparser_local<N>
    
    // serparser_heap : Allocate buffers in the heap area
    serparser_heap
    MONOSTICK BLUE or REDarrow-up-right
    Parent_MONOSTICK
    BLUE PAL or RED PALarrow-up-right
    OPEN-CLOSE SENSE PALarrow-up-right

    2

    The sum of each byte of the data part is calculated in 8-bit width and taken as 2's complement. In other words, the sum of each byte of the data part plus the checksum byte is calculated to be 0 in 8-bit width. The checksum byte is represented by two ASCII characters. For example, in 00A01301FF123456, 0x00 + 0xA0 + ... + 0x56 = 0x4F, the two's complement of which is 0xB1. (i.e. 0x4F + 0xB1 = 0x00)

    footer

    2

    [CR] (0x0D) [LF] (0x0A)

    N

    N

    Specifies the original data.

    Checksum

    1

    Calculates the XOR of each byte of the data part. For example, if the data part is 00A01301FF123456, 0x00 xor 0xA0 xor ... 0x56 = 0x3D.

    footer

    (1)

    The checksum is effectively the end of the line. The output from the radio module will be appended with 0x04 (EOT).

    uint8_t PARSER::ASCII = 1

    ASCII format

    uint8_t PARSER::BINARY = 2

    binary format

    header

    1

    Data part

    N

    2N

    Each byte of the original data is represented by two ASCII characters (A-F in upper case). For example, 0x1F is represented as 1 (0x31) F (0x46).

    checksum

    header

    2

    Data Length

    2

    The data length is two bytes in big-endian format, with MSB (0x8000) set and the length of the data part specified. For example, if the length of the data part is 8 bytes, specify 0x80 0x08.

    Class name (alias definition) Memory Allocation

    Contents

    serparser_attach

    specify an already existing buffer with begin()

    serparser_local<N>

    allocate a buffer of N bytes internally

    serparser_heap

    allocate a heap of the size specified by the parameters of the begin() method

    format
    format

    Data part

    digitalRead()

    Reads the value of the port of the input configuration.

    Reads the value of the port of the input configuration.

    static inline E_PIN_STATE digitalRead(uint8_t u8pin)

    Get the input value of a pin that has been previously set as an input, either LOW or HIGH.

    circle-info

    No conversion operator from type E_PIN_STATE to type int is defined, so direct assignment to a numeric type is not possible.

    // serparser_attach : use existing buffer
    serparser_attach p1;
    
    uint8_t buff[128];
    p1.begin(ARSER::ASCII, buff, 0, 128);
    
    
    // serparser : allocate N bytes buffer inside
    serparser p2<128>;
    p2.begin(PARSER::ASCII);
    
    
    // serparser_heap : allocate buffer in heap area
    serparser_heap p3;
    p3.begin(PARSER::ASCII, 128);
    void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)
    void begin(uint8_t ty)
    void begin(uint8_t ty, uint16_t siz)
    BUFTYPE& get_buf()
    inline bool parse(uint8_t b)
    while (Serial.available()) {
        int c = Serial.read();
        
        if (SerialParser.parse(c)) {
            // Format interpretation complete, data sequence obtained in b (smplbuf<uint8_t>)
            auto&& b = SerialParser.get_buf();
            
            // The following is the processing of the resulting data sequence
            if (b[0] == 0xcc) {
              // ...
            }
        }
    }
    operator bool() 
    while (Serial.available()) {
        int c = Serial.read();
        
        SerialParser.parse(c);
        
        if(SerialParser) {
            // Format interpretation complete, data sequence obtained in b (smplbuf<uint8_t>)
            auto&& b = SerialParser.get_buf();
            // ...
        }
    }
    uint8_t u8buf[] = { 0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc };
    
    ser_parser pout;
    pout.begin(ARSER::ASCII, u8buf, 6, 6); // Specify 6 bytes of u8buf
    
    Serial << pout;// Format output to Serial -> :112233AABBCC??[CR][LF] 

    Parent_MONOSTICK

    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.

    circle-check

    This act includes

    • Receiving wireless packets

    • Data interpretation of received packets

    • Interactive mode settings -

    • Conversion of byte strings to ASCII format -

    hashtag
    Act Features

    • Receives packets from the child of the sample act and outputs them to the serial port.

    hashtag
    How to use Act

    hashtag
    TWELITE and wiring required

    Role
    Items

    Please check the following default settings at first.

    • Application ID: 0x1234abcd

    • Channel: 13

    hashtag
    Act Explanation

    hashtag
    Declaration part

    hashtag
    includes

    Include board behavior for . 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

    hashtag
    Other

    Declaration of default values, function prototypes, etc.

    hashtag
    setup()

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

    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 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 object, the LID and the retransmission count are set in the 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.

    hashtag
    loop()

    There is no processing during loop() in this sample.

    hashtag
    on_rx_packet()

    Callback function called when a packet is received. In this example, some output is produced for the received packet data.

    hashtag
    analyze_payload()

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

    circle-info

    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.

    hashtag
    Define and output your own ASCII format

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

    circle-info

    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.

    hashtag
    Dump output including NWK_SIMPLE header.

    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

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

    PAL_MOT-fifo

    is used to acquire sensor values.

    circle-check

    The ACT of the ACT includes the following.

    the_twelite

    The core class object of TWENET (mwx::twenet)

    The the_twelite object summarizes the procedures for using TWENET and includes procedures for operating the wireless microcontroller, such as basic wireless settings, sleep, and other procedures.

    hashtag
    summary.

    the_twelite is set up in the setup()

    BEHAVIOR

    BEHAVIOR can be defined by defining the class in the specified way, so that class object. The registered BEHAVIOR will be embedded in TWENET, allowing the user code to describe the application's behavior. It is possible to define callback functions for interrupts and events from TWENET, which is not possible in a loop description. Although it requires more definitions than a loop description, it is suitable for describing more complex applications.

    circle-check

    See sample BEHAVIOR .

    → Default channel
  • lid_default→ Default value of device ID (LID)

  • .hide_items()→ Item Hide Settings

  • Parent Device

    MONOSTICK BLUE or REDarrow-up-right

    Child Device

    Sample Act Child Setup (e.g. Slp_Wk_and_Tx, PAL_AMB, PAL_MAG, PAL_MOT???, etc...)

    <STG_STD>
    serparser
    <MONOSTICK>
    <STG_STD>
    <STG_STD>
    the_twelite
    nwk
    expand_bytes()
    ASCII format
    hashtag
    Class Definition (.hpp)

    BEHAVIOR definition requires a class definition as shown below.

    The above example defines a BEHAVIOR class with the name MY_APP_CLASS. MY_APP_CLASS must be described in several places.

    Define the class name and the base (parent) class. MWX_APPDEFS_CRTP() is a macro.

    Here, the necessary definitions are imported by #include.

    Constructor definition.

    hashtag
    methods

    hashtag
    loop()

    This is the main loop and has the same role as loop() in the global definition.

    hashtag
    on_create()

    on_create() is called at object creation time (use<>() method). The val is a parameter for future extension.

    hashtag
    on_begin()

    on_begin()is called aftersetup()ends.val` is a parameter for future extension.

    hashtag
    on_sleep()

    Called before sleep. val is a parameter for future extension.

    hashtag
    warmboot()

    Called in the initial stage when returning from sleep. The val is a parameter for future expansion.

    At this point, the peripherals have not yet been initialized. The sleep wake-up factor can be checked.

    hashtag
    wakeup()

    Called when waking up from sleep. The val is a parameter for future extension.

    circle-info

    Sleep can also be called here.

    hashtag
    receive()

    When a packet is received, it is called with the received packet information as rx.

    hashtag
    transmit_complete()

    The transmission information is called as evTx when packet transmission is completed. The evTx.u8CbId is the ID at the time of transmission and evTx.bStatus is the flag indicating success (1) or failure (0) of the transmission.

    hashtag
    Define handlers (.cpp)

    BEHAVIOR handlers (interrupt, event, and state definitions) are defined in a cpp file. The file cannot be split and all handler definitions must be in one file.

    circle-exclamation

    Even in the case of BEHAVIORs that do not define handlers, be sure to create the following cpp file.

    The required definitions of the MWX library (#include "_mwx_cbs_cpphead.hpp") must be included at the beginning and end of the cpp file.

    At the beginning of the file, include the .hpp file of the BEHAVIOR definition as shown above. Specify the class name of the behavior in __MWX_APP_CLASS_NAME. In the above, it is MY_APP_CLASS.

    At the end of the file, include the necessary definitions (#include "_mwx_cbs_cpptail.cpp").

    The handler definition is written as shown in the following example. Types of definitions are described later. The definition of the handler to be used is described by using the macro for definition. Do not write handlers that are not used.

    The MWX_????? _INT() is the definition of an interrupt handler, and MWX_? _EVENT() is the definition of an event handler, and MWX_STATE() is the state definition of a state machine.

    hashtag
    Interrupt and Event Handlers

    Interrupt handlers are executed when a microcontroller interrupt occurs, interrupting the code currently being executed. For this reason, it is desirable to write as short a process as possible, and great care must also be taken with regard to manipulation of variables and the like.

    The interrupt handler has a parameter uint8_t& handled, and setting this value to true will prevent subsequent event calls from being made.

    If the interrupt handler exits with handled set to false, the event handler will be called in the application loop (normal code). The event handler has no handled parameter. Since the event handler is normal code, it can perform relatively large processing. However, the event handler also incurs overhead, so it may not be able to handle the processing that is called at each frequent interrupt. In addition, since events are processed by the system's internal FIFO queue, events may be lost if they cannot be processed within a certain period of time.

    The following is an explanation of macros for defining handler functions.

    hashtag
    DIO

    DIO (digital IO) interrupt event. N specifies the target DIO number. The arg is a definition for future extension.

    To generate an interrupt, use pinMode(), attachDioInt().

    hashtag
    TICKTIMER

    TickTimer interrupt and event. The arg is a definition for future extension.

    triangle-exclamation

    The handled flag of the TickTimer must not be set to true, otherwise TWENET will not work.

    hashtag
    TIMER

    Timer interrupt event. TheN specifies the number of the target timer. The arg is a definition for future extension.

    In order to generate an interrupt, the Timer object is started with software interrupts enabled.

    hashtag
    Other

    circle-exclamation

    Definition of other interrupts and events that are not defined standardly in the MWX library and require an understanding of the AHI Peripherals Manual.

    Other interrupt events can be received by the following handler functions. These will not be available in the future when dedicated handlers are defined.

    Peripheral (AHI) interrupt handler u32DeviceId corresponds to arg and u32ItemBitmap corresponds to arg2.

    hashtag
    State Machine

    A state machine (state machine) is a method of describing an application that receives messages and operates by transitioning its state in response to those messages.

    The PAL_AMB-behavior sample describes the flow of the application's operation, including the start of sensor operation, acquisition of sensor values, wireless packet transmission to completion of transmission, and sleep transition. Please refer to it as an actual example.

    The events to be received are as follows.

    Event Name
    Description

    E_EVENT_START_UP

    It is called at system startup. Immediately after power-on, it is called with 0 parameters. Because it is in the initial stage of execution, PEV_Process() is called once from the begin() method to start the operation when transitioning to the normal processing state. It is still called after returning from sleep, but with parameters other than 0. Normal processing can be performed from this state.

    E_EVENT_NEW_STATE

    It is called in a new state immediately after a state transition. Describes the process that is first executed when a transition is made to a certain state.

    E_EVENT_TICK_TIMER

    Called by TickTimer every 1ms

    E_EVENT_TICK_SECOND

    It is called every second.

    hashtag
    PEV_SetState()

    The state is set to s.

    Exiting the state handler causes a transition to the next state, followed by a state handler being called with the E_EVENTS_NEW_STATE event.

    hashtag
    PEV_u32Elaspsed_ms()

    Returns the elapsed time ≪ ms] since the state transition. It is used for purposes such as managing timeouts.

    In the above example, a system reset is performed after 100 ms.

    hashtag
    PEV_Process()

    Called from outside the state handler. Execute the state handler with the event ev parameter u32evarg.

    The transmission completion event is communicated to the state machine. In other words, call the state handler.

    triangle-exclamation

    Do not call the state handler directly. It will cause problems such as E_EVENT_NEW_STATE not being executed.

    hashtag
    PEV_KeepStateOnWakeup()

    Set just before sleep. After returning from sleep, the previous state is maintained. That is, the state handler is called with E_EVENT_START_UP with sleep started.

    hashtag
    PEV_is_coldboot()

    Determine if the event is E_EVENT_START_UP on wake-up.

    hashtag
    PEV_is_warmboot()

    Judges whether the event is E_EVENT_START_UP when returning from sleep.

    the_twelite
    PAL_AMB-behavior
    // use twelite mwx c++ template library
    #include <TWELITE>
    #include <MONOSTICK>
    #include <NWK_SIMPLE>
    #include <STG_STD>
    // application ID
    const uint32_t DEFAULT_APP_ID = 0x1234abcd;
    // channel
    const uint8_t DEFAULT_CHANNEL = 13;
    // option bits
    uint32_t OPT_BITS = 0;
    
    /*** function prototype */
    bool analyze_payload(packet_rx& rx);
    auto&& brd = the_twelite.board.use<MONOSTICK>();
    auto&& set = the_twelite.settings.use<STG_STD>();
    auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    set << SETTINGS::appname("PARENT"); // Title in the settings screen
    set << SETTINGS::appid_default(DEFAULT_APP_ID); // Application ID Default
    set << SETTINGS::ch_default(DEFAULT_CHANNEL); // channel default
    set << SETTINGS::lid_default(0x00); //LID Default
    set.hide_items(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);
    set.reload(); // Read settings from non-volatile memory
    OPT_BITS = set.u32opt1(); // Example of reading (option bits)
    the_twelite
    	<< set                    // Reflects interactive mode settings
    	<< TWENET::rx_when_idle() // Set RF to receive when idle.
    	;
    
    // Register Network
    nwk << set;		            // Reflects interactive mode settings
    nwk << NWK_SIMPLE::logical_id(0x00) // Only the LID is reconfigured.
    	;
    brd.set_led_red(LED_TIMER::ON_RX, 200); // RED (on receiving)
    brd.set_led_yellow(LED_TIMER::BLINK, 500); // YELLOW (blinking)
    the_twelite.begin(); // start twelite!
    void loop() {
    }
    void on_rx_packet(packet_rx& rx, bool_t &handled) {  
    	Serial << ".. coming packet (" << int(millis()&0xffff) << ')' << mwx::crlf;
    
      ...
      
    	// packet analyze
    	analyze_payload(rx);
    }
    bool b_handled = false;
    
    uint8_t fourchars[4]{};
    auto&& np = expand_bytes(
    	    rx.get_payload().begin(), rx.get_payload().end()
    		, fourchars
        );
        
    if (np == nullptr) return;
    
    // display fourchars at first
    Serial
    	<< fourchars 
    	<< format("(ID=%d/LQ=%d)", rx.get_addr_src_lid(), rx.get_lqi())
    	<< "-> ";
    	
    char fourchars[5]{}; // Allocate 5 bytes including the terminating character `\0`.
    auto&& np = expand_bytes(
    	    rx.get_payload().begin(), rx.get_payload().end()
    		, make_pair((char *)fourchars, 4)
    	);
    // Slp_Wk_and_Tx
    if (!b_handled && !strncmp(fourchars, "TXSP", 4)) {
    	b_handled = true;
    	uint32_t tick_ms;
    	uint16_t u16work_ct;
    
    	np = expand_bytes(np, rx.get_payload().end()
    		, tick_ms
    		, u16work_ct
    	);
    
    	if (np != nullptr) {
    		Serial << format("Tick=%d WkCt=%d", tick_ms, u16work_ct);
    	} else {
    		Serial << ".. error ..";
    	}
    }
    smplbuf_u8<128> buf;
    mwx::pack_bytes(buf
    	, uint8_t(rx.get_addr_src_lid()		// Logical ID of the sender
    	, uint8_t(0xCC)											   // 0xCC
    	, uint8_t(rx.get_psRxDataApp()->u8Seq)	// Sequence number of the packet
    	, uint32_t(rx.get_addr_src_long())	// Serial number of the sender.
    	, uint32_t(rx.get_addr_dst())		// destination address
    	, uint8_t(rx.get_lqi())			// LQI:Link Quality Indicator
    	, uint16_t(rx.get_length())		// Number of following bytes
    	, rx.get_payload() 			// data payload
    );
    
    serparser_attach pout;
    pout.begin(PARSER::ASCII, buf.begin(), buf.size(), buf.size());
    
    Serial << "FMT PACKET -> ";
    pout >> Serial;
    Serial << mwx::flush;
    serparser_attach pout;
    pout.begin(PARSER::ASCII, rx.get_psRxDataApp()->auData, 
        rx.get_psRxDataApp()->u8Len, rx.get_psRxDataApp()->u8Len);
    
    Serial << "RAW PACKET -> ";
    pout >> Serial;
    Serial << mwx::flush;
    
    // Reference: packet structure of the control unit.
    // uint8_t  : 0x01 fixws
    // uint8_t  : LID of sender
    // uint32_t : Long address (Serial Number) of sender
    // uint32_t : Destination address
    // uint8_t  : Number of repeating
    class MY_APP_CLASS: MWX_APPDEFS_CRTP(MY_APP_CLASS)
    {
    public:
        static const uint8_t TYPE_ID = 0x01;
    
        // load common definition for handlers
        #define __MWX_APP_CLASS_NAME MY_APP_CLASS
        #include "_mwx_cbs_hpphead.hpp"
        #undef __MWX_APP_CLASS_NAME
    
    public:
        // constructor
        MY_APP_CLASS() {}
    
        void _setup() {}    
        void _begin() {}
    
    public:
        // TWENET callback handler (mandate)
        void loop() {}
        void on_sleep(uint32_t & val) {}
        void warmboot(uint32_t & val) {}
        void wakeup(uint32_t & val) {}
    
        void on_create(uint32_t& val) { _setup();  }
        void on_begin(uint32_t& val) { _begin(); }
        void on_message(uint32_t& val) { }
    
    public:
        void network_event(mwx::packet_ev_nwk& pEvNwk) {}
        void receive(mwx::packet_rx& rx) {}
        void transmit_complete(mwx::packet_ev_tx& evTx) {}
    };
    class MY_APP_CLASS: MWX_APPDEFS_CRTP(MY_APP_CLASS)
        #define __MWX_APP_CLASS_NAME MY_APP_CLASS
        #include "_mwx_cbs_hpphead.hpp"
        #undef __MWX_APP_CLASS_NAME
    MY_APP_CLASS() {}
    void receive(mwx::packet_rx& rx)
    void transmit_complete(mwx::packet_ev_tx& evTx)
    #include <TWELITE>
    #include "myAppClass.hpp" // BEHAVIOR definition file
    
    /*****************************************************************/
    // MUST DEFINE CLASS NAME HERE
    #define __MWX_APP_CLASS_NAME MY_APP_CLASS
    #include "_mwx_cbs_cpphead.hpp" // Definition at the beginning
    /*****************************************************************/
    /*****************************************************************/
    // common procedure (DO NOT REMOVE)
    #include "_mwx_cbs_cpptail.cpp"
    // MUST UNDEF CLASS NAME HERE
    #undef __MWX_APP_CLASS_NAME
    /*****************************************************************/
    // TickTimer interrupt
    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);
    }
    
    // PAL_AMB::PIN_BIN(12) event
    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;
    }
    
    // Operation definition of state STATE_0
    MWX_STATE(E_MWX::STATE_0, uint32_t ev, uint32_t evarg) {
    	if (ev == E_EVENT_START_UP) {
    		Serial << "[STATE_0:START_UP]" << mwx::crlf;	
    	} else
    	if (ev == E_ORDER_KICK) {
    		PEV_SetState(E_MWX::STATE_1);
    	}
    }
    
    // Operation definition for state STATE_1
    MWX_STATE(E_MWX::STATE_1, uint32_t ev, uint32_t evarg) {
    	if (ev == E_EVENT_NEW_STATE) {
    		Serial << "[STATE_1]" << mwx::crlf;	
    	} else
    	if (ev == E_ORDER_KICK) {
    		PEV_SetState(E_MWX::STATE_2);
    	} else
    	if (ev == E_EVENT_TICK_SECOND) {
    		Serial << "<1>";
    	}	
    }
    MWX_DIO_INT(N, uint32_t arg, uint8_t& handled)
    MWX_DIO_EVENT(N, arg)
    MWX_TICKTIMER_INT(uint32_t arg, uint8_t& handled)
    MWX_TICKTIMER_EVENT(uint32_t arg)
    MWX_TIMER_INT(N, uint32_t arg, uint8_t& handled)
    MWX_TIMER_EVENT(N, uint32_t arg)
    MWX_MISC_INT(uint32_t arg, uint32_t arg2, handled)
    MWX_MISC_EVENT(auint32_t rg, uint32_t arg2)
    void PEV_SetState(uint32_t s)
    uint32_t PEV_u32Elaspsed_ms()
    MWX_STATE(MY_APP_CHILD::STATE_TX, uint32_t ev, uint32_t evarg) {
      ...
      
    	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();
    	}
    }
    void PEV_Process(uint32_t ev, uint32_t u32evarg) {
    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
    }
    void PEV_KeepStateOnWakeup()
    bool PEV_is_coldboot(uint32_t ev, uint32_t u32evarg)
    bool PEV_is_warmboot(uint32_t ev, uint32_t u32evarg)
    Sending and receiving wireless packets
  • Interactive settings mode configuration - <STG_STD>

  • State transition control by state machine - <SM_SIMPLE>

  • <PAL_MOT> or <CUE>Board operation with board BEHAVIOR

  • hashtag
    ACT Features

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

    hashtag
    how to use act

    hashtag
    Required TWELITE

    Role
    Example

    Parent Node

    Act to work.

    Child Node

    +

    hashtag
    Explanation of ACT

    hashtag
    Include

    Board BEHAVIOR <PAL_MOT> is included.

    hashtag
    setup()

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

    hashtag
    Accelerometer initialization

    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.

    hashtag
    begin()

    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.

    hashtag
    sleepNow()

    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.

    hashtag
    wakeup()

    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().

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

    MOTION SENSE PALarrow-up-right
    function, which also calls
    the_twelite.begin()
    to start the process. It is not possible to set up
    the_twelite
    except in
    setup()
    .

    In the above example, the application ID is set, the communication channel is set, and the receiving circuit is set.

    Various procedures are included.

    It is also possible to register classes that handle wireless networks, classes that summarize board support, and classes that perform event-driven processing described by the user. By registering these classes, dedicated functions can be easily used. These classes are referred to as "behaviors" in this explanation.

    In the above example, two types of behaviors are registered: environmental sensor pal behavior <PAL_AMB> and simple relay network <NWK_SIMPLE>. By registering these, hardware such as sensors on the environmental sensor pal can be easily handled. In addition, the complicated handling of wireless packets can be implicitly provided with functions such as relay processing and automatic discarding of duplicate packet arrivals.

    hashtag
    Methods

    circle-exclamation

    The MWX library defines other methods in addition to those introduced here.

    Act descriptions include those that are not directly related to the act description, those that are set up but do not function effectively, and those that are used internally.

    hashtag
    <<operator (setting)

    The << operator is used to initialize the object the_twelite.

    The following configuration class objects are used as input, and default values are applied if no configuration is made.

    hashtag
    TWENET::appid(uint32_t id)

    Set the parameter id to the specified application ID. This is a required specification.

    Reading the settings is done with uint32_t the_twelite.get_appid().

    hashtag
    TWENET::channel(uint8_t ch)

    Set the specified channel number (11..26) in parameter ch.

    Reading the settings is done with uint8_t the_twelite.get_channel().

    hashtag
    TWENET::tx_power(uint8_t pw)

    Parameter pw is set to (0. 3) for the radio output. The default is (3: no output attenuation).

    Reading the settings is done with uint8_t the_twelite.get_tx_power().

    hashtag
    TWENET::rx_when_idle(uint8_t bEnable)

    If the parameter bEnable is 1, the receiver circuit is always activated and can receive radio packets from others. The default is 0, which is dedicated to transmission only.

    Reading the settings is done with uint8_t the_twelite.get_rx_when_idle().

    hashtag
    TWENET::chmgr(uint8_t ch1 = 18, uint8_t ch2 = 0, uint8_t ch3 = 0)

    Enables the channel manager. If multiple channels are specified, sending/receiving is performed on multiple channels; if 0 is specified for ch2 and ch3, the specification is disabled.

    hashtag
    STG_STD

    Reflects the setting values in interactive mode.

    The items that will be reflected are as follows

    • app_id

    • channel

    • tx_power

    • Number of retransmissions when MAC ack is used

    circle-exclamation

    There are other settings in the MWX library code that are irrelevant to the library's functionality at this time or that may cause conflicts if set.

    hashtag
    begin()

    It is executed after preconfiguration (see << operator) and registration of the behavior. It is usually placed at the end of the setup() function.

    • the_twelite finished setting up

    • Behavior initialization

    circle-exclamation

    TWENET initialization is also performed after the setup() function exits. Since many processes are to be executed after setup() exits, do not do anything other than initialization here.

    hashtag
    例

    hashtag
    change_channel()

    Changes channel settings. On failure, the channel is not changed and false is returned.

    hashtag
    get_channel_phys()

    Obtains the currently set channel number (11..26) from the MAC layer API.

    hashtag
    get_hw_serial()

    Get the module serial number.

    hashtag
    sleep()

    Put the module to sleep.

    parameter
    explanation

    u32Periodms

    Sleep duration[ms]

    bPeriodic

    Recalculate the next wake-up time based on the previous wake-up time. ※It may be from the current timing for reasons such as the next wake-up timing is imminent.

    bRamoff

    Set to true to sleep without retaining RAM (must be reinitialized from setup() instead of wakeup() after wakeup)

    u8Device

    Designation of a wake-up timer to be used for sleep. Specify

    TWENET::SLEEP_WAKETIMER_PRIMARYorTWENET::SLEEP_WAKETIMER_SECONDARY.

    circle-info

    Before sleep, the on_sleep() method of the built-in object or behavior is called to perform the pre-sleep procedure. After returning from sleep, the opposite procedure is performed by the on_wakeup() method.

    hashtag
    is_wokeup_by_dio()

    Returns true if the return factor from sleep is the specified digital pin.

    hashtag
    is_wokeup_by_wktimer()

    Returns true if the wake-up timer is the wake-up factor for returning from sleep.

    hashtag
    reset_system()

    Resets the system. After reset, the process starts from setup().

    hashtag
    stop_watchdog()

    Stops the watchdog timer. Stop the timer if you are going to wait for polling for a long time.

    circle-info

    The watchdog timer is restarted each time in the main loop inside the library. It takes about 4 seconds for the timer to run out and reset.

    hashtag
    restart_watchdog()

    Restart the watchdog timer.

    hashtag
    Behavior.

    Three behaviors can be registered with the_twelite, and the following class objects are defined to store them.

    • network : A behavior that implements a network. Normally, <NWK_SIMPLE> is registered.

    • network2 : BEHAVIOR that implements networking. Use this behavior if you want another network BEHAVIOR to process packets which were not accepted by the first network due to the data structure of the payload or other reasons. (Reference: Using NWK_LAYERED and NWK_SIMPLE together)

    • board: Behaviors for board support. Procedures for using each device on the board are added.

    • app: Behaviors describing user applications. Behaviors can be written in terms of interrupts, events, and state transitions using state machines. It is also easy to define multiple application descriptions and select an application with completely different behavior at startup.

    • settings: Behavior for performing configuration (interactive mode). Registers .

    hashtag
    use<B>()

    Register behavior . Registration is done within setup()arrow-up-right. The return value is a reference to the object corresponding to the registered behavior.

    After registration, the object is retrieved in the same way as at the time of registration.

    triangle-exclamation

    If an incorrect behavior is specified, a panic operation (infinite loop) occurs and program operation stops.

    circle-info

    It is not intended to declare objects of the behavior as global variables. Please use use<B>() for each use.

    However, it is possible to define a pointer to an object in a global variable and write as follows (The MWX library has a policy of minimizing the use of pointer types and using reference types, so the following description is not recommended.)

    hashtag
    class object.

    the_twelite defines the three class objects board, network, and app mentioned above, but also the following

    hashtag
    tx_status

    Notification of transmission completion status.

    circle-info

    The description of the event-driven behavior is managed by the transmit_complete() callback.

    hashtag
    is_complete()

    Returns true when the packet with the specified ID has completed transmission.

    hashtag
    is_success()

    Returns true when the packet with the specified ID has completed transmission and has been successfully sent.

    hashtag
    receiver

    Retrieve incoming packets.

    circle-info

    In the event-driven behavior description, it is obtained in the receive() callback.

    circle-exclamation

    The received packet data obtained by the read() method is designed to be overwritten when subsequent packets are received and processed. If you read the data immediately after available() and do some short processing, this will not be a problem, but as a general rule, read the data, copy the data needed for application use, and finish loop() promptly. For example, a long delay() during loop() will cause incoming packets to be dropped.

    hashtag
    available()

    Returns true if there is an incoming packet that has not yet been read.

    hashtag
    read()

    Read packets.

    Terms

    The following is a supplement to the terminology used in this document.

    circle-exclamation

    The explanation of terms may not be in accordance with the definitions provided in standards and other documents.

    hashtag
    General terms

    hashtag
    SDK (TWELITE SDK, MWSDK)

    Software Development Environment

    circle-info

    The SDK for software development of TWELITE wireless microcontrollers is called TWELITE SDK (or MWSDK).

    hashtag
    IEEE802.15.4

    This is the radio standard used by the TWELITE radio module; as long as you use the MWX library, you do not need to be aware of the details of the radio standard.

    hashtag
    packet

    The smallest unit of communication in wireless communications.

    The maximum amount varies depending on the communication method and settings during communication, but in the MWX library standard communication <NWK_SIMPLE>, the amount of data a user can send in one packet is 90 bytes.

    hashtag
    payload (of a packet)

    It refers to the body of data contained in a wireless packet.

    hashtag
    node

    It refers to a radio station in a wireless network.

    hashtag
    MWX library specific terms

    hashtag
    ACT

    A program created using this library. This refers to its source code or the program that runs.

    hashtag

    A program in the form of an event, among other ACTs. The source code or the program that runs it.

    BEHAVIORs are described by a single class definition, which describes callback functions from TWENET, events, and interrupt processing, all in one place. There are three types of behaviors available in the MWX library:

    • Application BEHAVIOR: A class defined by the user that describes the application in an event-driven manner.

    • Board BEHAVIOR: A class to simplify the use of the functionality of the board that implements the TWELITE radio module.

    • Network BEHAVIOR: A class for simplifying procedures in wireless networks.

    Behavior names are enclosed in < >. For example, the behavior name for a simple relay network is <NWK_SIMPLE>.

    hashtag
    Class objects

    In the description of this library, objects that are declared globally from the beginning in the library are referred to as class objects: Serial, Wire, etc. These class objects can be used without any procedure or by performing the start procedure.

    Class objects that consume relatively large amounts of memory allocate memory along with the initialization parameters during the initialization procedure (.setup() or .begin() method).

    hashtag
    C++ terms

    circle-info

    This is a general term, and assumes knowledge of the C language.

    hashtag
    C++

    The C++ language.

    circle-info

    The MWX library is written in C++ and C.

    hashtag
    C++11

    One of the versions of the C++ standard, meaning C++ as of 2011, which was standardized by ISO in 2011. It has been greatly enhanced since its predecessor, C++03. Newer versions such as C++14 and C++17 are available.

    circle-info

    The MWX library is implemented using features and syntax added in C++11, and MWSDK compiler support is limited to C++11.

    hashtag
    Class

    A collection of procedures that focus on some data in a single place. A structure contains the procedures for handling that structure. It actually develops into a much deeper topic, but please refer to technical books.

    In C++, the keywords struct and class are essentially the same thing, and a class is a class regardless of which keyword it is declared with.

    If the above class definition was also done in C, for example, it would look like the following:

    hashtag
    Wrapper class

    It is a class that includes existing C libraries and their internal structures, and adds C++ specific functionality to make it easier to use. In some cases, the description "wrapped ~structure" is used in the explanation.

    circle-info

    The MWX library is a combination of wrapper classes from TWENET's C library and a set of newly implemented classes.

    hashtag
    Method, Member function

    A function that is defined in a class and is associated with the class.

    hashtag
    Object (instance)

    A class is materialized (allocated memory).

    In this explanation, object and instance are treated as having the same meaning.

    hashtag
    Constructor

    Initialization procedure at the time of object creation.

    hashtag
    Destructor

    This is the procedure when the object is destroyed, paired with the constructor.struct myhello {

    hashtag
    abstract class

    In C++, polymorphism is achieved by virtual classes. Specifically, a class that defines a pure virtual function specified by the virtual keyword.

    circle-exclamation

    The MWX library does not use virtual functions due to compiler limitations and performance reasons. We use a to achieve polymorphism.

    hashtag
    Scope

    In the C/C++ language, think of it as a scope enclosed in { }. The objects created in this scope are destroyed when they leave the scope. The destructor is called at this time.

    The following is an explicitly scoped version of helo2, which is discarded after line 8 and the destructor is called.

    MThe MWX library uses the following notation. Here, the validity period of an object declared within the conditional expression of an if statement (older C languages such as C89 do not allow declarations in such a place) is within {} of the if statement.

    For example, a two-wire serial bus is a procedure where there is a start and end procedure and the bus is manipulated by an object only during that time. After the object is created, if the bus is properly connected, the true clause of the if statement is executed and the bus is written or read by the created object. When the bus read/write operation is completed, the if statement is exited and the destructor is called to terminate the bus usage.

    hashtag
    Namespace

    The namespace are actively used in C++ to avoid duplication of definition names. To access a definition in a namespace, use::.

    hashtag
    Template

    Think of a template as an extension of a C macro.

    This example defines a simple array, where T and N are template parameters, where T is the type name and N is a number, and defines an array class of type T with N elements.

    hashtag
    nullptr

    In C++11, NULL pointers are now written as nullptr.

    hashtag
    reference type

    In C++, reference types are available. This is similar to access by pointer, but with the restriction that it must refer to an object.

    For functions with pass-by-reference parameters like the one below, the value of i can be rewritten in incr().

    In the example of the template explanation, the return type of operator[] is changed to T&. By doing so, it becomes possible to perform assignment operations directly on the data inside the array, such as a[0]=1.

    circle-info

    As a rule, the programming interface of the MWX library does not use pointer types, but reference types.

    hashtag
    type inference

    C++11 introduces the auto keyword for type inference. This allows the compiler to infer the type of an object from its initialization description, thus omitting the need to specify the specific type name. This is effective in cases where class names using template are very long.

    In most of the explanations, auto&&, which is called universal reference, is used. Universal references can be written here without being aware of passing references.

    hashtag
    container

    A class for storing multiple objects of a specific data type such as arrays is called a container. An array class such as myary mentioned in the template example is also called a container.

    circle-info

    The MWX library provides an array class smplbuf and a FIFO queue class smplque.

    hashtag
    Iterator, .begin(), .end()

    A pointer in C can be thought of as a way to access a contiguous set of memory elements in a continuous manner from the beginning to the end. The simplest implementation of a FIFO queue is a ring buffer, but there is no memory continuity. Even such a data structure can be described in the same way as a pointer using an iterator.

    The methods .begin() and .end() are used to get the iterator. The iterator that points to the beginning of the container is obtained with .begin(). The iterator that points to the next to the end is obtained with .end(). The reason for using the next to the end instead of the end is for the clarity of the loop description in the for and while statements, and to handle the case where the number of elements stored in the container is zero.

    In the above, some_process() is applied to each element of the que using the iterator p. p is incremented by the ++ operator as an iterator that points to the next element. Even if the container has a data structure that cannot be described by a pointer, it can be processed in the same way as using a pointer.

    Since .end() indicates the next to the end, the end decision of the while statement is as simple as (p ! = e), which is concise. If there is no element in the queue, .begin() returns the same iterator as .end(). If there are no elements in the queue, .begin() will return the same iterator as .end(), which is the next iterator after the iterator for the unstored elements.

    For a contiguous container in memory, its iterator will usually be a normal pointer. It is not expected to be a large overhead during its operation.

    hashtag
    C++ standard library

    The C++ standard library includes the STL (Standard Template Library), which is part of the MWX library.

    circle-exclamation

    Due to the of the C/C++ compiler for TWELITE, only a few features are available.

    hashtag
    Algorithm

    In C, for example, the process of finding the maximum or minimum value is written separately depending on the type. In C++, you can use templates, iterators, etc. to describe such operations independently of their types. This is called an algorithm.

    For example, the algorithm to find the maximum value as shown above. This algorithm is type-independent. (It is called generic programming.)

    Here we specify an iterator for que and apply the algorithm std::minmax_elenet to obtain its maximum and minimum. std::minmax_elemet is defined in the C++ standard library. Its return value is std::pair, which combines any two values. The algorithm calculates the maximum and minimum values if the elements indicated by the iterator can be compared with each other using operators such as <,>,==. The return type is also derived from the iterator type.

    PAL_AMB-behavior

    is used to acquire sensor values.

    • [BEHAVIOR] (./) is used to describe the Parent Node Child Node.

    • The sensor is described directly using instead of using the function to obtain values.

    #include <TWELITE>
    #include <NWK_SIMPLE>
    #include <PAL_>
    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;
    }
    auto&& brd = the_twelite.board.use<PAL_MOT>();
    
    u8ID = (brd.get_DIPSW_BM() & 0x07) + 1;
    if (u8ID == 0) u8ID = 0xFE; // 0 is to 0xFE
    	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
    	brd.sns_MC3630.begin(SnsMC3630::Settings(
    		SnsMC3630::MODE_LP_14HZ, SnsMC3630::RANGE_PLUS_MINUS_4G));
    void begin() {
    	sleepNow(); // the first time is just sleeping.
    }
    void sleepNow() {
    	pinMode(PAL_MOT::PIN_SNS_INT, WAKE_FALLING);
    	the_twelite.sleep(60000, false);
    }
    void wakeup() {
    	Serial << "--- PAL_MOT(Cont):" << FOURCHARS
    	       << " wake up ---" << mwx::crlf;
    
    	b_transmit = false;
    	txid[0] = 0xFFFF;
    	txid[1] = 0xFFFF;
    }
    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();
    		}
    	}
    }
    	if (!b_transmit) {
    if (!brd.sns_MC3630.available()) {
    	Serial << "..sensor is not available." 
    			<< mwx::crlf << mwx::flush;
    	sleepNow();
    }
    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;
    }
    		for (int ip = 0; ip < 2; ip++) {
    // 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
    );
    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.
    }
    MWX_APIRET ret = pkt.transmit();
    
    if (ret) {
    	Serial << "..txreq(" << int(ret.get_value()) << ')';
    	txid[ip] = ret.get_value() & 0xFF;
    } else {
    	sleepNow();
    }
    } else {
    	if(		the_twelite.tx_status.is_complete(txid[0])
    		 && the_twelite.tx_status.is_complete(txid[1]) ) {
    
    		sleepNow();
    	}
    }
    void loop() {
      auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    }
    NWK_SIMPLE *pNwk = nullptr;
    
    setup() {
      auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    	pNwk = &nwk;
    }
    
    void transmit() {
      if (auto&& pkt = pNwk->prepare_tx_packet()) {
        ...
      }
    }
    void setup() {
      ...
     	the_twelite
    		<< TWENET::appid(APP_ID)    
    		<< TWENET::channel(CHANNEL) 
    		<< TWENET::rx_when_idle();  
      ...
      the_twelite.begin();
    }
    // Get a serial number
    uint32_t u32hwser = the_twelite.get_hw_serial();
    
    // Set channel to 11
    the_twelite.change_channel(11);
    
    // Sleep for 1 second.
    the_twelite.sleep(1000);
    
    // Perform a reset
    the_twelite.reset_system();
    void setup() {
    	/*** SETUP section */
    	// use PAL_AMB board support.
    	auto&& brd = the_twelite.board.use<PAL_AMB>();
    	
    	...
    	
    	// Register Network
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    	nwk << NWK_SIMPLE::logical_id(u8ID);
    auto&& set = the_twelite.settings.use<STG_STD>();
    ...
    set.reload();       // Load settings
    the_twelite << set; // Reflects interactive mode settings
    void begin()
    void setup() {
    	// use PAL_AMB board support.
    	auto&& brd = the_twelite.board.use<PAL_AMB>();
    	
    	// settings
     	the_twelite
    		<< TWENET::appid(APP_ID)    
    		<< TWENET::channel(CHANNEL) 
    		<< TWENET::rx_when_idle();  
    	
    	// Register Network
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    	nwk << NWK_SIMPLE::logical_id(u8ID);
    	
    	// somo others
    	
    	// begin the TWENET!
    	the_twelite.begin();
    }
    inline bool change_channel(uint8_t u8Channel)
    uint8_t get_channel_phys()
    inline uint32_t get_hw_serial()
    inline void sleep(
            uint32_t u32Periodms, 
            bool_t bPeriodic = true, 
            bool_t bRamOff = false, 
            uint8_t u8Device = TWENET::SLEEP_WAKETIMER_PRIMARY)
    bool is_wokeup_by_dio(uint8_t port)
    bool is_wokeup_by_wktimer()
    inline void reset_system()
    inline void stop_watchdog()
    inline void restart_watchdog()
    // example
    auto&& brd = the_twelite.board.use<PAL_AMB>();
    bool is_complete(uint8_t cbid)
    bool is_success(uint8_t cbid)
    bool available()
    packet_rx& read()
    <STG_STD>
    MONOSTICK BLUE or REDarrow-up-right
    Parent_MONOSTICK
    BLUE PAL or RED PALarrow-up-right
    Motion Sensor PAL MOTION SENSE PALarrow-up-right
    BEHAVIOR
    differnet approach
    limitations
    struct myhello {
      int _i;
      void say_hello() { printf("hello %d\n", _i); }
    };
    typedef struct _c_myhello {
      int _i;
      void (*pf_say_hello)(struct _c_myhello *);
    } c_myhello;
    
    void say_hello(c_myhello*p) { p->pf_say_hello(); }
    void init_c_my_hello(c_myhello*p) {
      p->pf_say_hello = say_hello;
    }
    struct myhello {
      int _i;
      void say_hello() { printf("hello %d\n", _i); } //Method
    };
    void func() {
        myhello obj_hello; // obj_hello is an object of myhello class
        obj_hello._i = 10;
        obj_hello.say_hello();
    }
    struct myhello {
      int _i;
      void say_hello() { printf("hello %d\n", _i); }
      
      myhello(int i = 0) : _i(i) {} // constructor
    };
    
    void my_main() {
      myhello helo(10); // Here, the constructor is called and set to _i=10
    }
      int _i;
      void say_hello() { printf("hello! %d\n", _i); }
      
      myhello(int i = 0) : _i(i) {} // constructor
      ~myhello() {
        printf("good bye! %d\n", _i);
      } // destructor
    };
    struct Base {
      virtual void say_hello() = 0;
    };
    
    struct DeriveEng : public Base {
      void say_hello() { printf("Hello!"); }
    };
    
    struct DeriveJpn : public Base {
      void say_hello() { printf("Kontiwa!"); }
    };
    void my_main() {
      myhello helo1(1);
      helo1.say_hello();
      
      {
        myhello helo2(2);
        helo2.say_hello();
      }
    }
    
    // hello! 1
    // hello! 2
    // good bye! 2
    // good bye! 1
    struct myhello {
      int _i;
      void say_hello() { printf("hello! %d\n", _i); }
      operator bool() { return true; } // Operators for judgment in if()
      
      myhello(int i = 0) : _i(i) {} // constructor
      ~myhello() { printf("good bye! %d\n", _i); } // constructor
    };
    
    // Function to create a myhello object (generator)
    myhello gen_greeting() { return my_hello(); }
    
    void my_main() {
      if (myhello x = gen_greeting()) {
        // The object x in myhello is valid during the if statement
        x.say_hello();
      }
      // object x is destroyed when exiting the if minute
    }
    const uint8_t DEV_ADDR = 0x70;
    if (auto&& wrt = Wire.get_writer(DEV_ADDR)) { //バスの初期化、接続判定
    	wrt(SHTC3_TRIG_H); // 書き出し
    	wrt(SHTC3_TRIG_L);
    } // バスの利用終了手続き
    namespace MY_NAME { // 名Namespace declaration
      const uint8_t MYVAL1 = 0x00;
    }
    
    ...
    void my_main() {
      uint8_t i = MY_NAME::MYVAL1; // reference of MY_NAME
    }
    template <typename T, int N>
    class myary {
      T _buf[N];
    public:
      myary() : _buf{} {}
      T operator [] (int i) { return _buf[i % N]; }
    };
    
    myary<int, 10> a1; // Array of type int with 10 elements
    myary<char, 128> a2; // Array of char type with 128 elements
    void incr(int& lhs, int rhs) { lhs += rhs; }
    
    void my_main() {
      int i = 10; j = 20;
      incr(i, j);
    }
    template <typename T, int N>
    class myary {
      T _buf[N];
    public:
      myary() : _buf{} {}
      T& operator [] (int i) { return _buf[i % N]; }
    };
    
    myary<int, 10> a1;
    void my_main() {
      a1[0] = 1;
      a1[1] = 2;
    }
    auto&& p = std::make_pair("HELLO", 5);
           // const char* と int のペア std::pair
    my_queue que; // my_queue is a class of queue
    
    auto&& p = que.begin();
    auto&& e = que.end();
    
    while(p != e) {
      some_process(*p);
      ++p;
    }
    // Return the iterator with the maximum value with any iterator as a parameter.
    template <class Iter>
    Iter find_max(Iter b, Iter e) {
      Iter m = b; ++b;
      while(b != e) {
        if (*b > *m) { m = b; }
        ++b;
      }
      return m;
    }
    #include <algorithm>
    
    auto&& minmax = std::minmax_element( // Algorithm for obtaining the maximum minimum
      que.begin(), que.end());
    
    auto&& min_val = *minmax.first;
    auto&& max_val = *minmax.second;

    Child Nodes are described by a state machine.

    circle-check

    See the explanation of BRD_APPTWELITE, the explanation of PAL_AMB, and the explanation of PAL_AMB-usenap before the explanation of this ACT. Also see the description of BEHAVIOR.

    circle-info

    This sample shows how to write BEHAVIOR. BEHAVIORS are used to describe more complex applications.

    hashtag
    ACT FEATURES.

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

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

    hashtag
    How to use ACT

    hashtag
    Preparation for TWELITE

    Role
    Example

    Parent Node

    Child Node

    +

    circle-exclamation

    When using PAL as the Parent Node, coin cell batteries cannot be used. As a rule of thumb, prepare a power supply environment that can provide a stable current of 50 mA or more.

    hashtag
    File Structure

    • PAL_AMB-behavior.hpp : Only setup() is defined. read DIP-SW and if D1..D3 is upper position, it works as Parent Node, otherwise it sets ID corresponding to DIP SW as Child Node.

    • Parent/myAppBhvParent.hpp : behavior class definition for Parent Node

    • Parent/myAppBhvParent.cpp : implementation

    • Parent/myAppBhvParent-handlers.cpp : implementation of handlers

    • Parent/myAppBhvParent.hpp : behavior class definition for Child Nodes

    • Parent/myAppBhvParent.cpp : implementation

    • Parent/myAppBhvParent-handlers.cpp : implementation of handlers

    The Parent Node's BEHAVIOR name is <MY_APP_PARENT> and the Child Node is <MY_APP_CHILD>.

    circle-info

    Build files can be added by Makefile descriptionarrow-up-right /... /install_n_build/makefile.md).

    hashtag
    Initialization setup()

    If the DIP SW reading is 0, register the behavior <MY_APP_PARENT> for the Parent Node, otherwise register the behavior <MY_APP_CHILD> for the Child Node.

    circle-exclamation

    If the Parent Node is MONOSTICK, the DIP SW for PAL reads 0 and behaves as the Parent Node. However, this behavior is not defined in the MONOSTICK specifications.

    hashtag
    Parent Node BEHAVIOR

    The Parent Node behaves as a non-sleeping receiver and outputs packet information to the serial port when it receives a packet from a Child Node.

    hashtag
    MY_APP_PARENT::receive()

    When a packet is received for the Parent Node, if the first four characters of the packet can be matched (FOURCHARS), the contents of the packet are displayed.

    hashtag
    MY_APP_PARENT::MWX_TICKTIMER_INT()

    The Parent Node's interrupt handler blinks the LED.

    hashtag
    MY_APP_PARENT::MWX_DIO_EVENT(PAL_AMB::PIN_BTN)

    When the button (5) on the PAL is pressed, the E_ORDER_KICK event is issued to the state machine.

    hashtag
    MY_APP_PARENT::MWX_STATE(E_MWX::STATE_0 .. 3)

    The state machine is described as a reference for state transitions and is not meaningful for the operation of the application. It executes state transitions by the E_ORDER_KICK event sent from the button, timeouts, and so on.

    hashtag
    BEHAVIOR of Child Node

    The behavior flow of the Child Node is the same as that of the PAL_AMB-usenap. From the initial sleep, "wake up → start sensor operation → short sleep → wake up → acquire sensor value → wireless transmission → wait for wireless transmission completion → sleep" is repeated.

    hashtag
    MY_APP_CHILD::on_begin()

    The _begin() function, called from on_begin(), executes the first sleep.

    (*It is acceptable to describe this process directly in on_begin() without describing it in _begin() function.)

    hashtag
    MY_APP_CHILD::wakeup()

    This is a description of the process of waking up from sleep.

    The first time Wire.begin() is executed here, which is redundant for the second and later times when the device wakes from sleep. This process can be moved to on_begin().

    hashtag
    MY_APP_CHILD::transmit_complete()

    Processes E_ORDER_KICK messages to the state machine upon completion of transmission.

    hashtag
    MY_APP_CHILD::transmit_complete()

    Defines the state name.

    hashtag
    MY_APP_CHILD::shtc3_???()

    This is an example of sensor acquisition implementation for SHTC3. For details on sending commands, etc., refer to the SHTC3 datasheet.

    hashtag
    MY_APP_CHILD::ltr308als_???()

    This is an example of LTR308ALS sensor acquisition implementation. Please refer to the LTR308ALS datasheet for details on sending commands, etc.

    WireWriteAndGet() sends 1 byte of cmd to the device of addr, then receives 1 byte and returns the value.

    hashtag
    MY_APP_CHILD::STATE_IDLE (0)

    State 0 has a special meaning. It is the state immediately after startup or after returning from sleep.

    Immediately after startup PEV_is_coldboot(ev,evarg) judgment becomes true and is called. Since it goes straight to sleep from on_begin(), it does not contain any code that transitions the state. **At this point, the major initialization has not yet been completed, so complex processing such as sending wireless packets cannot be performed. **In order to perform the first state transition for such processing, send an event from on_begin() and perform the state transition according to that event.

    After returning from sleep, there is a first call to PEV_is_warmboot(ev,evarg) which will be true. A call to PEV_SetState() will transition to the STATE_SENSOR state.

    hashtag
    MY_APP_CHILD::STATE_SENSOR

    When the transition is made from STATE_IDLE after returning from sleep, the state handler for STATE_SENSOR is called continuously. The event ev at this time is E_EVENT_NEW_STATE.

    Here, the operation of two sensors, SHTC3 and LTR308ALS, is started. After a certain period of time, the sensors will be ready to acquire data. This time wait is done with the sleep setting of 66ms. Note that PEV_KeepStateOnWakeup() is called before sleep. After this call, the state after returning from sleep is not STATE_IDLE, but the state it was in when it went to sleep, i.e. STATE_SENSOR.

    When returning from a short sleep, a call is first made with the PEV_is_warmboot(ev,evarg) decision set to true. At the time of this call, wireless packets can be sent, etc. Transition to STATE_TX.

    hashtag
    MY_APP_CHILD::STATE_TX

    Here, at the time of the E_EVENT_NEW_STATE event, the sensor data reading and wireless packet transmission procedures are started. Please refer to other act sample examples for details of the transmission procedure.

    Unlike the ACT description in the loop, the process of waiting for the message by PEV_Process() from transmit_complete() is used as a confirmation of completion. Sleep is performed upon receipt of the message. Sleep processing is done by transitioning to STATE_SLEEP.

    Finally, timeout processing is performed. This is in case the completion message of the sent packet has not been returned. PEV_u32Elaspsed_ms() returns the elapsed time since the transition to that state in milliseconds [ms]. If the time has elapsed, the above will perform a system reset the_twelite.reset_system() (assuming this timeout is too much).

    hashtag
    MY_APP_CHILD::STATE_SLEEP

    Perfom sleep. Describe it in E_EVENT_NEW_STATE immediately after the transition from the previous state. Since other events may be called just before sleep, be sure to execute the_twelite.sleep() in a decision expression that is executed only once.

    PAL AMBIENT SENSE PALarrow-up-right
    Wire
    board behavior

    Slp_Wk_and_Tx

    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.

    circle-check

    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

    hashtag
    ACT features.

    • After starting up, the system goes through an initialization process and then goes to sleep.

      1. setup()Initialise

      2. begin()

    hashtag
    Description of the ACT

    hashtag
    Declarations

    hashtag
    Includes

    To send packets, <NWK_SIMPLE> is included. Also, basic definitions such as application ID are in "Common.h".

    hashtag
    State definition.

    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 (state transition) using the STATE state enumeration.

    The step declared here contains functions for managing state, timeouts and waiting for processing.

    hashtag
    Sensor data.

    In this sample we do not process the sensor data, but we prepare dummy data for explanation.

    hashtag
    setup()

    Initializes variables and class objects.

    • Initialization of the step state machine

    • Initialization of the_twelite class object

    • Register and initialize the network <NWK_SIMPLE>

    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.

    hashtag
    begin()

    Called only once, immediately after setup(). The SleepNow() function is called to perform the first sleep procedure.

    hashtag
    wakeup()

    Called immediately after waking up. Here, it initializes the sensor data area and outputs a message on waking.

    hashtag
    loop()

    The above code is a simplified version of the actual code.

    This control structure uses the . 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.

    hashtag
    STATE::INIT

    Initialises the sensor values of the dummies. One is determined randomly by an add counter and one by a counter stop value.

    hashtag
    STATE::WORK_JOB

    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 .

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

    circle-exclamation

    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.

    circle-info

    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.

    hashtag
    STATE::WAIT_TX

    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.

    hashtag
    STATE::EXIT_NORMAL

    Call SleepNow() to start the sleep process.

    hashtag
    STATE::EXIT_FATAL

    As a critical error, a system reset is performed.

    hashtag
    SleepNow()

    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().

    hashtag
    Transmit()

    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. specifies the destination of the packet. The tx_addr specifies the destination of the packet, the specifies the number of retransmissions, and the specifies the transmission delay.

    The payload of a packet is an array of derivatives obtained by pkt.get_payload(). You can set the value of this array directly, but here we use to set the value.

    circle-info

    The maximum length of the payload is 91 bytes in the above example, see 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.

    circle-info

    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

    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.

    circle-info

    `` 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()).

    hashtag
    on_tx_comp()

    This is a system event that is called when the transmission is complete. Here, it is set to complete by .set_flag().

    mwx::stream

    input-output stream

    Upper-level class that handles input/output streams.

    • Interfaces to several classes (Serial, Wire, SPI, smplbuf) by polymorphism using CRTP (Curiously Recurring Template Pattern) method.

      • In CRTP, lower classes are defined as template class Derived : public stream<Derived>; and methods of lower classes are referenced from upper classes.

    • This class defines common processing such as print method, << operator, etc., and calls write() method, etc. implemented in lower classes, which is similar to using virtual functions.

    hashtag
    Interfaces (implemented in lower classes)

    Lower classes implement the functions listed below.

    hashtag
    available()

    Returns 1 if the input exists, 0 if it does not.

    Parameters
    Description
    circle-exclamation

    The return value of this implementation is not the buffer length.

    hashtag

    hashtag
    flush()

    Flush output (wait for output to complete).

    hashtag
    read()

    Inputs 1-byte data from the stream. If the data does not exist, return -1.

    hashtag
    write()

    Outputs 1 byte to the stream.

    Parameters
    Description

    hashtag
    vOutput()

    This is a static function that produces a single byte output. Since this is not a class method, member variables and other information are not available. Instead, a pointer to the class instance is passed to vp, which is passed as a parameter.

    This static function is used internally and the function pointer is passed as a one-byte output function of fctprintf(). This is used to implement the print method, etc.

    Parameters
    Description

    hashtag
    Interface

    hashtag
    putchar()

    Output a single byte.

    hashtag
    print(), println()

    Performs various types of formatted output.

    Paramita
    Explanation

    hashtag
    printfmt()

    Prints output in printf format.

    TWESDK/TWENET/current/src/printf/README.md 参照

    hashtag
    operator <<

    Argument type
    Explanation
    circle-info

    When outputting as a byte string, cast to uint8_t, uint16_t, uint32_t type. When outputting numerical values as strings, explicitly cast to int type.

    circle-info

    Single-byte types are handled differently depending on the type name. Usually, use the size-conscious uint8_t[S] type.

    hashtag
    set_timeout(), get_error_status(), clear_error_status()

    Manages input timeouts and errors using the >> operator.

    The timeout period is specified by set_timeout(), and input is processed using the >> operator. If no input is received within the given timeout period, the error value can be read out with get_error_status(). The error status is cleared by clear_error_status().

    Argument type
    Description

    hashtag
    Error Value

    Value
    Meaning

    hashtag
    operator >>

    Input processing.

    triangle-exclamation
    • Cannot be executed within setup().

    • Because it waits for polling, depending on the timeout time setting (e.g. no timeout), the watchdog timer may be triggered and reset.

    circle-info

    Normally, the following readout is performed during loop().

    The following is a list of types that can be read and stored.

    Argument type
    Explanation

    PAL_AMB

    Environmental Sensor Pal AMBIENT SENSE PALarrow-up-right is used to acquire sensor values.

    circle-check

    This ACT includes

    • Sending and receiving wireless packets

    • Configuring settings via Interactive settings mode -

    • State transition control by state machine -

    • Board operation with board behavior

    hashtag
    ACT FEATURES.

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

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

    hashtag
    how to use act

    hashtag
    Required TWELITE

    Role
    Example

    hashtag
    Explanation of ACT

    hashtag
    Include

    Environment sensor pal ) include board behavior.

    hashtag
    setup()

    The first step is to initialize variables, etc. Here we are initializing the state machine step.

    First, the board support 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 .

    hashtag
    loop()

    The loop() is controlled by the step 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.

    hashtag
    case STATE::INTERACTIVE:

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

    hashtag
    case STATE::INIT:

    Start sensor data acquisition.

    hashtag
    case STATE::SENSOR:

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

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

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

    hashtag
    case STATE::TX_WAIT_COMP:

    This section determines timeouts and transmission completion events.

    hashtag
    STATE::GO_SLEEP:

    Processes sleepNow().

    hashtag
    on_tx_comp()

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

    hashtag
    sleepNow()

    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.

    circle-info

    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.

    triangle-exclamation

    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.

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

    hashtag
    Applications

    hashtag
    Reduction of energy consumption

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

    PAL_MOT-single

    This ACT acquires several samples of acceleration data after sleep recovery and sends that data.

    circle-check

    The ACT of the "Act" includes the following

    // 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>();
    }
    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;
        }
    }
    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);
    }
    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;
    }
    void _begin() {
        // sleep immediately.
        Serial << "..go into first sleep (1000ms)" << mwx::flush;
        the_twelite.sleep(1000);
    }
    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
    }
    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
    }
    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;
    MWX_APIRET MY_APP_CHILD::shtc3_start()
    MWX_APIRET MY_APP_CHILD::shtc3_read()
    MWX_APIRET MY_APP_CHILD::ltr308als_read()
    MWX_APIRET MY_APP_CHILD::ltr308als_start()
    static MWX_APIRET WireWriteAngGet(uint8_t addr, uint8_t cmd)
    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);
    	}
    }
    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);
    	}
    }
    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()) {
    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
    }
    
        // ↓ ↓ ↓ Send a message
    
    } else if (ev == E_ORDER_KICK && evarg == uint32_t(u8txid)) {
    		Serial << "[STATE_TX] SUCCESS TX(" << int(evarg) << ')' << mwx::crlf;
    		PEV_SetState(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();
    	}
    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
    	}
    }
    MONOSTICK BLUE or REDarrow-up-right
    BLUE PAL or RED PALarrow-up-right
    AMBIENT SENSE PALarrow-up-right
    Run sleep
  • After waking up from sleep, the state variables are initialized and the actions are performed in the following order

    1. wakeup() wakes up from sleep, performs each initialization

    2. 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)

    3. loop()/TX state: Make a request to send

    4. loop()/WAIT_TX state: Waiting for transmission completion

    5. loop()/EXIT_NORMAL state: Run sleep (back to 1.)

  • loop()/EXIT_FATAL state :Resetting the module if an error occurs

  • (register
    DEVICE_ID
    ).
    The same applies to data of type uint16_t.
    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.

    SM_SIMPLE state machine
    SM_SIMPLE state machine
    tx_addr
    tx_retry
    tx_packet_delay
    smblbuf<uint8_t>
    pack_bytes()
    NWK_SIMPLE packet structure and maximum length
    MWX_APIRET
    .get_humid_dmil() : int16_t : humidity at 1% as 100 (5680 for 56.8%)
  • .get_temp() : float : float value (56.8 for 56.8%)

  • Parent Node

    MONOSTICK BLUE or REDarrow-up-right ACT Parent_MONOSTICK in action.

    Child Node

    BLUE PAL or RED PALarrow-up-right +Environmental Sensor Pal AMBIENT SENSE PALarrow-up-right

    <STG_STD>
    <SM_SIMPLE>
    <PAL_AMB>
    <PAL_AMB>
    <PAL_AMB>
    startSensorCapture()
    SMSMSIMPLE_SIMPLE state machine
    PAL_AMB-UseNap
    Sending and receiving wireless packets
  • Configuration through Interactive settings mode - <STG_STD>

  • State transition control by state machine - <SM_SIMPLE>

  • <PAL_MOT> or <CUE> board operation with board BEHAVIOR

  • hashtag
    Explanation of ACT

    Wake up → start acquiring accelerometer data → wait for accelerometer FIFO interrupt → retrieve accelerometer data → wireless transmission → sleep.

    circle-info

    The accelerometer stops adding data to the FIFO queue when the FIFO queue is full.

    hashtag
    宣言部

    hashtag
    インクルード

    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>arrow-up-right is included.

    hashtag
    state-defined

    Define states for sequential processing during loop() and also state machinestep is declared.

    hashtag
    Sensor data storage

    Data structure for storing sensor data.

    hashtag
    setup()

    Registers board, configuration, and network behavior objects.

    hashtag
    Interactive settings mode

    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.

    hashtag
    the_twelite

    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.

    circle-info

    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.

    hashtag
    NWK_SIMPLE Object

    Settings are also made for the network behavior object. The logical device ID (LID) and retransmission settings in Interactive settings mode are reflected.

    hashtag
    Other hardware initialization, etc.

    LED blink settings and other settings.

    hashtag
    begin()

    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.

    hashtag
    wakeup()

    After waking up, the state variable eState is set to the initial state INIT. After this, loop() is executed.

    hashtag
    loop()

    The basic structure of loop() is <SM_STATE> state machine state with switch ... . case clause. The initial state is STATE::INIT or STATE::INTERACTIVE.

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

    hashtag
    STATE::INIT

    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.

    hashtag
    STATE::CAPTURE

    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.

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

    circle-info

    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.

    hashtag
    STATE::REQUEST_TX

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

    hashtag
    E_SATE::WAIT_TX

    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.

    hashtag
    E_SATE::EXIT_NORMAL, FATAL

    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.

    hashtag
    MWX_APIRET TxReq()

    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.

    hashtag
    sleepNow()

    Sleep procedure.

    • Serial ports should call Serial.flush() to output all before sleep.

    • The <SM_SIMPLE> state machine must do on_sleep().

    #include <TWELITE>
    #include <NWK_SIMPLE>
    #include <SM_SIMPLE>
    
    #include "Common.h"
    enum class STATE {
        INIT = 0,    // INIT STATE
        WORK_JOB,    // do some job (e.g sensor capture)
        TX,          // reuest transmit
        WAIT_TX,     // wait its completion
        EXIT_NORMAL, // normal exiting.
        EXIT_FATAL   // has a fatal error (will do system reset)
    };
    SM_SIMPLE<STATE> step;
    struct {
    	uint16_t dummy_work_ct_now;
    	uint16_t dummy_work_ct_max;  // counter for dummy work job. 
    } sensor;
    void setup() {
    	/*** SETUP section */
    	step.setup(); // init state machine
    	
    	// the twelite main class
    	the_twelite
    		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
    		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
    		<< TWENET::rx_when_idle(false);  // open receive circuit (if not set, it can't listen packts from others)
    
    	// Register Network
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    	nwk	<< NWK_SIMPLE::logical_id(DEVICE_ID); // set Logical ID. 
    
    	/*** BEGIN section */
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial << "--- Sleep an Tx Act ---" << crlf;
    }
    the_twelite.begin(); // start twelite!
    void begin() {
    	Serial << "..begin (run once at boot)" << crlf;
    	SleepNow();
    }
    void wakeup() {
     memset(&sensor, 0, sizeof(sensor));
    	Serial << crlf << int(millis()) << ":wake up!" << crlf;
    }
    void loop() {
    	do {
    		switch(step.state()) {
    		case STATE::INIT:
    			sensor.dummy_work_ct_now = 0;
    			sensor.dummy_work_ct_max = random(10,1000);
    			step.next(STATE::WORK_JOB);
    		break;
    
    		...
    		}
    	} while (step.b_more_loop());
    }
    sensor.dummy_work_ct_now = 0;
    sensor.dummy_work_ct_max = random(10,1000);
    
    step.next(STATE::WORK_JOB);
    if (TickTimer.available()) {
    	Serial << '.';
    	sensor.dummy_work_ct_now++;
    	if (sensor.dummy_work_ct_now >= sensor.dummy_work_ct_max) {
    		Serial << crlf;
    		step.next(STATE::TX);
    	}
    }
    if (Transmit()) {
    	Serial << int(millis()) << ":tx request success!" << crlf;
    	step.set_timeout(100);
    	step.clear_flag();
    	step.next(STATE::WAIT_TX);
    } else {
    	// normall it should not be here.
    	Serial << int(millis()) << "!FATAL: tx request failed." << crlf;
    	step.next(STATE::EXIT_FATAL);
    }
    if (step.is_flag_ready()) {
    	Serial << int(millis()) << ":tx completed!" << crlf;
    	step.next(STATE::EXIT_NORMAL);
    } else if (step.is_timeout()) {
    	Serial << int(millis()) << "!FATAL: tx timeout." << crlf;
    	step.next(STATE::EXIT_FATAL);
    }
    SleepNow();
    Serial << crlf << "!FATAL: RESET THE SYSTEM.";
    delay(1000); // wait a while.
    the_twelite.reset_system();
    void SleepNow() {
    	uint16_t u16dur = SLEEP_DUR;
    	u16dur = random(SLEEP_DUR - SLEEP_DUR_TERMOR, SLEEP_DUR + SLEEP_DUR_TERMOR);
    
    	Serial << int(millis()) << ":sleeping for " << int(u16dur) << "ms" << crlf;
    	Serial.flush();
    
    	step.on_sleep(); // reset status of statemachine to INIT state.
    
    	the_twelite.sleep(u16dur, false);
    }
    MWX_APIRET vTransmit() {
    	Serial << int(millis()) << ":vTransmit()" << crlf;
    
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    		// set tx packet behavior
    		pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    			<< tx_retry(0x1) // set retry (0x3 send four times in total)
    			<< tx_packet_delay(0,0,2); // send packet w/ delay (send first packet with randomized delay from 0 to 0ms, repeat every 2ms)
    
    		// prepare packet payload
    		pack_bytes(pkt.get_payload() // set payload data objects.
    			, make_pair(FOURCC, 4) // string should be paired with length explicitly.
    			, uint32_t(millis()) // put timestamp here.
    			, uint16_t(sensor.dummy_work_ct_now) // put dummy sensor information.
    		);
    		
    		// do transmit 
    		//return nwksmpl.transmit(pkt);
    		return pkt.transmit(); 
    	}
    
    	return MWX_APIRET(false, 0);
    }
    if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    pkt << tx_addr(0x00) // Destination
    		<< tx_retry(0x1) // Number of resends
    		<< tx_packet_delay(0,0,2); // Transmission delay
    pack_bytes(pkt.get_payload() // set payload data objects.
    	, make_pair(FOURCC, 4) // string should be paired with length explicitly.
    	, uint32_t(millis()) // put timestamp here.
    	, uint16_t(sensor.dummy_work_ct_now) // put dummy sensor information.
    );	
    auto&& pay = pkt.get_payload(); // get buffer object.
    
    // the following code will write data directly to internal buffer of `pay' object.
    uint8_t *p = pay.begin(); // get the pointer of buffer head.
    
    S_OCTET(p, FOURCC[0]); // store byte at pointer `p' and increment the pointer.
    S_OCTET(p, FOURCC[1]);
    S_OCTET(p, FOURCC[2]);
    S_OCTET(p, FOURCC[3]);
    
    S_DWORD(p, millis()); // store uint32_t data.
    S_WORD(p, sensor.dummy_work_ct_now); // store uint16_t data.
    
    pay.redim(p - pay.begin());
    return pkt.transmit(); 
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	step.set_flag(ev.bStatus);
    }
    #include <TWELITE>
    #include <NWK_SIMPLE>// network support
    #include <PAL_AMB> // PAL_AMB
    #include <STG_STD> // Interactive settings mode
    #include <SM_SIMPLE> // Simple State Machine
    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;
    }
    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);
    
    // 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)
    	brd.set_led(LED_TIMER::BLINK, 10); // blink (on 10ms/ off 10ms)
    	the_twelite
    		<< TWENET::appid(APP_ID)     // set application ID (identify network group)
    		<< TWENET::channel(CHANNEL); // set channel (pysical channel)
    Wire.begin(); // start two wire serial bus.
    startSensorCapture();
    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());
    }	
    brd.sns_SHTC3.begin();
    brd.sns_LTR308ALS.begin();
    
    step.next(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);
    	}
    // 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);
    }
    	pkt << tx_addr(0x00)  // Parent Node 0x00
    		<< tx_retry(0x1)    // 1 retry
    		<< tx_packet_delay(0, 0, 2); // minimul delay
    pack_bytes(pkt.get_payload() 
    	, make_pair(FOURCHARS, 4)  
    	, uint32_t(sensor.u32luminance)
    	, uint16_t(sensor.i16temp)
    	, uint16_t(sensor.i16humid)
    );
    // 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);
    }
    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);
    }
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	step.set_flag(ev.bStatus);
    }
    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);
    }
    void wakeup() {
    	Serial	<< mwx::crlf
    			<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
    			<< mwx::crlf
    			<< "..start sensor capture again."
    			<< mwx::crlf;
    the_twelite << set;// Interactive settings mode
    the_twelite << twenet::channel(19); // set ch overwrite to 19
    #include <TWELITE> // MWX library basic
    #include <NWK_SIMPLE> // network
    #include <SM_SIMPLE> // State machine (state transition)
    #include <STG_STD> // Interactive settings mode
    
    /*** board selection (choose one) */
    #define USE_PAL_MOT
    //#define USE_CUE
    // board dependend definitions.
    #if defined(USE_PAL_MOT)
    #define BRDN PAL_MOT
    #define BRDC <PAL_MOT>
    #elif defined(USE_CUE)
    #define BRDN CUE
    #define BRDC <CUE>
    #endif
    // include board support
    #include BRDC
    enum class E_STATE : uint8_t {
    	INTERACTIVE = 255,
    	INIT = 0,
    	START_CAPTURE,
    	WAIT_CAPTURE,
    	REQUEST_TX,
    	WAIT_TX,
    	EXIT_NORMAL,
    	EXIT_FATAL
    };
    SM_SIMPLE<E_STATE> step;
    struct {
    	int32_t x_ave, y_ave, z_ave;
    	int32_t x_min, y_min, z_min;
    	int32_t x_max, y_max, z_max;
    	uint16_t n_seq;
    	uint8_t n_samples;
    } sensor;
    /// load board and settings objects
    auto&& brd = the_twelite.board.use BRDC (); // load board support
    auto&& set = the_twelite.settings.use<STG_STD>(); // load save/load settings(interactive mode) support
    auto&& nwk = the_twelite.network.use<NWK_SIMPLE>(); // load network support
    // settings: configure items
    set << SETTINGS::appname("MOT");
    set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
    set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
    set << SETTINGS::lid_default(0x1); // set default LID
    set.hide_items(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=LOW is detected, start with intaractive mode.
    if (digitalRead(brd.PIN_SET) == PIN_STATE::LOW) {
    	set << SETTINGS::open_at_start();
    	brd.set_led(LED_TIMER::BLINK, 300); // slower blink
    	step.next(STATE::INTERACTIVE);
    	return;
    }
    
    // load settings
    set.reload(); // load from EEPROM.
    OPT_BITS = set.u32opt1(); // this value is not used in this example.
    the_twelite << set;
    nwk << set;
    brd.set_led(LED_TIMER::BLINK, 100);
    void begin() { 
    	auto&& set = the_twelite.settings.use<STG_STD>();
    	if (!set.is_screen_opened()) {
    		// sleep immediately, waiting for the first capture.
    		sleepNow();
    	}
    }
    void wakeup() {
    	Serial << crlf << "--- PAL_MOT(OneShot):" 
    	       << FOURCHARS << " wake up ---" << crlf;
    	eState = E_STATE::INIT;
    }
    void loop() {
    	auto&& brd = the_twelite.board.use<PAL_MOT>();
    
    	do {
    		switch(step.state()) {
    			case STATE::INTERACTIVE:
    			break;
    		...
    	} while(step.b_more_loop());
    }
    case STATE::INIT:
    	brd.sns_MC3630.get_que().clear(); // clear queue in advance (just in case).
    	memset(&sensor, 0, sizeof(sensor)); // clear sensor data
    	step.next(STATE::START_CAPTURE);
    break;
    case STATE::START_CAPTURE:
    	brd.sns_MC3630.begin(
    		// 400Hz, +/-4G range, get four samples and will average them.
    		SnsMC3630::Settings(
    			SnsMC3630::MODE_LP_400HZ, SnsMC3630::RANGE_PLUS_MINUS_4G, N_SAMPLES)); 
    
    	step.set_timeout(100);
    	step.next(STATE::WAIT_CAPTURE);
    break;
    case STATE::WAIT_CAPTURE:
    	if (brd.sns_MC3630.available()) {
    		brd.sns_MC3630.end(); // stop now!
    sensor.n_samples = brd.sns_MC3630.get_que().size();
    if (sensor.n_samples) sensor.n_seq = brd.sns_MC3630.get_que()[0].get_t();
    ...
    // get all samples and average them.
    for (auto&& v: brd.sns_MC3630.get_que()) {
    	sensor.x_ave  += v.x;
    	sensor.y_ave  += v.y;
    	sensor.z_ave  += v.z;
    }
    
    if (sensor.n_samples == N_SAMPLES) {
    	// if N_SAMPLES == 2^n, division is much faster.
    	sensor.x_ave /= N_SAMPLES;
    	sensor.y_ave /= N_SAMPLES;
    	sensor.z_ave /= N_SAMPLES;
    }
    ...
    // can also be:
    //	int32_t x_max = -999999, x_min = 999999;
    //	for (auto&& v: brd.sns_MC3630.get_que()) {
    //		if (v.x >= x_max) x_max = v.x;
    //		if (v.y <= x_min) x_min = v.x;
    //		...
    //	}	
    auto&& x_minmax = std::minmax_element(
    	get_axis_x_iter(brd.sns_MC3630.get_que().begin()),
    	get_axis_x_iter(brd.sns_MC3630.get_que().end()));
    sensor.x_min = *x_minmax.first;
    sensor.x_max = *x_minmax.second;
    ...
    if (brd.sns_MC3630.available()) {
      ...
      brd.sns_MC3630.get_que().clear(); // clean up the queue
      step.next(STATE::REQUEST_TX); // next state
    } else if (step.is_timeout()) {
      Serial << crlf << "!!!FATAL: SENSOR CAPTURE TIMEOUT.";
      step.next(STATE::EXIT_FATAL);
    }
    break;
    case STATE::REQUEST_TX:
    	if (TxReq()) {
    		step.set_timeout(100);
    		step.clear_flag();
    		step.next(STATE::WAIT_TX);
    	} else {
    		Serial << crlf << "!!!FATAL: TX REQUEST FAILS.";
    		step.next(STATE::EXIT_FATAL);
    	}
    break;
    case STATE::WAIT_TX:
    	if (step.is_flag_ready()) {
    		step.next(STATE::EXIT_NORMAL);
    	}
    	if (step.is_timeout()) {
    		Serial << crlf << "!!!FATAL: TX TIMEOUT.";
    		step.next(STATE::EXIT_FATAL);
    	}
    break;
    case STATE::EXIT_NORMAL:
    	sleepNow();
    break;
    
    case STATE::EXIT_FATAL:
    	Serial << flush;
    	the_twelite.reset_system();
    break;
    MWX_APIRET TxReq() {
    	auto&& brd = the_twelite.board.use<PAL_MOT>();
    	MWX_APIRET ret = false;
    
    	// prepare tx packet
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {		
    		// set tx packet behavior
    		pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    			<< tx_retry(0x1) // set retry (0x1 send two times in total)
    			<< tx_packet_delay(0, 0, 2); // send packet w/ delay
    		
    		// prepare packet (first)
    		pack_bytes(pkt.get_payload() // set payload data objects.
    				, make_pair(FOURCHARS, 4)  // just to see packet identification, you can design in any.
    				, uint16_t(sensor.n_seq)
    				, uint8_t(sensor.n_samples)
    				, uint16_t(sensor.x_ave)
    				, uint16_t(sensor.y_ave)
    				, uint16_t(sensor.z_ave)
    				, uint16_t(sensor.x_min)
    				, uint16_t(sensor.y_min)
    				, uint16_t(sensor.z_min)
    				, uint16_t(sensor.x_max)
    				, uint16_t(sensor.y_max)
    				, uint16_t(sensor.z_max)
    			);
    
    		// perform transmit
    		ret = pkt.transmit();
    
    		if (ret) {
    			Serial << "..txreq(" << int(ret.get_value()) << ')';
    		}
    	}
    
    	return ret;
    }
    void sleepNow() {
    	Serial << crlf << "..sleeping now.." << crlf;
    	Serial.flush();
    	step.on_sleep(false); // reset state machine.
    	the_twelite.sleep(3000, false); // set longer sleep (PAL must wakeup less than 60sec.)
    }

    uint32_t

    output 4 bytes (big-endian order)

    const char* uint8_t* const char[S]

    Output up to the terminating character. Output does not include the terminating character. >(S specifies the size of the fixed array)

    uint8_t[S]

    Output the array size S bytes as is. (S is the fixed array size specification)

    format()

    output in printf format

    mwx::crlf

    output of newline CRLF

    mwx::flush

    flush output

    bigendian()

    output numeric types in big-endian order. (right-hand side value)

    std::pair<T*, T*>

    A pair containing begin(), end() pointers of byte type. Can be created by make_pair. Tis assumed to be of typeuint8_t`. (right-hand side value)

    output byte string using bytelist()

    std::initializer_list.

    smplbuf<uint8_t,AL>&

    Output the contents of an array class of type uint8_t. ALC is .

    smplbuf<uint8_t, AL>::to_stream()

    Outputs data of smplbuf&#x3C;T> T is of type uint8_t, AL is a .

    Return value int

    0: no data 1: data present

    n

    The character you want to output.

    Return value size_t

    1 if output succeeds, 0 if it fails.

    out

    the character to output

    vp

    pointer to a class instance Usually, cast to the original class and call the write() method

    val

    Integer powerups

    base

    power form BIN binary / OCT 8 math / DEC 10 math / HEX 16 math

    place

    Number of decimals below the decimal point

    back size_t

    the number of booklets

    char

    1-byte output (not formatted as a number)

    1-byte output (not formatted as a number)

    double

    numeric output (printf's "%.2f")

    uint8_t

    output 1 byte (same as char type)

    uint16_t

    output 2 bytes (big-endian order)

    centisec

    Sets the timeout period in units of 1/10 second. If 0xff is specified, timeout is disabled.

    0

    No Error

    1

    Error Status

    1-byte input (big-endian order)

    2-byte input (big-endian order)

    uint32_t

    4-byte input (big-endian order)

    uint8_t[S]

    input for S bytes (S specifies fixed array size)

    null_stream(int n)

    read n bytes away

    Building ACT

    Building ACT

    The application program written in the MWX library is called ACT. The first step is to build and write it.

    • About the build folder structure

    • About the build script

    • About building with Visual Studio Code (Described as VSCode)

    circle-info

    This page describes several methods of building, all of which ultimately involve running the make command. Please refer to the for details.

    triangle-exclamation

    Depending on the OS environment, security warnings may appear when running each executable program (e.g. make, build toolchain like gcc). It is necessary to configure the settings to suppress the warning. (Please consult with yourself or your system administrator to determine if you wish to run the program with the warning suppressed.)

    hashtag
    About the build folder structure

    Open the folder MWSDK_ROOT (e.g. C:\MWSDK) where you have installed the MWSDK. It has the following structure

    Tool act files such as compilers are stored under Act_samples. (Some of them are omitted below)

    These acts are simple examples to help you write your own MWX library, but many of them have the following features

    • Obtaining sensor values

    • After obtaining the sensor value, send a wireless packet to the master

    • Sleeps for a period of time (or waits for an interrupt) after transmission is complete

    The Parent-MONOSTICK act is used to receive and display packets. This act for the parent is output in ASCII format. (:00112233AABBCC.... .FF[CR][LF], and the middle part is a hexadecimal byte expressed by two ASCII characters. The trailing ? is also a two-character byte, but it becomes a checksum byte called LRC. Reference: ASCII format)

    When trying to get it to work in practice, try the following combinations:

    Parents
    Child
    Remark

    Now let's take a look inside the PingPong folder from within ACT.

    circle-info

    You can also build other acts in Act_samples. In this case, the folder and file names should be read differently.

    You must have a .cpp file with the same name as the folder directly under the folder.

    circle-info

    If it is a small act, you can write it in this .cpp file. If you have a larger act, you can build it in multiple files by referring to the .

    circle-exclamation

    The ACT file PingPong.cpp is located directly under the PingPong folder. If you change the name of the folder, make sure to rename the .cpp file to the same name as the folder.

    Next, open the build folder.

    It contains the scripts and Makefiles needed for the build.

    Build by running make TWELITE={BLUE or RED} in the folder containing this Makefile. Building with VSCode is the same, calling make internally.

    hashtag
    Building with TWELITE STAGE App. (Normal)

    The TWELITE STAGE app can be used to build, write, and run. This section describes the TWELITE STAGE application from startup to build.

    hashtag
    0. TWELITE Connection

    Connect MONOSTICK or TWELITE R to your USB port.

    triangle-exclamation

    TWELITE is a sensitive electronic component and should be handled with care. Typical precautions are listed below.

    Especially when TWELITE R is used, the electronic board is often in direct contact with the outside without a case, which may cause unintended shorts, noise, or other problems that prevent the USB device from operating properly.

    In this case, closing the application and unplugging and plugging in the USB device will usually restore it. In the worst case, the USB device may be damaged or the PC may be corrupted.

    hashtag
    1. Launch the TWELITE STAGE application

    Launch the executable TWELITE_Stage.{extension} located in the {TWELITE SDK installation} folder. (reference: )。

    Below is an example of the screen while the TWELITE STAGE application is running. The main screen on the left and the command prompt screen are shown, but the main screen is usually used. The command prompt screen is not normally used, but it displays various information and input data from the TWELITE microcontroller serial port.

    The main operations on the main screen are as follows

    • Left mouse click (selection)

    • Double right mouse click (return to previous screen)

    • Quickly press ESC twice, or ESC once on some screens (return to previous screen)

    (Reference: )

    hashtag
    2. Serial port selection

    This is the first screen that appears when you start the TWELITE STAGE application. If TWELITE R or MONOSTICK is connected in advance, it will be listed on this screen. Select the TWELITE you wish to operate on this screen. It is also possible to select the TWELITE without selecting it on this screen.

    (Reference: )

    hashtag
    3. Main Menu

    After exiting the serial port selection screen, the main menu appears. Select the "Application Rewrite" menu for build and write.

    (Reference: )

    hashtag
    4. Wrt Firmware (Application programming) menu

    Before selecting the application programming menu, please check the TWELITE connection and serial port selection. The serial port selection status can be checked on the help screen that appears by holding down the Alt(Cmd) key.

    There are several categories of projects that can be referenced from the TWELITE STAGE application. HELP on the right side displays related information in a browser. Foldr displays the folder where the project is located.

    If TWELITE is already connected, the TWELITE model is determined when the menu is selected. (Inside the TWELITE STAGE application, the build is performed according to the TWELITE model that has been determined.)

    circle-exclamation

    If an error occurs here, return to the main menu from this screen and reselect the menu. If necessary, deselect the serial port by pressing Alt(Cmd) + 0 on the TWELITE STAGE application and check the various connections, including the USB connection. Some USB connection errors may not be resolved until you reboot your computer.

    (Reference: )

    hashtag
    4. Project Selection

    Here, select "Act Build & Wrt" from the " Wrt Firmware" menu.

    Project names, such as sample acts, are listed. The HELP on the right side displays the related information in a browser. The foldr displays the folder where the project is located.

    Reference: )

    hashtag
    5. Build & Wrt(Programming)

    Here, select BRD_APPTWELITE in the project selection screen shown earlier.

    Once selected, writing will be performed as shown in the following screen example. If an error is displayed, follow the on-screen instructions or return to the previous screen and try again.

    (Reference: )

    hashtag
    6. Go to Interactive settings mode

    When the programming is successfully completed, it will continue to (settings screen). However, the screen will not be displayed unless the firmware supports Interactive settings mode.

    In Interactive settings mode, you can configure various settings, including TWELITE's wireless CHANNEL.

    (Reference: )

    hashtag
    7. Terminal screen

    Return to the root menu and select Viewer > Terminal.

    This is a very simple terminal where you can check messages from TWELITE and input data into TWELITE.

    The screen displays a message when a wireless transmission is made approximately every second. You can also transition to the Interactive settings mode screen by typing + + +.

    (Reference: )

    hashtag
    About building with VSCode (Optional)

    VSCode is a powerful editor for source editing, but it is also possible to build firmware for TWELITE microcontrollers on VSCode.

    VSCode is started from the TWELITE STAGE application from the project listing in Build&Write menu. (Note: The settings is required at TWELITE STAGE App [Setting Menu] > Wrt Firmware > Open a folder with VSCode. For simplicity of configuration, the executable TWELITE_Stage_VSCode.{extension} is available for Windows, Linux, and macOS.)

    Set "Open folder with code (VSCode)" to 1 in STAGE settings.

    Press [VSCode] on the right end of the list of builds.

    circle-exclamation

    If VSCode is already launched, the necessary settings by setting the system environment variables may not be reflected. In this case, exit VSCode and start VSCode again from the TWELITE STAGE application.

    Because of the use of system environment variables to reflect information, in principle, simultaneous operation of multiple TWELITE STAGES that reference different library folders may cause problems. If you open Terminal on VSCode and the environment variable MWSDK_ROOT is properly set, you can expect the build to be successful.

    hashtag
    Build with VSCode

    Firstly, open a workspace from TWELITE STAGE app that you want build. The attached workspace in TWELITE STAGE SDK has build task definitions for TWELITE microcontroller.

    circle-exclamation

    In the example below, a workspace is opened with an example screen of the English interface.

    Open a workspace and select [Terminal>Run Task...].

    Select the type of TWELITE radio module (BLUE/RED) and the act name to build. In the example below we have selected Build for TWELITE BLUE PingPong (for TWELITE BLUE/PingPong act). The build will start immediately after selection.

    The progress of the build is shown in the TERMINAL section at the bottom of the screen.

    circle-check

    If the build is successful, you will see a message about the creation of the .elf file, along with its size information (text data bss dec hex filename), as shown in the highlighted part of the screenshot above.

    Also, a BIN file (in the above example, PingPong_BLUE_???.bin) file should be created under the build folder. Please check it.

    circle-info

    The build definition adds a definition to convert folder names (e.g. /c/User/...) that do not conform to the Windows 10 file system (e.g. C:/User/...).

    The conversion is not complete, but you can extract the filename and line number of the error from the compilation message.

    The execution command in .vscode/tasks.json is sh -c "make ... | sed -E -e s#..."

    circle-exclamation

    If the build does not work, check the error messages first; it is often easy to identify the cause of an error from a message on a line containing the string error.。

    To be sure, clean (remove intermediate files in the objs_??? folder) and rerun the build to be sure. (All operations, including make clean, will fail if there are intermediate files left over from builds in other environments).

    hashtag
    Build on command line (Linux/macOS)

    Additional information about building in the command line environment.

    circle-exclamation

    A working knowledge of the command line (e.g. bash) is required.

    triangle-exclamation

    Depending on the OS environment, security warnings may appear when running each executable program. It is necessary to configure the settings to suppress the warning. (Please consult with yourself or your system administrator to determine if you wish to run the program with the warning suppressed.)

    hashtag
    Linux, macOS

    To build by command line, run make in a window where bash (or some other shell) is running. Make sure that the environment variable MWSDK_ROOT is set correctly beforehand. For example, if you install to /work/MWSTAGE/MWSDK, set ~/.profile as follows.

    Run make from the command line (bash). If make is not available, you need to install a package. (The following is an example for Ubuntu Linux)

    circle-info
    • On Linux environments, install the make or build-essential package.

    • In the macOS environment, install Command Line Tools in Xcode.

    hashtag
    Windows

    On Windows, run {MWSTAGE SDK install}/MWSDK/WIN_BASH.cmd. Environment variables and make utility are already set.

    hashtag
    Building

    A build should look like this:

    hashtag
    Example commands

    See the for more details.

    Example
    Remark

    hashtag
    About intermediate files

    When the build is done, the objs_??? folder is created and an intermediate file is created in it. This file is dependent on the environment in which it was compiled, so if any files from other environments are left, make will fail.

    circle-info

    Deleting the objs_??? folder'' may resolve the make error.

    int available()
    
    // example
    while(Serial.available()) {
      int c = Serial.read();
      // ... any
    }
    void flush()
    
    // example
    Serial.println("long long word .... ");
    Serial.flush();
    int read()
    
    // example
    int c;
    while (-1 != (c = read())) {
        // any
    }
    size_t write(int c)
    
    // example
    Serial.write(0x30);
    static void vOutput(char out, void* vp)
    void mwx::stream::putchar(char c)
    
    // example
    Serial.putchar('A');
    // result -> A
    size_t print(T val, int base = DEC) // T: 整数型
    size_t print(double val, int place = 2)
    size_t print(const char*str)
    size_t print(std::initializer_list<int>)
    
    // example
    Serial.print("the value is ");
    Serial.print(123, DEC);
    Serial.println(".");
    // result -> the value is 123.
    
    Serial.print(123.456, 1);
    // result -> 123.5
    
    Serial.print({ 0x12, 0x34, 0xab, 0xcd });
    // will output 4byte of 0x12 0x34 0xab 0xcd in binary.
    size_t printfmt(const char* format, ...);
    
    // example 
    Serial.printfmt("the value is %d.", 123);
    // result -> the value is 123.
    // examples
    Serial << "this value is" // const char*
           << int(123)
           << '.';
           << mwx::crlf;
    // result -> this value is 123.
    
    Serial << fromat("this value is %d.", 123) << twe::crlf;
    // result -> this value is 123.
    
    Serial << mwx::flush; // flush here
    
    Serial << bigendian(0x1234abcd);
    // will output 4byte of 0x12 0x34 0xab 0xcd in binary.
    
    Serial << int(0x30) // output 0x30=48, "48"
           << '/'
           << uint8_t(0x31); // output '1', not "48"
    // result -> 48/1
    
    smplbuf<char,16> buf = { 0x12, 0x34, 0xab, 0xcd };
    Serail << but.to_stream();
    // will output 4byte of 0x12 0x34 0xab 0xcd in binary.
    
    Seiral << make_pair(buf.begin(), buf.end());
    // will output 4byte of 0x12 0x34 0xab 0xcd in binary.
    
    Serial << bytelist({ 0x12, 0x34, 0xab, 0xcd });
    // will output 4byte of 0x12 0x34 0xab 0xcd in binary.
    uint8_t get_error_status()
    void clear_error_status()
    void set_timeout(uint8_t centisec)
    
    // example
    Serial.set_timeout(100); // 1000msのタイムアウトを設定
    uint8_t c;
    Serial >> c;
    inline D& operator >> (uint8_t& v)
    inline D& operator >> (char_t& v)
    template <int S> inline D& operator >> (uint8_t(&v)[S])
    inline D& operator >> (uint16_t& v)
    inline D& operator >> (uint32_t& v)
    inline D& operator >> (mwx::null_stream&& p)
    
    //// Example
    uint8_t c;
    
    the_twelite.stop_watchdog(); // stop watchdog
    Serial.set_timeout(0xFF); // no timeout
    
    // read out 1 byte
    Serial >> c;
    Serial << crlf << "char #1: [" << c << ']';
    
    // skipping
    Serial >> null_stream(3); // Read away 3 bytes
    Serial << crlf << "char #2-4: skipped";
    
    // Read 4 bytes (limited to fixed-length arrays of type uint8_t)
    uint8_t buff[4];
    Serial >> buff;
    Serial << crlf << "char #5-8: [" << buff << "]";
    void loop() {
      uint8_t c;
      while(Serial.available()) {
        Serial >> c;
        // Or c = Serial.read();
        
        switch(c) { ... }  // Branch processing according to the value of c
      }
    }
    memory allocation methodarrow-up-right
    memory allocation methodarrow-up-right
    Also, handle electronic circuit boards with care.
    • Circuit error.

      • Check the circuit again before turning on the power.

      • Be careful not to reverse-insert batteries or over-voltage.

    • Static electricity

      • Even a voltage that is not human-sensitive can cause a semiconductor failure. Even simple measures such as touching metal parts before working, wristbands, and special mats can have a reasonable effect.

    • Short-circuits caused by touching metal objects, etc.

      • Make sure that there are no metal objects near the electronic board. If clips or other objects are scattered around, this may cause a short circuit, or even a dangerous situation in which the battery heats up due to a large discharge.

    Hold down the Alt(Cmd) key (help screen)

  • Normal keyboard input (follow the screen)

  • to rewrite the string of the drive name equivalent in the output message.

    BRD_APPTWELITE

    BRD_APPTWELITE

    The parent device is started with the M1 pin low (GND level). In normal mode (always running), you can see it works like App_TweLite.

    PingPong

    PingPong

    The system works with two children. When one of them sends a ping packet, the other sends a pong packet back.

    Parent-MONOSTICK

    Other

    You can check the transmission of packets of the act for the child machine.

    make TWELITE=BLUE

    build for TWELITE BLUE

    make TWELITE=RED

    build for TWELITE RED

    make cleanall

    Delete intermediate files

    Makefile description
    Makefile description
    TWELITE STAGE Application Manual - Usagearrow-up-right
    TWELITE STAGE App Manual - Key and Mouse Operationsarrow-up-right
    TWELITE STAGE App Manualarrow-up-right
    TWELITE STAGE App Manualarrow-up-right
    TWELITE STAGE App Manualarrow-up-right
    TWELITE STAGE App Manual-Act Build & Wrtarrow-up-right
    TWELITE STAGE App Manual-build screenarrow-up-right
    Interactive settings modearrow-up-right
    TWELITE STAGE App Manual-Interactive settings modearrow-up-right
    TWELITE STAGE App Manual-Terminalarrow-up-right
    Makefile description
    Example of screen
    Serial port selection screen
    Main Menu
    Wrt Firmware (Application programming)
    Act Build & Wrt menu
    programming..
    complete
    Interactive settings mode
    Terminal
    Settings for VSCode
    Open as VSCode
    List of build tasks
    Selecting a build task
    Build progress
    MWSDK_ROOT
      |
      +-ChipLib      : Semiconductor library
      +-License      : Software License Agreement
      +-MkFiles      : makefile
      +-Tools        : A set of tools such as compilers
      +-TWENET       : TWENET/MWX library
      +-Act_samples  : Sample codes of ACT
      ...
    Act_samples
      |
      +-BRD_APPTWELITE    : ACT for boards with the same configuration as App_TweLite
      +-PAL_AMB           : ACT for environmental sense PAL
      +-PAL_MAG           : ACT for open-close PAL
      +-PAL_MOT           : ACT for motion PAL
      ..
      +-Parent-MONOSTICK  : ACT for parent device (for MONOSTICK)
      +-PingPong          : ACT for transmit and receive like Ping Pong
      +-PulseCounter      : ACT using a pulse counter
      +-act0              : Scratching ACT
    Act_samples
      +-PingPong
        +-PingPong.cpp   : ACT code
        +-build          : build folder
        +-.vscode        : setting files for VSCode
    Act_samples
      +-PingPong
        +-build
          +-Makefile        : makefile
          +-build-BLUE.cmd  : build script for TWELITE BLUE
          +-build-RED.cmd   : build script for TWELITE RED
          +-build-clean.cmd : clean up obj_* files
    MWSDK_ROOT=/work/MWSTAGE/MWSDK
    export MWSDK_ROOT
    $ make
    
    Command 'make' not found, but can be installed with:
    
    sudo apt install make
    sudo apt install make-guile
    
    $ sudo apt install make
    ...
    $ cd $MWSDK_ROOT
    $ cd Act_samples/PingPong/build
    $ pwd
    /mnt/c/MWSDK/Act_samples/PingPong/build
    
    $ ls
    ... View file list
    
    $ rm -rfv objs_*
    ... Delete intermediate files just in case
    
    $ make TWELITE=BLUE
    ... Normal build for BLUE
    
    $ make -j8 TWELITE=BLUE
    ... Parallel build for BLUE (8 processes simultaneously)
    ...
    "windows": {
        "command": "sh",
        "args": [
            "-c", "make TWELITE=BLUE 2>&1 | sed -E -e s#\\(/mnt\\)?/\\([a-zA-Z]\\)/#\\\\\\2:/#g"
        ],

    PingPong

    Send a PING wireless packet from one of the two serially connected TWELITEs and receive a PONG wireless packet back from the other.

    circle-check

    This ACT includes.

    Sending a prompt response from the receipt of a wireless packet
  • Transmission with direct address of the peer

  • Input from serial port - Serial

  • Digital (button) input - Buttons

  • Analogue input - Analogue

  • hashtag
    how to use act

    hashtag
    Required TWELITE

    Two of any of the following.

    • MONOSTICK BLUE or REDarrow-up-right

    • TWELITE DIParrow-up-right connected to UART with TWELITE Rarrow-up-right products/TWE-Lite-DIP/index.html), etc.

    hashtag
    Explanation of ACT

    hashtag
    Include

    Include <TWELITE> in all ACTs. Here, the simple network <NWK_SIMPLE> should be included.

    hashtag
    Declaration section

    • 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

    hashtag
    セットアップ setup()

    The general flow of the program is the initial setup of each section and the start of each section.

    hashtag
    the_twelite

    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.

    circle-info

    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.

    hashtag
    Analogue

    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

    circle-info

    Except for the first time, the ADC is started in an interrupt handler.

    hashtag
    Buttons

    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.

    circle-info

    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.

    hashtag
    Serial

    Serial objects can be used without initialization or initiation procedures.

    Outputs a string to the serial port. mwx::crlf is a newline character.

    hashtag
    loop()

    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.

    circle-exclamation

    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.

    hashtag
    Serial

    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.

    hashtag
    Buttons

    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.

    circle-info

    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.

    hashtag
    transmit()

    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.

    hashtag
    Getting Network and Packet Objects

    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.

    hashtag
    Settings for sending packets

    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.

    hashtag
    Data Payload in a Packet

    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.

    circle-info

    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.

    hashtag
    on_rx_packet()

    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.

    circle-info

    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.

    circle-info

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

    // The following statements are not available in the MWX library
    #include <iostream>
    std::cout << "hello world" << std::endl;
    // use twelite mwx c++ template library
    #include <TWELITE>
    #include <NWK_SIMPLE>
    // application ID
    const uint32_t APP_ID = 0x1234abcd;
    
    // channel
    const uint8_t CHANNEL = 13;
    
    // DIO pins
    const uint8_t PIN_BTN = 12;
    
    /*** function prototype */
    void vTransmit(const char* msg, uint32_t addr);
    
    /*** application defs */
    // packet message
    const int MSG_LEN = 4;
    const char MSG_PING[] = "PING";
    const char MSG_PONG[] = "PONG";
    void setup() {
    	/*** SETUP section */
    	Buttons.setup(5); // init button manager with 5 history table.
    	Analogue.setup(true, 50); // setup analogue read (check every 50ms)
    
    	// the twelite main class
    	the_twelite
    		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
    		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    
    	// Register Network
    	auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
    	nwksmpl << NWK_SIMPLE::logical_id(0xFE) // set Logical ID. (0xFE means a child device with no ID)
    	        << NWK_SIMPLE::repeat_max(3);   // can repeat a packet up to three times. (being kind of a router)
    
    	/*** BEGIN section */
    	Buttons.begin(pack_bits(PIN_BTN), 5, 10); // check every 10ms, a change is reported by 5 consequent values.
    	Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC)); // _start continuous adc capture.
    
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial << "--- PingPong sample (press 't' to transmit) ---" << mwx::crlf;
    }
    	// the twelite main class
    	the_twelite
    		<< TWENET::appid(APP_ID)    // set application ID (identify network group)
    		<< TWENET::channel(CHANNEL) // set channel (pysical channel)
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    auto&& nwksmpl = the_twelite.network.use<NWK_SIMPLE>();
    nwksmpl << NWK_SIMPLE::logical_id(0xFE);
            << NWK_SIMPLE::repeat_max(3);
    the_twelite.begin(); // start twelite!
    Analogue.setup(true);
    Analogue.begin(pack_bits(PIN_ANALOGUE::A1, PIN_ANALOGUE::VCC), 50); 
    Buttons.setup(5);
    Buttons.begin(pack_bits(PIN_BTN),
    					5, 		// history count
    					10);  	// tick delta
    Serial << "--- PingPong sample (press 't' to transmit) ---" << mwx::crlf;
    void loop() {
    	  // read from serial
    		while(Serial.available())  {
    				int c = Serial.read();
    				Serial << mwx::crlf << char(c) << ':';
    				switch(c) {
    				    case 't':
    				    	  vTransmit(MSG_PING, 0xFF);
    				        break;
    				    default:
    							  break;
    				}
    		}
    
    
    	// Button press
    	if (Buttons.available()) {
    		uint32_t btn_state, change_mask;
    		Buttons.read(btn_state, change_mask);
    
    		// Serial << fmt("<BTN %b:%b>", btn_state, change_mask);
    		if (!(change_mask & 0x80000000) && (btn_state && (1UL << PIN_BTN))) {
    			// PIN_BTN pressed
    			vTransmit(MSG_PING, 0xFF);
    		}
    	}
    }
    		while(Serial.available())  {
    				int c = Serial.read();
    				Serial << mwx::crlf << char(c) << ':';
    				switch(c) {
    				    case 't':
    				    	  vTransmit(MSG_PING, 0xFF);
    				        break;
    				    default:
    							  break;
    				}
    		}
    	if (Buttons.available()) {
    		uint32_t btn_state, change_mask;
    		Buttons.read(btn_state, change_mask);
    // Serial << fmt("<BTN %b:%b>", btn_state, change_mask);
    if (!(change_mask & 0x80000000) && (btn_state && (1UL << PIN_BTN))) {
    	// PIN_BTN pressed
    	vTransmit(MSG_PING, 0xFF);
    void vTransmit(const char* msg, uint32_t addr) {
    	Serial << "vTransmit()" << mwx::crlf;
    
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    		// set tx packet behavior
    		pkt << tx_addr(addr)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    			<< tx_retry(0x3) // set retry (0x3 send four times in total)
    			<< tx_packet_delay(100,200,20); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
    
    		// prepare packet payload
    		pack_bytes(pkt.get_payload() // set payload data objects.
    			, make_pair(msg, MSG_LEN) // string should be paired with length explicitly.
    			, uint16_t(analogRead(PIN_ANALOGUE::A1)) // possible numerical values types are uint8_t, uint16_t, uint32_t. (do not put other types)
    			, uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)) // A1 and VCC values (note: alalog read is valid after the first (Analogue.available() == true).)
    			, uint32_t(millis()) // put timestamp here.
    		);
    	
    		// do transmit 
    		pkt.transmit();
    	}
    }
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    		pkt << tx_addr(addr)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    			<< tx_retry(0x3) // set retry (0x3 send four times in total)
    			<< tx_packet_delay(100,200,20); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
    # Index of first byte: Data type : Number of bytes : Contents
    
    00: uint8_t[4] : 4 : four-character identifier
    08: uint16_t   : 2 : ADC value of AI1 (0..1023)
    06: uint16_t   : 2 : Voltage value of Vcc (2000..3600)
    10: uint32_t   : 4 : millis() system time
    // prepare packet payload
    pack_bytes(pkt.get_payload() // set payload data objects.
    	, make_pair(msg, MSG_LEN) // string should be paired with length explicitly.
    	, uint16_t(analogRead(PIN_ANALOGUE::A1)) // possible numerical values types are uint8_t, uint16_t, uint32_t. (do not put other types)
    	, uint16_t(analogRead_mv(PIN_ANALOGUE::VCC)) // A1 and VCC values (note: alalog read is valid after the first (Analogue.available() == true).)
    	, uint32_t(millis()) // put timestamp here.
    );
    pkt.transmit();
    void on_rx_packet(packet_rx& rx, bool_t &handled) {
    		uint8_t msg[MSG_LEN];
    		uint16_t adcval, volt;
    		uint32_t timestamp;
    
    		// expand packet payload (shall match with sent packet data structure, see pack_bytes())
    		expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
    					, msg       // 4bytes of msg
    											//   also can be -> std::make_pair(&msg[0], MSG_LEN)
    					, adcval    // 2bytes, A1 value [0..1023]
    				  , volt      // 2bytes, Module VCC[mV]
    					, timestamp // 4bytes of timestamp
            );
    		
    		// if PING packet, respond pong!
        if (!strncmp((const char*)msg, "PING", MSG_LEN)) {
    				// transmit a PONG packet with specifying the address.
            vTransmit(MSG_PONG, rx.get_psRxDataApp()->u32SrcAddr);
        }
    
    		// display the packet
    		Serial << format("<RX ad=%x/lq=%d/ln=%d/sq=%d:" // note: up to 4 args!
                        , rx.get_psRxDataApp()->u32SrcAddr
                        , rx.get_lqi()
                        , rx.get_length()
    					, rx.get_psRxDataApp()->u8Seq
                        )
    				<< format(" %s AD=%d V=%d TS=%dms>" // note: up to 4 args!
    					, msg
    					, adcval
    					, volt
    					, timestamp
    					)
                   << mwx::crlf
    			   << mwx::flush;
    	}
    while (the_twelite.receiver.available()) {
    		auto&& rx = the_twelite.receiver.read();
    Serial << format("..receive(%08x/%d) : ",
       rx.get_addr_src_long(), rx.get_addr_src_lid());
    uint8_t msg[MSG_LEN];
    uint16_t adcval, volt;
    uint32_t timestamp;
    
    // expand packet payload (shall match with sent packet data structure, see pack_bytes())
    expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
    		, msg       // 4bytes of msg
    								//   also can be -> std::make_pair(&msg[0], MSG_LEN)
    		, adcval    // 2bytes, A1 value [0..1023]
    	  , volt      // 2bytes, Module VCC[mV]
    		, timestamp // 4bytes of timestamp
        );
    if (!strncmp((const char*)msg, "PING", MSG_LEN)) {
        vTransmit(MSG_PONG, rx.get_psRxDataApp()->u32SrcAddr);
    }
    		Serial << format("<RX ad=%x/lq=%d/ln=%d/sq=%d:" // note: up to 4 args!
                        , rx.get_psRxDataApp()->u32SrcAddr
                        , rx.get_lqi()
                        , rx.get_length()
    										, rx.get_psRxDataApp()->u8Seq
                        )
               << format(" %s AD=%d V=%d TS=%dms>" // note: up to 4 args!
                        , msg
                        , adcval
                        , volt
                        , timestamp
                        )
             << mwx::crlf
    			   << mwx::flush;

    BRD_I2C_TEMPHUMID

    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 PALarrow-up-right or TWELITE ARIA BLUE / REDarrow-up-right). However you can also use generic I2C devices by editing receiving/sending procedures. Connect a generic I2C device according to the following diagram.

    Connection with a generic I2C device
    circle-check

    This Act includes following features:

    • Sending / Receiving wireless packets

    • Configuration with "interactive settings mode" -

    • Building a state machine with "SM_SIMPLE" -

    hashtag
    Features

    • Send / Receive I2C commands

    • Sleep periodically for running with a button cell.

    hashtag
    Usage

    hashtag
    Required TWELITE modules

    Type
    Example

    hashtag
    Act Explained

    hashtag
    Header files

    Header files are <NWK_SIMPLE> (Simple network support), <STG_STD> (Interactive settings mode) and <SM_SIMPLE> (Simple state machines).

    hashtag
    Sensor drivers

    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

    Name
    Description

    Each procedures are shown below.

    hashtag
    setup()

    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.

    hashtag
    begin()

    Write commands to operate the sensor.

    In the MWX Libary, you can choose two different ways to control the I2C line from the . This act is using .

    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.

    hashtag
    get_convtime()

    Return the value of CONV_TIME.

    hashtag
    read()

    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.

    Byte
    Description

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

    hashtag
    setup()

    Called once when the TWELITE is started. This function performs various initializations.

    hashtag
    State machine with SM_SIMPLE

    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.

    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.

    hashtag
    Set BEHAVIOR

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

    hashtag
    Interactive settings mode STG_STD

    In order to make the interactive settings mode configuration items suitable for the application you are describing, initialize . /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.

    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.

    hashtag
    Initialize peripherals

    Initialization for the I2C device.

    hashtag
    Start MWX

    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.

    hashtag
    loop()

    The loop() is controlled by the step 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.

    hashtag
    case STATE::INTERACTIVE:

    It is not convenient for the main loop to operate during interactive settings mode, so it is fixed in this state.

    hashtag
    case STATE::INIT:

    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 .

    hashtag
    case STATE::SENSOR:

    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, , 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 is a low-cost function to divide by 100.

    hashtag
    case STATE::TX:

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

    hashtag
    case STATE::TX_WAIT_COMP:

    Here, it waits for the completion of transmission. Judgment of timeout (in case of error) or transmission completion event is made.

    hashtag
    STATE::GO_SLEEP:

    Processes the sleepNow() function. By calling this function, the TWELITE wireless microcontroller goes to sleep.

    hashtag
    on_tx_comp()

    This is a system event called when transmission is completed. Here .set_flag() is called to set the flag of step.

    hashtag
    sleepNow()

    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.

    circle-info

    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.

    triangle-exclamation

    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.

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

    CRC8 value of the byte 3 and 4

    SETTINGS::ch_default : channel default. Execute if you want your own application to have its own default channel.

    Parent

    Run the Act Parent_MONOSTICK on the MONOSTICK BLUE or REDarrow-up-right

    Children

    - BLUE PAL / RED PALarrow-up-right + AMBIENT SENSE PALarrow-up-right - TWELITE ARIA BLUE / REDarrow-up-right

    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)

    <STG_STD>
    <SM_SIMPLE>
    Wire class object
    Helper functions
    Helper functions
    SM_SIMPLEarrow-up-right
    STG_STGarrow-up-right
    SM_SIMPLE state machine
    Example of waiting for sleep
    TickTimer
    div100()

    5

    #include <TWELITE>
    #include <NWK_SIMPLE>// Network support
    #include <STG_STD>   // Interactive settings mode
    #include <SM_SIMPLE> // Simple state machines
    /*** sensor select, define either of USE_SHTC3 or USE_SHT40  */
    // use SHTC3 (TWELITE PAL)
    #define USE_SHTC3
    // use SHT40 (TWELITE ARIA)
    #undef USE_SHT40
    #if defined(USE_SHTC3)
    // for SHTC3
    struct SHTC3 {
    	uint8_t I2C_ADDR;
    	uint8_t CONV_TIME;
    
        bool setup() { ... }
    	bool begin() { ... }
    	int get_convtime() { return CONV_TIME; }
    	bool read(int16_t &i16Temp, int16_t &i16Humd) { ... }
    } sensor_device;
    bool setup() {
    	// here, initialize some member vars instead of constructor.
    	I2C_ADDR = 0x70;
    	CONV_TIME = 10; // wait time [ms]
    	return true;
    }
    bool begin() {
    	// send start trigger command
    	if (auto&& wrt = Wire.get_writer(I2C_ADDR)) {
    		wrt << 0x60; // SHTC3_TRIG_H
    		wrt << 0x9C; // SHTC3_TRIG_L
    	} else {
    		return false;
    	}
    	return true;
    }
    int get_convtime() {
    	return CONV_TIME;
    }
    bool read(int16_t &i16Temp, int16_t &i16Humd) {
    	// read result
    	uint16_t u16temp, u16humd;
    	uint8_t u8temp_csum, u8humd_csum;
    	if (auto&& rdr = Wire.get_reader(I2C_ADDR, 6)) {
    		rdr >> u16temp;      // read two bytes (MSB first)
    		rdr >> u8temp_csum;  // check sum (crc8)
    		rdr >> u16humd;      // read two bytes (MSB first)
    		rdr >> u8humd_csum;  // check sum (crc8)
    	} else {
    		return false;
    	}
    
    	// check CRC and save the values
    	if (   (CRC8_u8CalcU16(u16temp, 0xff) == u8temp_csum)
    		&& (CRC8_u8CalcU16(u16humd, 0xff) == u8humd_csum))
    	{
    		i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
    		i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);
    	} else {
    		return false;
    	}
    
    	return true;
    }
    void setup() {
    	/*** SETUP section */
    	...
    }
    // application state defs
    enum class STATE : uint8_t {
    	INTERACTIVE = 255,
    	INIT = 0,
    	SENSOR,
    	TX,
    	TX_WAIT_COMP,
    	GO_SLEEP
    };
    
    // simple state machine.
    SM_SIMPLE<STATE> step;
    
    void setup() {
    	...
    	/// init vars or objects
    	step.setup(); // initialize state machine
    	...
    }
    void setup() {
    	...
    	/// load board and settings objects
    	auto&& set = the_twelite.settings.use<STG_STD>(); // load save/load settings(interactive settings mode) support
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>(); // load network support
    	...
    }
    	...
    	/// configure settings
    	// configure settings
    	set << SETTINGS::appname(FOURCHARS);
    	set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
    	set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
    	set.hide_items(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(DIO12)=LOW is detected, start with intaractive mode.
    	if (digitalRead(PIN_DIGITAL::DIO12) == PIN_STATE::LOW) {
    		set << SETTINGS::open_at_start();
    		step.next(STATE::INTERACTIVE);
    		return;
    	}
    	// load values
    	set.reload(); // load from EEPROM.
    	OPT_BITS = set.u32opt1(); // this value is not used in this example.
    
    	// LID is configured DIP or settings.
    	LID = set.u8devid(); // 2nd is setting.
    	if (LID == 0) LID = 0xFE; // if still 0, set 0xFE (anonymous child)
    	/// configure system basics
    	the_twelite << set; // apply settings (from interactive settings mode)
    	nwk << set; // apply settings (from interactive settings mode)
    	nwk << NWK_SIMPLE::logical_id(LID); // set LID again (LID can also be configured by DIP-SW.)
    	...
    	/*** BEGIN section */
    	Wire.begin(); // start two wire serial bus.
    	// let the TWELITE begin!
    	the_twelite.begin();
    
    	/*** INIT message */
    	Serial << "--- TEMP&HUMID:" << FOURCHARS << " ---" << mwx::crlf;
    	Serial	<< format("-- app:x%08x/ch:%d/lid:%d"
    					, the_twelite.get_appid()
    					, the_twelite.get_channel()
    					, nwk.get_config().u8Lid
    				)
    			<< mwx::crlf;
    	Serial 	<< format("-- pw:%d/retry:%d/opt:x%08x"
    					, the_twelite.get_tx_power()
    					, nwk.get_config().u8RetryDefault
    					, OPT_BITS
    			)
    			<< mwx::crlf;
    void loop() {
    	do {
    		switch (step.state()) {
    		 // states
    		case STATE::INIT:
    		...
    		break;
    		...
    		}
    	while(step.b_more_loop());
    }
    // start sensor capture
    sensor_device.begin();
    step.set_timeout(sensor_device.get_convtime()); // set timeout
    step.next(STATE::SENSOR);
    if (step.is_timeout()) {
    	// the sensor data should be ready (wait some)
    	sensor_device.read(sensor.i16temp, sensor.i16humid);
    
    	Serial << "..finish sensor capture." << mwx::crlf
    		<< "     : temp=" << div100(sensor.i16temp) << 'C' << mwx::crlf
    		<< "       humd=" << div100(sensor.i16humid) << '%' << mwx::crlf
    		;
    	Serial.flush();
    
    	step.next(STATE::TX);
    }
    step.next(STATE::GO_SLEEP); // set default next state (for error handling.)
    
    // get new packet instance.
    if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    	...
    }
    // set tx packet behavior
    pkt << tx_addr(0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    	<< tx_retry(0x1) // set retry (0x1 send two times in total)
    	<< tx_packet_delay(0, 0, 2); // send packet w/ delay
    pack_bytes(pkt.get_payload()
    	, make_pair(FOURCHARS, 4)
    	, uint16_t(sensor.i16temp)
    	, uint16_t(sensor.i16humid)
    	);
    // 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);
    }
    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);
    }
    void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
    	step.set_flag(ev.bStatus);
    }
    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);
    }
    void wakeup() {
    	Serial	<< mwx::crlf
    			<< "--- PAL_AMB:" << FOURCHARS << " wake up ---"
    			<< mwx::crlf
    			<< "..start sensor capture again."
    			<< mwx::crlf;
    	...
    }

    The design policy

    This section describes the specifications, limitations, notes in this document, and design memos for the C++ language used in the MWX library.

    circle-info

    This page is intended for use in situations where you need to refer to the library resource code to understand how the library works or to modify it. It assumes a higher level of knowledge of the C++ language compared to using the library.

    hashtag
    Design Policy

    • The application loop description is intended to be similar to the commonly used API system, but the implementation should be tailored to the characteristics of TWELITE.

    • TWENET is an event-driven code description, and it should be classed so that it can be handled. The above classifications will encapsulate the behavior of the application.

    • Event-driven and loop descriptions should be able to coexist.

    circle-info

    As we are working on the implementation in a limited time, the design does not cover all the details, but if you have any questions about the design or implementation, please contact our support.

    hashtag
    About the C++ Compiler

    hashtag
    Version

    gcc version 4.7.4

    hashtag
    C++ standard

    C++11 (For compiler support status, please refer to the general information.)

    hashtag
    C++ limitations

    ※ This is a description of what we know.

    • You can allocate memory with the new and new[] operators, but you cannot destroy the allocated memory; most C++ libraries that allocate memory dynamically are virtually unusable. It is used for objects that are created only once and not destroyed after that.

    • The constructor of the global object is not called. Note: If necessary, you can initialize the constructor call by using the initialization function (setup()) as shown in (new ((void*)&obj_global) class_foo();).

    hashtag
    Design Memo

    This section contains information that will help you understand the code when referring to the MWX library code.

    hashtag
    Current implementation

    Due to the limited time available for implementation, some of the details may not be sufficiently developed. For example, const is not fully taken into account in many classes.

    hashtag
    namespace

    We have the following policy for namespaces.

    • In principle, definitions are placed in a common namespace mwx.

    • We want to be able to use namespaces without identifiers, but we want to require identifiers for some definitions.

    • Class names should be relatively long, and those used by users should be defined as aliases.

    Classes, Functions, and constants are defined within the namespace of mwx names (more precisely, mwx::L1 enclosed in inline namespace L1), with a few exceptions. inline namespace is specified so that definitions that require the specification of mwx:: can coexist with those that do not. The reason why inline namespace is specified is to allow definitions that require the specification of mwx:: to coexist with those that do not.

    Most of the definitions do not require namespace names to be specified by using namespace. These specifications are made in using_mwx_def.hpp in the library.

    Exceptionally, relatively short names can be specified as mwx::crlf, mwx::flush. These are placed in the inline namespace mwx::L2; using namespace mwx::L2; will allow them to be used without specifying the namespace name.

    Also, some class names have a using specification.

    The std::make_pair used in the MWX library is specified using.

    hashtag
    CRTP(Curiously recursive template patterns.)

    Since virtual functions (virtual) and run-time type information (RTTI) are not available, and even if they were available, they would be difficult to perform, is used as an alternative design method. CRTP is a template pattern for calling methods of a child class from the parent class from which it is inherited.

    The following example shows how to implement an interface called interface() in a Derived class that inherits from Base, and calls the Derived::print() method from Base.

    The following are the main classes used in the MWX library.

    • Basic parts of event processingmwx::appdefs_crtp

    • state machine public mwx::processev_crtp

    • stream mwx::stream

    hashtag
    Virtualization with CRTP

    In the CRTP class, the class from which it inherits is different for each instance. For this reason, it is not possible to cast it to a parent class and treat it as a member of the same family, nor is it possible to use advanced polymorphism such as virtual functions or RTTI (runtime type information).

    The following is an example of implementing the above CRTP example with a virtual function: CRTP cannot manage instances together in the same array as in Base* b[2].

    The MWX library solves this problem by defining a dedicated class for storing class instances of CRTP and defining a similar interface to this class. An example code is given below.

    The VBase class member variable p_inst stores a pointer to an object of type Base , and pf_intrface is a member function pointer to Base::s_intrface. Base::s_intrface invokes the T::intrface method by being passed an object instance of itself as an argument and static_casting it to the T type.

    Storage in VBase is implemented here by overloading the = operator (see below for source examples).

    In the above example, when making a call to b[0].intrface(), Base::s_intrface() will be called with reference to the VBase::pf_intrface function pointer. In addition, a call to Derived1::intrface() will be made. This part is expected to be expanded inline by the compiler.

    It is also possible to perform a conversion from the VBase type to the original Derived1 or Derived2 through a forced cast, but there is no way to directly know the type of the pointer stored in void*. Although there is no completely safe way to do this, a unique ID (TYPE_ID) is provided for each class as shown below, and the ID is checked when the cast is executed (get() method). If the get() method is called with a different type, an error message will be displayed.

    BIf a pointer is stored as a Base type, it may not be correctly converted to a T type (e.g., when T has multiple inheritance), so a static_assert is used to determine at compile time that the pointer is derived from a Base type by using is_base_of in <type_trails>.

    hashtag
    new, new[] operator

    The microcontroller in the TWELITE module does not have enough memory nor does it have advanced memory management. However, the area from the end of the microcontroller's memory map to the stack area is available as a heap area, which can be allocated as needed. An overview of the memory map is shown in the figure below, where APP is the RAM area allocated by the application code, HEAP is the heap area, and STACK is the stack area.

    Even if it is not possible to delete, the new operator may be useful in some situations. For this reason, the new and new[] operators are defined as follows: pvHear_Alloc() is a function for allocating memory provided by the semiconductor library, and the same is true for u32HeapStart and u32HeapEnd. 0xdeadbeef is a dummy address. Please do not point out that it is strange that beef is dead.

    Since exceptions cannot be used, there is no way to deal with failures. Also, if you continue to allocate without being aware of the memory capacity, there is a possibility of interference with the stack area.

    circle-info

    The memory allocated by the system (e.g. MAC layer) is about 2.5KB.

    hashtag
    Container class

    The MWX library does not use the container classes provided by the standard library, considering the small resources of the microcontroller and the lack of dynamic memory allocation, but defines two simple container classes. The container classes have defined iterators and begin() and end() methods, so you can use some of the range for statements and STL algorithms.

    Class name
    remark

    hashtag

    hashtag
    About memory in container classes

    In the container class, the memory allocation method is specified as a parameter of the template argument.

    Class name
    Remark

    hashtag
    variable parameter

    In the MWX library, variable number arguments are used for operations on byte sequences, bit sequences, and printf equivalent operations. The example below shows the process of setting 1 to the specified bit position.

    In this process, the parameter pack of template (typename... part of template) to perform recursive processing to expand the arguments. In the above example, since constexpr is specified, the calculation is done at compile time and the result is equivalent to macro definition or const value specification such as b2. It can also behave as a function that dynamically calculates variables as arguments.

    In the following example, the expand_bytes function is used to store a value in a local variable from the received packet data string. In the case of using a parameter pack, since the type of each argument can be known, it is possible to store values of different sizes and types from the byte string of the received packet, as shown below.

    hashtag
    Iterator

    An iterator is an abstraction of a pointer, which has the effect of making it possible to access data structures as if they were pointers, even if the data structures are not memory-contiguous, for example.

    circle-info

    In the C++ STL, the combination of an iterator indicating the beginning of the container obtained by the begin() method and an iterator indicating the "next" end of the container obtained by the end() method is often used.

    The reason why we use end() for the "next" at the end of the container is because we expect the following description, and the MWX library follows this in its container implementation.

    Conform the iterator to the standard library specification, so that range for statements can be used, and algorithms from the standard library can be used.

    (The MWX library has not been tested for compatibility with the C++ standard library. Please check the operation before use.)

    The following example shows the use of an iterator for a FIFO queue that cannot be accessed continuously with a normal pointer, and also an iterator that extracts only a specific member of the FIFO queue structure (the X axis in the example).

    The following is an excerpt of the implementation of an iterator for the smplque class. In this iterator, the queue object is managed by its entity and its index. The part of the queue that is discontiguous in memory (ring buffer structure where the next to the tail must point to the beginning) is solved by smplque::operator []. If the addresses of the objects match and the indices match, the iterators point to the same thing.

    This implementation part also includes the typedefs required by , allowing more STL algorithms to be applied.

    構造体を格納したコンテナ中の、特定構造体メンバーだけアクセスするイテレータは少々煩雑です。構造体のメンバーにアクセスするメンバー関数を予め定義しておきます。このメンバー関数をパラメータ(R& (T::*get)())としたテンプレートを定義します。Iterはコンテナクラスのイテレータ型です。

    Iterators that access only specific structure members in the container that contains the structure are a bit complicated. Define a member function to access the structure members in advance. Next, define a template with this member function as a parameter (R& (T::*get)()). "Iter" is the iterator type of the container class.

    The operator * that accesses the value calls the member function described above. The *_p is the axis_xyzt structure, and (*_p.*get)() calls _p->get_x() if &axis_xyzt::get_x is specified in T::*get.

    The _axis_xyzt_iter_gen class implements only begin(), end() and generates the above iterators. Now you can use range for statements and algorithms.

    This class name is very long and difficult to write in the source code. We will prepare a generator function to generate this class. In the example below, it is get_axis_x() in the last line. By using this generator function, the description becomes as simple as auto&& vx = get_axis_x(que); as shown in the beginning.

    This iterator for extracting only the axes can also be used with the smplbuf class of array type as well.

    hashtag
    Implementing interrupt, event, and state handlers

    In order to describe the application behavior by user-defined classes, typical handlers need to be defined as mandatory methods, but it is complicated to define all the other numerous interrupt handlers, event handlers, and state machine state handlers. Ideally, only those defined by the user should be defined, and only that code should be executed.

    In the MWX library, a large number of DIO interrupt handlers (on TWELITE hardware, a single interrupt, but for ease of use, a handler is assigned to each DIO) are defined as empty handlers using templates, and user-defined member functions are defined by specializing the templates.

    The actual user-described code has been simplified by macroizing and including header files, but the above includes the code necessary for the explanation.

    The my_app_def::cbTweNet_u8HwInt() is called from the interrupt handler from TWENET. in the cpp file, only int_dio_handler<12> is instantiated with the specialization described in it. file is instantiated from a template in the hpp file. The rest are instantiated from templates in the hpp file.

    Eventually, we can expect that the compiler optimization will determine that codes other than number 12 are meaningless and disappear from the code (however, we do not guarantee that they will be optimized as described above).

    In other words, in user code, if you want to define the behavior at interrupt 12, just write int_dio_handler<12> (Note: to enable DIO interrupt, you need to call attachInterrupt()). Handlers that are not registered are expected to be low-cost calls due to compile-time optimization.

    circle-info

    One technique to enable this when the user defines a function, and call another function if not defined, is to resolve it at link time. Specify __attribute__((wake)) as shown below. If the wakeup() function is defined in the user code, the user code will be linked to the function, and if it is not defined, the function with empty content will be linked.

    In the implementation of the above handler, it is necessary to generate weak member variables explicitly, and it is difficult to optimize by inlining, so it is not used. However, weak functions are defined to receive some callbacks from TWENET, such as wakeup().

    hashtag
    Stream class

    The stream class is mainly used for input/output of UART (serial port), and MWX library mainly defines procedures for output. But some of them are also defined for input.

    This section describes the implementation required by the derived class.

    The above is an implementation of the write() method that writes a single character. The stream<serial_jen> of the parent class accesses the serial_jen::write() method using the get_Drived() method to perform casting.

    Define methods such as write(), read(), flush(), and available() as needed.

    For formatting output, we use Marco Paland's , which needs to be implemented for use with the MWX library. In the following example, the derived class serial_jen needs to define the vOutput() method for 1-byte output, and save the auxiliary information for output in the parent class pvOutputContext since vOutput() is a static method. The other is to save the auxiliary information in the pvOutputContext of the parent class since vOutput() is a static method.

    By get_pfcOutput(), the vOutput() function defined in the derived class is specified, and pvOutputContext is passed as its parameter. In the above example, when the << operator is called with int type, serial_jen::vOutput() and TWE_tsFILE* which is already set for UART are passed to the fctprintf() function.

    hashtag
    Worker object for Wire, SPI

    In the Wire class, it is necessary to manage the communication from start to end when sending and receiving with a 2-wire device. This section describes the contents of the description of using the worker object.

    This is an excerpt of the periph_twowire::writer class, which inherits from mwx::stream<writer> to implement the stream interface, and implements the write() and vOutput() methods to use the steam interface. To use the steam interface, the write() and `vOutput() methods are implemented.

    The constructor calls the method to start communication for 2-wire serial and the destructor calls the method to end communication. Also, the operator bool() operator returns true if the communication of the 2-wire serial device is successfully started.

    The get_writer() method creates an object wrt. Due to the Return Value Optimization (RVO) of the C++ compiler, the writer is created directly in the wrt, so no copy is made and the bus running in the constructor is not initialized multiple times. However, RVO is not guaranteed by the C++ specification, and just in case, the MWX library defines copy, delete assignment operators, and move constructors (although it is unlikely that move constructors will be evaluated).

    The wrt in the if clause is first initialized by the constructor and starts communication at the same time. If there is no error at the start of communication, the bool operator at the time of conditional judgment returns true, and the processing in the scope of the if clause takes place. If there is no error at the start of communication, the bool operator at the conditional judgment returns true, and the processing in the if clause scope is performed. When the scope is exited, the destructor terminates the 2-wire serial bus. If there is no communication partner, false will be returned and the wrt object will be destroyed.

    It overrides the definition of operator << (int), which is specific to Wire and SPI. The default behavior of the stream is to convert numeric values to strings and output them, but Wire and SPI rarely write numeric strings to the bus, and on the contrary, we often want to input literals of numeric type such as configuration values. We will change this behavior.

    In this example, the int type values are truncated to 8 bits and the values are output.

    Simplify procedures by classifying typical peripherals. Make them accessible by loop descriptions whenever possible.

  • Simplify the procedures for using the boards we sell, such as MONOSTICK/PAL, by creating classes. (For example, to automate the use of an external watchdog timer.

  • Application classes and board classes should be made available through a unified procedure, introducing the idea of polymorphism. (For example, to load application classes with several behaviors at startup, and to avoid defining the connection code of the TWENET C library each time).

  • There are no restrictions on the use of C++ functionality. For example, it provides a means to simplify typical procedures such as packet construction and decomposition, which are complicated in handling wireless packets.

  • The operator -> should be avoided as much as possible, and the API should be based on reference types in principle.

  • Exception cannot be used.
  • Unable to use virtual function.

  • smplbuf

    It is an array class that manages the maximum area (capacity) and the usable area (size) whose size can be specified within the maximum area. This class also implements the stream interface, so data can be written using the << operator.

    smplque

    The FIFO queue is implemented. The size of the queue is determined by template parameters. There is also a template argument to manipulate the queue using interrupt inhibition.

    alloc_attach

    Specify the buffer memory that has already been allocated. This is used when you want to manage the memory area allocated for the C library, or when you want to process the same buffer area as a fragmented area.

    alloc_static

    Allocate as a static array in the class. The size is determined in advance or used as an area for temporary use.

    alloc_heap

    Allocate to the heap area. Once allocated to the system heap, it cannot be discarded, but it is suitable for use in initialization to allocate an area according to application settings.

    https://gcc.gnu.org/gcc-4.7/cxx0x_status.htmlarrow-up-right
    https://cpprefjp.github.io/implementation-status.htmlarrow-up-right
    CRTP (Curiously recurring template pattern) arrow-up-right
    printf libraryarrow-up-right
    // at some header file.
    namespace mwx {
      inline namespace L1 {
        class foobar {
          // class definition...
        };
      }
    }
    
    // at using_mwx_def.hpp
    using namespace mwx::L1; // Definitions in mwx::L1 can be accessed without mwx::.
                             // But mwx::L2 needs mwx::.
    template <class T>
    class Base {
    public:
      void intrface() {
        T* derived = static_cast<T*>(this);
        derived->prt();
      }
    };
    
    class Derived : public class Base<Derived> {
      void prt() {
         // print message here!
         my_print("foo");
      }
    }
    class Base {
    	virtual void prt() = 0;
    public:
    	void intrface() { prt(); }
    };
    
    class Derived1 : public Base {
    	void prt() { my_print("foo"); }
    };
    
    class Derived2 : public Base {
    	void prt() { my_print("bar"); }
    };
    
    Derived1 d1;
    Derived2 d2;
    Base* b[2] = { &d1, &d2 };
    
    void tst() {
    	for (auto&& x : b) { x->intrface(); }
    }
    class VBase {
    public:
    	void* p_inst;
    	void (*pf_intrface)(void* p);
    
    public:
    	void intrface() {
    		if (p_inst != nullptr) {
    			pf_intrface(p_inst);
    		}
    	}
    };
    
    template <class T>
    class Base {
    	friend class VBase;
    	static void s_intrface(void* p) {
    		T* derived = static_cast<T*>(p);
    		derived->intrface();
    	}
    public:
    	void intrface() {
    		T* derived = static_cast<T*>(this);
    		derived->prt();
    	}
    };
    
    class Derived1 : public Base<Derived1> {
    	friend class Base<Derived1>;
    	void prt() { my_print("foo"); }
    };
    
    class Derived2 : public Base<Derived2> {
    	friend class Base<Derived2>;
    	void prt() { my_print("bar"); }
    };
    
    Derived1 d1;
    Derived2 d2;
    
    VBase b[2];
    
    void tst() {
    	b[0] = d1;
    	b[1] = d2;
    
    	for (auto&& x : b) {
    		x.intrface();
    	}
    }
    #include <type_trails>
    
    class Derived1 : public Base<Derived1> {
    public:
       static const uint8_t TYPE_ID = 1;
    }
    
    class Derived1 : public Base<Derived1> {
    public:
       static const uint8_t TYPE_ID = 2;
    }
    
    class VBase {
      uint8_t type_id;
    public:
    	
    	template <class T>
    	void operator = (T& t) {
    		static_assert(std::is_base_of<Base<T>, T>::value == true,
    						"is not base of Base<T>.");
    
    		type_id = T::TYPE_ID;
    		p_inst = &t;
    		pf_intrface = T::s_intrface;
    	}
    	
      template <class T>
      T& get() {
        static_assert(std::is_base_of<Base<T>, T>::value == true,
    					  "is not base of Base<T>.");
    			
    		if(T::TYPE_ID == type_id) {
    			return *reinterpret_cast<T*>(p_inst);
    		} else {
    			// panic code here!
    		}
      }
    }
    
    Derived1 d1;
    Derived2 d2;
    
    VBase b[2];
    
    void tst() {
    	b[0] = d1;
    	b[1] = d2;
    	
      Derived1 e1 = b[0].get<Derived1>(); // OK
      Derived2 e2 = b[1].get<Derived2>(); // OK
      
      Derived2 e3 = b[1].get<Derived1>(); // PANIC!
    }
    |====APP====:==HEAP==..   :==STACK==|
    0                                  32KB
    void* operator new(size_t size) noexcept {
        if (u32HeapStart + size > u32HeapEnd) {
            return (void*)0xdeadbeef;
        } else {
            void *blk = pvHeap_Alloc(NULL, size, 0);   
            return blk;
        }
    }
    void* operator new[](size_t size) noexcept {
        return operator new(size); }
    void operator delete(void* ptr) noexcept {}
    void operator delete[](void* ptr) noexcept {}
    smplbuf<int16_t, alloc_local<int16_t, 16>> buf;
    buf.push_back(-1); // push_back() は末尾に追加
    buf.push_back(2);
    ...
    buf.push_back(10);
    
    //範囲for文
    for(auto&& x : buf) { Serial << int(x) << ',' }
    //アルゴリズム std::minmax
    auto&& minmax = std::minmax_element(buf.begin(), buf.end());
    Serial << "Min=" << int(*minmax.first)
           << ",Max=" << int(*minmax.second);
    // packing bits with given arguments, which specifies bit position.
    //   pack_bits(5, 0, 1) -> (b100011) bit0,1,5 are set.
    
    // The first function of recursive extraction
    template <typename Head>
    constexpr uint32_t pack_bits(Head head) { return  1UL << head; }
    
    // Extract head and transfer the rest of the parameters to pack_bits 
    //by recursive call
    template <typename Head, typename... Tail>
    constexpr uint32_t pack_bits(Head head, Tail&&... tail) {
      return (1UL << head) | pack_bits(std::forward<Tail>(tail)...);
    }
    
    // コンパAfter ILL, the following two will have the same result.
    constexpr uint32_t b1 = pack_bits(1, 4, 0, 8);
    // b1 and b2 are the same! 
    const uint32_t b2 = (1UL << 1)|(1UL << 4)|(1UL << 0)|(1UL << 8);
    auto&& rx = the_twelite.receiver.read(); // received packet
    
    // Variable that stores the contents of the packet after expansion
    // The payload of a packet is a sequence of bytes, arranged as follows.
    //   [B0][B1][B2][B3][B4][B5][B6][B7][B8][B9][Ba][Bb]
    //   <message       ><adc*  ><vcc*  ><timestamp*    >
    //   * Numerical types are big-endian.
    uint8_t msg[MSG_LEN];
    uint16_t adcval, volt;
    uint32_t timestamp;
    
    // expand packet payload
    expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
    		, msg       // 4bytes of msg
    		, adcval    // 2bytes, A1 value [0..1023]
    	  , volt      // 2bytes, Module VCC[mV]
    	  , timestamp // 4bytes of timestamp
    );
    smplque<uint8_t, alloc_local<uint8_t, 5> > que;
    que.push('a'); que.push('b'); que.pop(); que.push('c'); ...
    
    auto&& p = que.begin();
    auto&& e = que.end();
    
    while(p != e) { // p advanced to e = all elements processed
      Serial << *p;
      ++p; // イThe prefix operator is used for incrementing the terrator.
         // In this case, writing p++ will result in a copy of the iterator 
           // in the code, although it is likely to be optimized by the compiler.
    }
    #include <algorithm>
    #include <cctype>
    
    // Character conversion using lambda expressions
    std::for_each(que.begin(), que.end(), 
      [](uint8_t& x) { x = std::toupper(x); });
    
    // range-for statement
    for (uint8_t x : que) {
      Serial << x;
    }
    //A queue with 5 elements, whose elements are the 4-axis structures of XYZT
    smplque<axis_xyzt, alloc_local<axis_xyzt, 5> > que;
    
    // Input data for testing.
    que.push(axis_xyzt(1, 2, 3, 4));
    que.push(axis_xyzt(5, 2, 3, 4));
    ...
    
    // Access using iterators as structures
    for (auto&& e : v) { Serial << int(e.x) << ','; }
    
    // Extract the X axis in the queue.
    auto&& vx = get_axis_x(que);
    // Access with X-axis iterator
    for (auto&& e : vx) { Serial << int(e) << ','; }
    
    // Since it is an iterator of int16_t elements, 
    //the STL algorithm (max-min) can be used.
    auto&& minmax = std::minmax_element(vx.begin(), vx.end());
    class iter_smplque {
    	typedef smplque<T, alloc, INTCTL> BODY;
    
    private:
    	uint16_t _pos; // index
    	BODY* _body;   // point to original object
    
    public: // for <iterator>
    	typedef iter_smplque self_type;
    	typedef T value_type;
    	typedef T& reference;
    	typedef T* pointer;
    	typedef std::forward_iterator_tag iterator_category;
    	typedef int difference_type;
    
    public: // pick some methods
    	inline reference operator *() {
    		return (*_body)[_pos];
    	}
    	
    	inline self_type& operator ++() {
    		_pos++;
    		return *this;
    	}
    };
    struct axis_xyzt {
        int16_t x, y, z;
        uint16_t t;
        int16_t& get_x() { return x; }
        int16_t& get_y() { return y; }
        int16_t& get_z() { return z; }
    };
    
    template <class Iter, typename T, typename R, R& (T::*get)()>
    class _iter_axis_xyzt {
        Iter _p;
        
    public:
        inline self_type& operator ++() {
            _p++;
            return *this; }
    
        inline reference operator *() {
            return (*_p.*get)(); }
    };
    
    template <class Ixyz, class Cnt>
    class _axis_xyzt_iter_gen {
        Cnt& _c;
        
    public:
        _axis_xyzt_iter_gen(Cnt& c) : _c(c) {}
        Ixyz begin() { return Ixyz(_c.begin()); }
        Ixyz end() { return Ixyz(_c.end()); }
    };
    
    // It's long, so shorten it with using
    template <typename T, int16_t& (axis_xyzt::*get)()>
    using _axis_xyzt_axis_ret = _axis_xyzt_iter_gen<
        _iter_axis_xyzt<typename T::iterator, axis_xyzt, int16_t, get>, T>;
    
    // Generator to extract X axis
    template <typename T>
    _axis_xyzt_axis_ret<T, &axis_xyzt::get_x>
    get_axis_x(T& c) {
        return _axis_xyzt_axis_ret<T, &axis_xyzt::get_x>(c);
    }
    class my_app_def {
    public: // Define required methods
    	void network_event(twe::packet_ev_nwk& pEvNwk) {}
    	void receive(twe::packet_rx& rx) {}
    	void transmit_complete(twe::packet_ev_tx& pEvTx) {}
    	void loop() {}
    	void on_sleep(uint32_t& val) {}
    	void warmboot(uint32_t& val) {}
    	void wakeup(uint32_t& val) {}
    	
    public: // It is cumbersome to make these descriptions mandatory.
      // DIO interrupt handler: There are 20 types.
      // DIO event handler: There are 20 types.
      // Timer interrupt handler: There are five types
      // Timer event handlers: there are 5 types
      // ...
    }
    // hpp file
    class my_app_def : class app_defs<my_app_def>, ... {
      // Empty handler
      template<int N> void int_dio_handler(uint32_t arg, uint8_t& handled) { ; }
    
      ...   
      // Implement only number 12.
      
    public:
      // Callback function called from TWENET
      uint8 cbTweNet_u8HwInt(uint32 u32DeviceId, uint32 u32ItemBitmap);
    };
    
    // cpp file
    template <>
    void my_app_def::int_dio_handler<12>(uint32_t arg, uint8_t& handled) {
      digitalWrite(5, LOW);
      handled = true;
      return;
    }
    
    void cbTweNet_u8HwInt(uint32 u32DeviceId, uint32 u32ItemBitmap) {
      uint8_t b_handled = FALSE;
      switch(u32DeviceId) {
      	case E_AHI_DEVICE_SYSCTRL:
          if (u32ItemBitmap & (1UL << 0)){int_dio_handler<0>(0, b_handled);}
          if (u32ItemBitmap & (1UL << 1)){int_dio_handler<1>(1, b_handled);}
          ...
          if (u32ItemBitmap & (1UL << 12)){int_dio_handler<12>(12, b_handled);}
          ...
          if (u32ItemBitmap & (1UL << 19)){int_dio_handler<19>(19, b_handled);}
        break;
      }
    }
      	case E_AHI_DEVICE_SYSCTRL:
          if (u32ItemBitmap & (1UL << 0)){;}
          if (u32ItemBitmap & (1UL << 1)){;}
          ...
          if (u32ItemBitmap & (1UL << 12)){
              int_dio_handler<12>(12, b_handled);}
          ...
          if (u32ItemBitmap & (1UL << 19)){;}
          break;
          
        // ↓ ↓ ↓
        
        // 結局、このように最適化されることが期待できる。
       	case E_AHI_DEVICE_SYSCTRL:
          if (u32ItemBitmap & (1UL << 12)){
            // int_dio_handler<12> もinline展開
            digitalWrite(5, LOW);
            handled = true;
          }
          break;
        
    // mwx_appcore.cpp
    void wakeup() __attribute__((weak));
    void wakeup() { }
    template <class D>
    class stream {
    protected:
    	void* pvOutputContext; // TWE_tsFILE*
    public:
      inline D* get_Derived() { return static_cast<D*>(this); }
    	inline D& operator << (char c) {
    		get_Derived()->write(c);
    		return *get_Derived();
    	}
    };
    
    class serial_jen : public mwx::stream<serial_jen> {
    public:
     	inline size_t write(int n) {
    		return (int)SERIAL_bTxChar(_serdef._u8Port, n);
    	}
    };
    template <class D>
    class stream {
    protected:
    	void* pvOutputContext; // TWE_tsFILE*
    public:
    	inline tfcOutput get_pfcOutout() { return get_Derived()->vOutput; }
    	
    	inline D& operator << (int i) {
    		(size_t)fctprintf(get_pfcOutout(), pvOutputContext, "%d", i);
    		return *get_Derived();
    	}
    };
    
    class serial_jen : public mwx::stream<serial_jen> {
    	using SUPER = mwx::stream<serial_jen>;
    	TWE_tsFILE* _psSer; // Low-level structure for serial output
    public:
      void begin() {
        SUPER::pvOutputContext = (void*)_psSer;
      }
      
    	static void vOutput(char out, void* vp) {
    		TWE_tsFILE* fp = (TWE_tsFILE*)vp;
    		fp->fp_putc(out, fp);
    	}
    };
    if (auto&& wrt = Wire.get_writer(SHTC3_ADDRESS)) {
    	Serial << "{I2C SHTC3 connected.";
    	wrt << SHTC3_TRIG_H;
    	wrt << SHTC3_TRIG_L;
    	Serial << " end}";
    }
    class periph_twowire {
    public:
    	class writer : public mwx::stream<writer> {
    		friend class mwx::stream<writer>;
    		periph_twowire& _wire;
    	
    	public:
    		writer(periph_twowire& ref, uint8_t devid) : _wire(ref) {
    	  	_wire.beginTransmission(devid); // Start communication with constructor
    		}
    	
    		~writer() {
    			_wire.endTransmission(); // Communication terminated by destructor
    		}
    	
    		operator bool() {
    			return (_wire._mode == periph_twowire::MODE_TX);
    		}
    	
    	private: // stream interface
    		inline size_t write(int n) {
    			return _wire.write(val);
    		}
    	
    		// for upper class use
    		static void vOutput(char out, void* vp) {
    			periph_twowire* p_wire = (periph_twowire*)vp;
    			if (p_wire != nullptr) {
    				p_wire->write(uint8_t(out));
    			}
    		}
    	};
    	
    public:
    	writer get_writer(uint8_t address) {
    		return writer(*this, address);
    	}
    };
    class periphe_twowire Wire; // global instance
    
    // ユーザコード
    if (auto&& wrt = Wire.get_writer(SHTC3_ADDRESS)) {
    	Serial << "{I2C SHTC3 connected.";
    	wrt << SHTC3_TRIG_H;
    	wrt << SHTC3_TRIG_L;
    	Serial << " end}";
    }
    			writer& operator << (int v) {
    				_wire.write(uint8_t(v & 0xFF));
    				return *this;
    			}

    BRD_APPTWELITE

    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.

    circle-check

    This ACT includes:

    • Transmission and reception of wireless packets

    • Interactive settings mode -

    • Digital (button) input -

    • analog input -

    circle-exclamation

    This sample cannot communicate with App_TweLite.

    hashtag
    functions of ACT

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

    hashtag
    How to use ACT

    hashtag
    Required TWELITE and wiring example

    Role
    Example

    hashtag
    Explanation of ACT

    hashtag
    include part

    Include <TWELITE> in all ACTs. Here is a simple network and board support <BRD_APPTWELITE> should be included.

    It also includes <STG_STD> to add Interactive settings mode.

    hashtag
    declaration part

    • sample-act common declaration

    • Its prototype declarations (send and receive), since the longer process is functionalized

    • Variables for holding data in the application

    hashtag
    setup()

    The general flow of the program is the initial setup of each section and the start of each section.

    hashtag
    Registration of various BEHAVIOR objects

    Register BEHAVIOR objects to determine system behavior. It can be an Interactive settings mode, board support, or a network description of wireless packets.

    circle-exclamation

    It will not work unless registered within setup().

    hashtag
    Configure the Interactive settings mode

    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

    circle-info

    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.

    circle-info

    Option Bits are not used in this sampler, although they are displayed in the menu.

    hashtag
    the_twelite

    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 .

    • TWENET::rx_when_idle() Specification to open the receive circuit.

    circle-info

    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.

    hashtag
    Analogue

    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.

    circle-info

    Except for the first time, the ADC is started in the interrupt handler.

    hashtag
    Buttons

    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.

    circle-info

    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.

    hashtag
    Timer0

    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.

    hashtag
    Start of the operation of the_twelite

    Execute the_twelite.begin() at the end of the setup() function.

    hashtag
    Serial

    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.

    circle-info

    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.

    hashtag
    ループ loop()

    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.

    circle-exclamation

    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.

    hashtag
    Buttons

    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.

    circle-info

    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.

    hashtag
    Analogue

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

    circle-exclamation

    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

    hashtag
    Timer0

    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.

    hashtag
    transmit()

    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.

    hashtag

    hashtag
    function prototype

    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.

    hashtag
    Obtaining network and packet objects

    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.

    hashtag
    Display suppression during Interactive settings mode screen display

    Suppresses screen output when Interactive settings mode screen is displayed.

    hashtag
    Packet transmission settings

    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.

    hashtag
    Data payload of the packet

    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.

    circle-info

    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.

    circle-info

    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.

    circle-info

    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.

    hashtag
    on_rx_packet()

    When a wireless packet is received, on_rx_packet() is called as a receive event.

    circle-info

    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.

    circle-info

    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.

    circle-info

    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.

    circle-info

    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.

    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.

  • reads configuration values saved by set.reload().

  • Copy the values of OPT_BITS and LID into the variables

  • , 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.
  • 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.

  • Parent Node

    At a minimum, wire M1=GND, DI1:button, DO1:LED.

    Child Node

    At a minimum, wire M1=open, DI1:button, DO1:LED.

    <STG_STD>
    Buttons
    Analogue
    <NWK_SIMPLE>
    <STG_STD>
    Wiring example (AI1-AI4 can be omitted)
    // use twelite mwx c++ template library
    #include <TWELITE>
    #include <NWK_SIMPLE>
    #include <BRD_APPTWELITE>
    #include <STG_STD>
    /*** Config part */
    // application ID
    const uint32_t DEFAULT_APP_ID = 0x1234abcd;
    // channel
    const uint8_t DEFAULT_CHANNEL = 13;
    // option bits
    uint32_t OPT_BITS = 0;
    // logical id
    uint8_t LID = 0;
    
    /*** function prototype */
    MWX_APIRET transmit();
    void receive();
    
    /*** application defs */
    const char APP_FOURCHAR[] = "BAT1";
    
    // sensor values
    uint16_t au16AI[5];
    uint8_t u8DI_BM;
    void setup() {
    	/*** SETUP section */	
    	// init vars
    	for(auto&& x : au16AI) x = 0xFFFF;
    	u8DI_BM = 0xFF;
    
    	// load board and settings
    	auto&& set = the_twelite.settings.use<STG_STD>();
    	auto&& brd = the_twelite.board.use<BRD_APPTWELITE>();
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    
    	// settings: configure items
    	set << SETTINGS::appname("BRD_APPTWELITE");
    	set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
    	set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
    	set.hide_items(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);
    	set.reload(); // load from EEPROM.
    	OPT_BITS = set.u32opt1(); // this value is not used in this example.
    	LID = set.u8devid(); // logical ID
    
    	// the twelite main class
    	the_twelite
    		<< set                      // apply settings (appid, ch, power)
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    
    	if (brd.get_M1()) { LID = 0; }
    
    	// Register Network
    	nwk << set // apply settings (LID and retry)
    			;
    
    	// if M1 pin is set, force parent device (LID=0)
    	nwk << NWK_SIMPLE::logical_id(LID); // write logical id again.
    	
    	/*** BEGIN section */
    	// start ADC capture
    	Analogue.setup(true, ANALOGUE::KICK_BY_TIMER0); // setup analogue read (check every 16ms)
    	Analogue.begin(pack_bits(
    						BRD_APPTWELITE::PIN_AI1,
    						BRD_APPTWELITE::PIN_AI2,
    						BRD_APPTWELITE::PIN_AI3,
    						BRD_APPTWELITE::PIN_AI4,
    				   		PIN_ANALOGUE::VCC)); // _start continuous adc capture.
    
    	// Timer setup
    	Timer0.begin(32, true); // 32hz timer
    
    	// start button check
    	Buttons.setup(5); // init button manager with 5 history table.
    	Buttons.begin(pack_bits(
    						BRD_APPTWELITE::PIN_DI1,
    						BRD_APPTWELITE::PIN_DI2,
    						BRD_APPTWELITE::PIN_DI3,
    						BRD_APPTWELITE::PIN_DI4),
    					5, 		// history count
    					4);  	// tick delta (change is detected by 5*4=20ms consequtive same values)	
    
    
    	the_twelite.begin(); // start twelite!
    
    	/*** INIT message */
    	Serial 	<< "--- BRD_APPTWELITE ---" << mwx::crlf;
    	Serial	<< format("-- app:x%08x/ch:%d/lid:%d"
    					, the_twelite.get_appid()
    					, the_twelite.get_channel()
    					, nwk.get_config().u8Lid
    				)
    			<< mwx::crlf;
    	Serial 	<< format("-- pw:%d/retry:%d/opt:x%08x"
    					, the_twelite.get_tx_power()
    					, nwk.get_config().u8RetryDefault
    					, OPT_BITS
    			)
    			<< mwx::crlf;
    }
    	auto&& set = the_twelite.settings.use<STG_STD>();
    	auto&& brd = the_twelite.board.use<BRD_APPTWELITE>();
    	auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    // Initialize Interactive settings mode
    auto&& set = the_twelite.settings.use<STG_STD>();
    
    set << SETTINGS::appname("BRD_APPTWELITE");
    set << SETTINGS::appid_default(DEFAULT_APP_ID); // set default appID
    set << SETTINGS::ch_default(DEFAULT_CHANNEL); // set default channel
    set.hide_items(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);
    set.reload(); // load from EEPROM.
    OPT_BITS = set.u32opt1(); // this value is not used in this example.
    LID = set.u8devid(); // logical ID;
    [CONFIG/BRD_APPTWELITE:0/SID=8XXYYYYY]
    a: (0x1234ABCD) Application ID [HEX:32bit]
    i: (        13) Device ID [1-100,etc]
    c: (        13) Channel [11-26]
    x: (      0x03) RF Power/Retry [HEX:8bit]
    o: (0x00000000) Option Bits [HEX:32bit]
    
     [ESC]:Back [!]:Reset System [M]:Extr Menu    
    auto&& brd = the_twelite.board.use<BRD_APPTWELITE>();
    	if (brd.get_M1()) { LID = 0; }
    	// the twelite main class
    	the_twelite
    		<< set
    		<< TWENET::rx_when_idle();  // open receive circuit (if not set, it can't listen packts from others)
    // The following statements are not available in the MWX library
    #include <iostream>
    std::cout << "hello world" << std::endl;
    auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
    nwk << set;
    nwk << NWK_SIMPLE::logical_id(LID);
    Analogue.setup(true, ANALOGUE::KICK_BY_TIMER0);
    	Analogue.begin(pack_bits(
    						BRD_APPTWELITE::PIN_AI1,
    						BRD_APPTWELITE::PIN_AI2,
    						BRD_APPTWELITE::PIN_AI3,
    						BRD_APPTWELITE::PIN_AI4,
    				   	PIN_ANALOGUE::VCC));
    Buttons.setup(5);
    Buttons.begin(pack_bits(
    						BRD_APPTWELITE::PIN_DI1,
    						BRD_APPTWELITE::PIN_DI2,
    						BRD_APPTWELITE::PIN_DI3,
    						BRD_APPTWELITE::PIN_DI4),
    					5, 		// history count
    					4);  	// tick delta
    Timer0.begin(32, true); // 32hz timer
    the_twelite.begin(); // start twelite!
    	Serial 	<< "--- BRD_APPTWELITE ---" << mwx::crlf;
    	Serial	<< format("-- app:x%08x/ch:%d/lid:%d"
    					, the_twelite.get_appid()
    					, the_twelite.get_channel()
    					, nwk.get_config().u8Lid
    				)
    			<< mwx::crlf;
    	Serial 	<< format("-- pw:%d/retry:%d/opt:x%08x"
    					, the_twelite.get_tx_power()
    					, nwk.get_config().u8RetryDefault
    					, OPT_BITS
    			)
    			<< mwx::crlf;
    /*** loop procedure (called every event) */
    void loop() {
    	if (Buttons.available()) {
    		uint32_t bp, bc;
    		Buttons.read(bp, bc);
    
    		u8DI_BM = uint8_t(collect_bits(bp, 
    							BRD_APPTWELITE::PIN_DI4,   // bit3
    							BRD_APPTWELITE::PIN_DI3,   // bit2
    							BRD_APPTWELITE::PIN_DI2,   // bit1
    							BRD_APPTWELITE::PIN_DI1)); // bit0
    
    		transmit();
    	}
    
    	if (Analogue.available()) {
    		au16AI[0] = Analogue.read(PIN_ANALOGUE::VCC);
    		au16AI[1] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI1);
    		au16AI[2] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI2);
    		au16AI[3] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI3);
    		au16AI[4] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI4);
    	}
    
    	if (Timer0.available()) {
    		static uint8_t u16ct;
    		u16ct++;
    
    		if (u8DI_BM != 0xFF && au16AI[0] != 0xFFFF) { // finished the first capture
    			if ((u16ct % 32) == 0) { // every 32ticks of Timer0
    				transmit();
    			}
    		}
    	}
    }
    	if (Buttons.available()) {
    		uint32_t bp, bc;
    		Buttons.read(bp, bc);
    u8DI_BM = uint8_t(collect_bits(bp, 
    		BRD_APPTWELITE::PIN_DI4,   // bit3
    		BRD_APPTWELITE::PIN_DI3,   // bit2
    		BRD_APPTWELITE::PIN_DI2,   // bit1
    		BRD_APPTWELITE::PIN_DI1)); // bit0
    
    /* collect_bits performs the following operations
    u8DI_BM = 0;
    if (bp & (1UL << BRD_APPTWELITE::PIN_DI1)) u8DI_BM |= 1;
    if (bp & (1UL << BRD_APPTWELITE::PIN_DI2)) u8DI_BM |= 2;
    if (bp & (1UL << BRD_APPTWELITE::PIN_DI3)) u8DI_BM |= 4;
    if (bp & (1UL << BRD_APPTWELITE::PIN_DI4)) u8DI_BM |= 8;
    */
    transmit();
    if (Analogue.available()) {
    	au16AI[0] = Analogue.read(PIN_ANALOGUE::VCC);
    	au16AI[1] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI1);
    	au16AI[2] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI2);
    	au16AI[3] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI3);
    	au16AI[4] = Analogue.read_raw(BRD_APPTWELITE::PIN_AI4);
    }
    if (Timer0.available()) {
    	static uint8_t u16ct;
    	u16ct++;
    
    	if (u8DI_BM != 0xFF && au16AI[0] != 0xFFFF) { // finished the first capture
    		if ((u16ct % 32) == 0) { // every 32ticks of Timer0
    			transmit();
    		}
    	}
    }
    MWX_APIRET transmit() {
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    	  auto&& set = the_twelite.settings.use<STG_STD>();
    		if (!set.is_screen_opened()) {
    			Serial << "..DI=" << format("%04b ", u8DI_BM);
    			Serial << format("ADC=%04d/%04d/%04d/%04d ", au16AI[1], au16AI[2], au16AI[3], au16AI[4]);
    			Serial << "Vcc=" << format("%04d ", au16AI[0]);
    			Serial << " --> transmit" << mwx::crlf;
    		}
    
    		// set tx packet behavior
    		pkt << tx_addr(u8devid == 0 ? 0xFE : 0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    			<< tx_retry(0x1) // set retry (0x1 send two times in total)
    			<< tx_packet_delay(0,50,10); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
    
    		// prepare packet payload
    		pack_bytes(pkt.get_payload() // set payload data objects.
    			, make_pair(APP_FOURCHAR, 4) // string should be paired with length explicitly.
    			, uint8_t(u8DI_BM)
    		);
    
    		for (auto&& x : au16AI) {
    			pack_bytes(pkt.get_payload(), uint16_t(x)); // adc values
    		}
    		
    		// do transmit 
    		return pkt.transmit();
    	}
    	return MWX_APIRET(false, 0);
    }
    MWX_APIRET transmit()
    	if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
    auto&& set = the_twelite.settings.use<STG_STD>();
    if (!set.is_screen_opened()) {
        //Not during Interactive settings mode screen!
    }
    pkt << tx_addr(u8devid == 0 ? 0xFE : 0x00)  // 0..0xFF (LID 0:parent, FE:child w/ no id, FF:LID broad cast), 0x8XXXXXXX (long address)
    		<< tx_retry(0x1) // set retry (0x3 send four times in total)
    		<< tx_packet_delay(0,50,10); // send packet w/ delay (send first packet with randomized delay from 100 to 200ms, repeat every 20ms)
    # Index of first byte: Data type : Number of bytes : Contents
    
    00: uint8_t[4] : 4 : four-character identifier
    04: uint8_t    : 1 : Bitmap of DI1..4
    06: uint16_t   : 2 : Voltage value of Vcc
    08: uint16_t   : 2 : ADC value of AD1 (0..1023)
    10: uint16_t   : 2 : ADC value of AD2 (0..1023)
    12: uint16_t   : 2 : ADC value of AD3 (0..1023)
    14: uint16_t   : 2 : ADC value of AD4 (0..1023)
    auto&& payl = pkt.get_payload();
    payl.reserve(16); // Resize to 16 bytes
    payl[00] = APP_FOURCHAR[0];
    payl[01] = APP_FOURCHAR[1];
    ...
    payl[08] = (au16AI[0] & 0xFF00) >> 8; //Vcc
    payl[09] = (au16AI[0] & 0xFF);
    ...
    payl[14] = (au16AI[4] & 0xFF00) >> 8; // AI4
    payl[15] = (au16AI[4] & 0xFF);
    // prepare packet payload
    pack_bytes(pkt.get_payload() // set payload data objects.
    	, make_pair(APP_FOURCHAR, 4) // string should be paired with length explicitly.
    	, uint8_t(u8DI_BM)
    );
    
    for (auto&& x : au16AI) {
    	pack_bytes(pkt.get_payload(), uint16_t(x)); // adc values
    }
    for(int i = 0; i < sizeof(au16AI)/sizeof(uint16_t)); i++) {
      pack_bytes(pkt.get_payload(), au16AI[i]);
    }
    return pkt.transmit();
    void on_rx_packet(packet_rx& rx, bool_t &handled) {	
    	auto&& set = the_twelite.settings.use<STG_STD>();
    
    	Serial << format("..receive(%08x/%d) : ", rx.get_addr_src_long(), rx.get_addr_src_lid());
    
      // expand the packet payload
    	char fourchars[5]{};
    	auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
    		, make_pair((uint8_t*)fourchars, 4)  // 4bytes of msg
        );
    
    	// check header
    	if (strncmp(APP_FOURCHAR, fourchars, 4)) { return; }
    
    	// read rest of payload
    	uint8_t u8DI_BM_remote = 0xff;
    	uint16_t au16AI_remote[5];
    	expand_bytes(np, rx.get_payload().end()
    		, u8DI_BM_remote
    		, au16AI_remote[0]
    		, au16AI_remote[1]
    		, au16AI_remote[2]
    		, au16AI_remote[3]
    		, au16AI_remote[4]
    	);
    
    	Serial << format("DI:%04b", u8DI_BM_remote & 0x0F);
    	for (auto&& x : au16AI_remote) {
    		Serial << format("/%04d", x);
    	}
    	Serial << mwx::crlf;
    
    	// set local DO
    	digitalWrite(BRD_APPTWELITE::PIN_DO1, (u8DI_BM_remote & 1) ? HIGH : LOW);
    	digitalWrite(BRD_APPTWELITE::PIN_DO2, (u8DI_BM_remote & 2) ? HIGH : LOW);
    	digitalWrite(BRD_APPTWELITE::PIN_DO3, (u8DI_BM_remote & 4) ? HIGH : LOW);
    	digitalWrite(BRD_APPTWELITE::PIN_DO4, (u8DI_BM_remote & 8) ? HIGH : LOW);
    
    	// set local PWM : duty is set 0..1024, so 1023 is set 1024.
    	Timer1.change_duty(au16AI_remote[1] == 1023 ? 1024 : au16AI_remote[1]);
    	Timer2.change_duty(au16AI_remote[2] == 1023 ? 1024 : au16AI_remote[2]);
    	Timer3.change_duty(au16AI_remote[3] == 1023 ? 1024 : au16AI_remote[3]);
    	Timer4.change_duty(au16AI_remote[4] == 1023 ? 1024 : au16AI_remote[4]);
    }
    void on_rx_packet(packet_rx& rx, bool_t &handled)
    if (!set.is_screen_opened()) {
       Serial << format("..receive(%08x/%d) : ",
          rx.get_addr_src_long(), rx.get_addr_src_lid());
    }
    char fourchars[5]{};
    auto&& np = expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
    	, make_pair((uint8_t*)fourchars, 4)  // 4bytes of msg
      );
    if (strncmp(APP_FOURCHAR, fourchars, 4)) { return; }
    	// read rest of payload
    	uint8_t u8DI_BM_remote = 0xff;
    	uint16_t au16AI_remote[5];
    	expand_bytes(np, rx.get_payload().end()
    		, u8DI_BM_remote
    		, au16AI_remote[0]
    		, au16AI_remote[1]
    		, au16AI_remote[2]
    		, au16AI_remote[3]
    		, au16AI_remote[4]
    	);
    auto&& set = the_twelite.settings.use<STG_STD>();
    ...
    Serial << format("DI:%04b", u8DI_BM_remote & 0x0F);
    for (auto&& x : au16AI_remote) {
    	Serial << format("/%04d", x);
    }
    Serial << mwx::crlf;
    // set local DO
    digitalWrite(BRD_APPTWELITE::PIN_DO1, (u8DI_BM_remote & 1) ? HIGH : LOW);
    digitalWrite(BRD_APPTWELITE::PIN_DO2, (u8DI_BM_remote & 2) ? HIGH : LOW);
    digitalWrite(BRD_APPTWELITE::PIN_DO3, (u8DI_BM_remote & 4) ? HIGH : LOW);
    digitalWrite(BRD_APPTWELITE::PIN_DO4, (u8DI_BM_remote & 8) ? HIGH : LOW);
    
    // set local PWM : duty is set 0..1024, so 1023 is set 1024.
    Timer1.change_duty(au16AI_remote[1] == 1023 ? 1024 : au16AI_remote[1]);
    Timer2.change_duty(au16AI_remote[2] == 1023 ? 1024 : au16AI_remote[2]);
    Timer3.change_duty(au16AI_remote[3] == 1023 ? 1024 : au16AI_remote[3]);
    Timer4.change_duty(au16AI_remote[4] == 1023 ? 1024 : au16AI_remote[4]);
    TWELITE DIParrow-up-right
    TWELITE DIParrow-up-right

    Wire (using helper class)

    accessing Wire (using helper class)

    The helper class version is a more abstract implementation. Creating objects reader, writer for reading and writing is the start of using the bus, and destroying the objects is the end of using the bus.

    By creating the object in the decision expression of the if statement, the validity period of the object is limited to the scope of the if clause, and the object is destroyed when it exits the if clause, at which point the bus usage termination procedure is performed.

    Also, read/write objects implement the mwx::stream interface, so you can use the << operator, etc.

    • Aligning the start and end of bus usage with the validity period of objects improves the visibility of source code and prevents omissions of termination procedures.

    • Unification of read/write procedures by mwx::stream interface

    hashtag
    loading

    Loading process and its termination procedure in scope if() { ... } in scope.

    The above reads one byte at a time using the rdr object generated by the get_readr() method. The parameter of the method is the two-line serial ID to be read. 1.

    1. in if(...) Create a rdrobject in. (The type is resolved with the universal reference auto&& by type inference. 2.) The generated rdr object defines an operator bool (), which is used to evaluate the decision expression. If communication is possible with the specified ID, true is returned. 3. The rdr object defines an int operator () (void) operator, which is called to read one byte of data from the 2-wire serial bus. If the read fails,

    hashtag
    get_reader(addr, read_count=0)

    I2C 読み出しに用いるワーカーオブジェクトを取得します。

    Parameters
    Description

    hashtag
    write (writer)

    Reading with a helper class to perform the writing process and its termination procedure in scope if() { ... } in scope.

    In the above, the wrt object generated by the get_writer() method is used to write one byte at a time. The method parameter is the two-line serial ID to be read.

    1. if(...) Generate a wrtobject in. (The type name is resolved with auto' because it is long. 2.) The generated wrtobject defines anoperator bool (), which is used to evaluate the decision expression. If communication is possible with the specified ID, trueis returned. 3. Thewrtobject defines anint operator () (void)operator, which is called to write one byte of data to the 2-wire serial bus. If it fails,-1` is returned, and if it succeeds, the written byte value is returned. 4.

    hashtag
    get_writer()

    Obtains the worker object used for I2C export.

    Parameters
    Description

    hashtag
    Worker object (writer)

    hashtag
    << operator

    The int and uint8_t types transfer 8 bits.

    hashtag
    () operator

    Write out 1 byte.

    hashtag
    worker object (reader)

    hashtag
    >> operator

    Reads only the size of each data type.

    hashtag
    () operator

    Reads 1 byte. Returns -1 if there is an error, returns the byte value read if normal.

    If b_stop is set to true, the STOP bit is issued on that read.

    hashtag
    Example

    The following example shows a measurement example of the temperature/humidity sensor SHTC3 of the environmental sensor PAL.

    if (auto&& wrt = Wire.get_writer(...)) { // Object creation and device communication determination
       // Within this scope (wave brackets) is the validity period of wrt.
       wrt << 0x00; // Writes 0x00 with mwx::stream interface
    } 
    // wrt is discarded at the point where the if clause is exited, and the use of the bus is terminated
    -1
    is returned, and if it succeeds, the read byte value is returned. 4.
  • if() { ... } The destructor of rdr is called at the end of the scope to STOP the two-wire serial bus.

  • if() { ... } The destructor of wrt is called at the end of the scope to STOP the two-wire serial bus.

  • addr

    I2C address for reading

    read_count

    Number of bytes to read (specifying this value will issue a STOP bit on the last transfer); specifying 0 will result in no STOP bit (some devices may work)

    addr

    I2C address for writing out

    const uint8_t DEV_ADDR = 0x70;
    uint8_t au8data[6];
    if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
    		for (auto&& c: au8data) {
    			c = rdr();
    		}
    }
    
    // same above
    uint16_t u16temp, u16humd;
    uint8_t u8temp_csum, u8humd_csum;
    if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
    		rdr >> u16temp;
    		rdr >> u8temp_csum;
    		rdr >> u16humd;
    		rdr >> u8humd_csum;
    }
    periphe_wire::reader
    get_reader(uint8_t addr, uint8_t read_count = 0)
    const uint8_t DEV_ADDR = 0x70;
    if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
    	wrt(SHTC3_TRIG_H);
    	wrt(SHTC3_TRIG_L);
    }
    
    // same above
    if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
    	wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
    	wrt << SHTC3_TRIG_L;
    }
    periph_wire::writer
    get_writer(uint8_t addr)
    operator << (int c)
    operator << (uint8_t c)
    operator << (uint16_t c) 
    operator << (uint32_t c)
    operator() (uint8_t val)
    operator() (int val)
    operator >> (uint8_t& c)
    operator >> (uint16_t& c)
    operator >> (uint32_t& c)
    operator >> (uint8_t(&c)[N]) // Fixed array of N bytes
    int operator() (bool b_stop = false)
    
    //Example
    uint8_t dat[6];
    if (auto&& rdr = Wire.get_reader(0x70)) {
      for(uint8_t& x : dat) {
        x = rdr();
      }
    }
    Wire.begin();
    // reset (may not necessary...)
    if (auto&& wrt = Wire.get_writer(0x70)) {
    	wrt << 0x80; // SHTC3_SOFT_RST_H
    	wrt << 0x05; // SHTC3_SOFT_RST_L
    }
    
    delay(5); // wait some
    
    // start read
    if (auto&& wrt = Wire.get_writer(0x70)) {
    	wrt << 0x60; // SHTC3_TRIG_H
    	wrt << 0x9C; // SHTC3_TRIG_L
    }
    
    delay(10); // wait some
    
    // read result
    uint16_t u16temp, u16humd;
    uint8_t u8temp_csum, u8humd_csum;
    if (auto&& rdr = Wire.get_reader(0x70, 6)) {
    	rdr >> u16temp;
    	rdr >> u8temp_csum;
    	rdr >> u16humd;
    	rdr >> u8humd_csum;
    }
    
    // checksum 0x31, init=0xFF
    if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
    	Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
    if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
    	Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }
    
    // calc temp/humid (degC x 100, % x 100)
    int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
    int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);
    
    Serial << "temp=" << int(i16Temp) 
    	   << ",humid=" << int(i16Humd) << mwx::crlf;