This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

Class Objects

Objects defined for operating networks and peripherals

Class objects are predefined in the MWX library, including the_twelite for handling TWENET and objects for using peripherals.

Each object must be initialized by calling its .setup() and .begin() methods.

(Only the Serial object that uses UART0 does not require initialization.)

1 - the_twelite

Core class for TWENET usage (mwx::twenet)
The the_twelite object consolidates procedures for using TWENET, including basic wireless settings, sleep procedures, and other operations to control the wireless microcontroller.

Overview

the_twelite performs settings and start the_twelite.begin() within the setup() function. Settings cannot be done outside of setup().

void setup() {
  ...
 	the_twelite
		<< TWENET::appid(APP_ID)
		<< TWENET::channel(CHANNEL)
		<< TWENET::rx_when_idle();
  ...
  the_twelite.begin();
}

In the above example, the application ID setting, communication channel setting, and receiver circuit setting are performed.

Various procedures are included.

// Get the 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 reset
the_twelite.reset_system();

Also, classes that handle wireless networks, board support, and user-written event-driven processing can be registered. By registering these classes, specialized functions can be easily utilized. These classes are referred to as “Behaviors” in this documentation.

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

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

Methods

<< operator (Settings)

The << operator is used to perform initial settings of the the_twelite object.

The following setting class objects are input, and if not set, default values are applied.

TWENET::appid(uint32_t id)

Sets the application ID specified by parameter id. This is mandatory.

Reading the setting is done by uint32_t the_twelite.get_appid().

TWENET::channel(uint8_t ch)

Sets the channel number (11..26) specified by parameter ch.

Reading the setting is done by uint8_t the_twelite.get_channel().

TWENET::tx_power(uint8_t pw)

Sets the output power setting (0..3) specified by parameter pw. The default is (3: no output attenuation).

Reading the setting is done by uint8_t the_twelite.get_tx_power().

TWENET::rx_when_idle(uint8_t bEnable)

If parameter bEnable is 1, the receiver circuit is always active to receive wireless packets from others. The default is 0, meaning mainly transmission only.

Reading the setting is done by uint8_t the_twelite.get_rx_when_idle().

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

Enables the channel manager. If multiple channels are specified, transmission and reception are performed on multiple channels. If 0 is specified for ch2 or ch3, that specification is disabled.

STG_STD

Applies the Interactive Mode settings.

auto&& set = the_twelite.settings.use<STG_STD>();
...
set.reload();       // Load settings
the_twelite << set; // Apply Interactive Mode settings

The applied items are as follows:

  • app_id
  • channel
  • tx_power
  • Retransmission count when using MAC ack

begin()

void begin()

Execute after completing prior settings (<< operator reference) and behavior registration. Usually placed at the very end of the setup() function.

  • the_twelite setup completed
  • Behavior initialization

Example

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

	// some others

	// begin the TWENET!
	the_twelite.begin();
}

change_channel()

inline bool change_channel(uint8_t u8Channel)

Changes the channel setting. If it fails, the channel is not changed and returns false.

get_channel_phys()

uint8_t get_channel_phys()

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

get_hw_serial()

inline uint32_t get_hw_serial()

Gets the module’s serial number.

sleep()

inline void sleep(
        uint32_t u32Periodms,
        bool_t bPeriodic = true,
        bool_t bRamOff = false,
        uint8_t u8Device = TWENET::SLEEP_WAKETIMER_PRIMARY)

Puts the module to sleep.

ParameterDescription
u32PeriodmsSleep duration [ms]
bPeriodicRecalculates next wake-up time based on the last wake-up time.
※ Sometimes the next wake-up timing may be from the current time due to proximity.
bRamoffIf set to true, sleep without retaining RAM (after waking up, reinitialization should start from setup() instead of wakeup())
u8DeviceSpecifies the wake-up timer used for sleep. Specify either TWENET::SLEEP_WAKETIMER_PRIMARY or TWENET::SLEEP_WAKETIMER_SECONDARY.

is_wokeup_by_dio()

bool is_wokeup_by_dio(uint8_t port)

Returns true if the wake-up cause from sleep is the specified digital pin.

is_wokeup_by_wktimer()

bool is_wokeup_by_wktimer()

Returns true if the wake-up cause from sleep is the wake-up timer.

reset_system()

inline void reset_system()

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

stop_watchdog()

inline void stop_watchdog()

Stops the watchdog timer. Stop the timer when performing long polling waits.

restart_watchdog()

inline void restart_watchdog()

Restarts the watchdog timer.

Behaviors

twe_twelite can register three behaviors, and the following class objects are defined to hold them.

  • network : Behavior implementing the network. Usually register <NWK_SIMPLE>.
  • network2 : Behavior implementing the network. Used when you want to process packets rejected by network (due to payload data structure or other criteria) with another network behavior. (Reference: Using NWK_LAYERED and NWK_SIMPLE together)
  • board: Behavior for board support. Adds procedures for using devices on the board.
  • app: Behavior describing user applications. Allows writing interrupt or event descriptions and state transitions using state machines. Also allows defining multiple application descriptions and easily selecting completely different applications at startup.
  • settings: Behavior for executing settings (Interactive Mode). Register <SET_STD>.

use<B>()

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

Registers behavior <B>. Registration is done inside setup(). The return value is a reference to the object corresponding to the registered behavior.

After registration, the object can be retrieved with the same syntax as during registration.

Class Objects

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

tx_status

Notifies the transmission completion status.

is_complete()

bool is_complete(uint8_t cbid)

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

is_success()

bool is_success(uint8_t cbid)

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

receiver

Obtains received packets.

available()

bool available()

Returns true if there is a received packet not yet read.

read()

packet_rx& read()

Reads a packet.

2 - Analogue

ADC (mwx::periph_analogue.hpp)
Analogue performs ADC execution and value acquisition. Multiple channels can be continuously acquired at once, and this can be sequentially executed according to the cycle of a timer or other periodic event.

Constants

Pin Definitions

ConstantTypeStandard App Pin Name
uint8_t PIN_ANALOGUE::A1 = 0ADC1 PinAI1
uint8_t PIN_ANALOGUE::A2 = 1ADC2 PinAI3
uint8_t PIN_ANALOGUE::A3 = 2``uint8_t PIN_ANALOGUE::D0 = 2ADC3 Pin (DIO0) *1AI2
uint8_t PIN_ANALOGUE::A4 = 3``uint8_t PIN_ANALOGUE::D1 = 3ADC4 Pin (DIO1) *1AI4
uint8_t PIN_ANALOGUE::VCC = 4Vcc Power Supply Voltage

Methods

setup()

void setup(
        bool bWaitInit = false,
        uint8_t kick_ev = E_AHI_DEVICE_TICK_TIMER,
        void (*fp_on_finish)() = nullptr)

Initializes the ADC. In setup(), it starts the internal semiconductor regulator, specifies the timer device for periodic execution, and specifies a callback function called when all specified ADC channels have finished.

ParameterDescription
bWaitInitIf true, waits for the internal semiconductor regulator initialization.
kick_evSpecifies the timer device for periodic execution. The following five devices can be specified. Except for the first time, ADC starts in the interrupt handler. E_AHI_DEVICE_TICK_TIMER (TickTimer)``E_AHI_DEVICE_TIMER0 .. 4 (Timer0 .. 4)
fp_on_finishCallback function called from the interrupt handler after all specified ports’ ADCs finish. Useful for separately storing ADC measurement values in FIFO queues, etc.

begin()

void begin(uint8_t bmPorts, uint8_t capt_tick = 1)

The first parameter specifies the ports for ADC. The port specification is a bitmap with bits set corresponding to the port numbers defined in the pin definitions. For example, to get values of PIN_ANALOGUE::A2 and PIN_ANALOGUE::VCC, specify (1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC ). You can also write pack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC) using pack_bits.

After calling begin(), the first ADC process starts promptly, and after its completion interrupt, the next pin process starts. When all processes finish, the callback function (if specified) is called. The next ADC process starts after waiting for the next timer interrupt.

The second parameter specifies the number of timer interrupts before starting ADC. For example, TickTimer is called every 1ms, and specifying 16 means processing every 16ms.

void begin()

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

end()

void end()

Stops ADC processing and stops the internal semiconductor regulator.

available()

inline bool available()

Returns true after ADC values are obtained. After checking with this function, it returns false until the next ADC completion.

read(), read_raw()

inline int16_t read(uint8_t s)
inline int16_t read_raw(uint8_t s)

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

ADC Interrupt Handler

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

Specifying a handler separately in the semiconductor peripheral library will cause malfunction.

Behavior During Sleep

If ADC is in periodic execution state by begin(), ADC processing resumes after wake-up from sleep.

3 - Buttons

Digital Input Management Class (mwx::periph_buttons)
Detects changes in digital input. This class detects changes when the same detected value is obtained multiple times. It is effective in reducing the effects of mechanical button chatter.

Methods

setup()

void setup(uint8_t max_history);

The parameter max_history is the maximum number of reference counts that can be set in begin(). Memory allocation and initialization are performed here.

begin()

void begin(uint32_t bmPortMask,
				   uint8_t u8HistoryCount,
				   uint16_t tick_delta);

Starts the operation of Buttons. 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. Multiple bits can be specified. The second parameter u8HistoryCount is the number of times required to confirm a value. The third parameter tick_delta specifies the interval for checking the value in milliseconds.

The confirmation of the value takes u8HistoryCount * tick_delta [ms]. For example, if u8HistoryCount=5 and tick_delta=4, it takes at least about 20 ms to confirm the state.

The check is performed in the event handler of TickTimer. Since it is not an interrupt handler, it is affected by processing delays, but it is sufficient for suppressing chatter of mechanical buttons and the like.

end()

void end()

Stops the operation of Buttons.

available()

inline bool available()

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

read()

bool read(uint32_t& u32port, uint32_t& u32changed)

Called when available is true. u32port is the bitmap of the current input DIO, and u32changed is the bitmap of DIOs where changes were detected.

Returns false if Buttons is not operating.

Operation Details

Initial Value Confirmation

At the time when Buttons starts operating, the input state of DIO is not confirmed. It becomes available when the value is confirmed. At this time, the MSB (bit 31) of the bitmap read by read() is set to 1.

Since operation confirmation is required, it cannot be used for monitoring ports where the input value changes constantly.

Sleep

If Buttons is running before sleep, it will resume after waking up. After resuming, the initial confirmation is performed.

4 - EEPROM

Perform read and write operations on the built-in EEPROM

Performs read and write operations on the built-in EEPROM of the TWELITE wireless microcontroller.

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

The beginning part is used for Settings (Interactive Mode), so when used together, it is recommended to use the latter half of the addresses. The amount of space consumed by the settings (Interactive Mode) depends on its implementation. Even with minimal settings, the first 256 bytes are used, so it is recommended to use addresses after that.

Methods

read()

uint8_t read(uint16_t address)

Reads data corresponding to address from the EEPROM.

write()

void write(uint16_t address, uint8_t value)

Writes value to address in the EEPROM.

update()

void update(uint16_t address, uint8_t value)

Performs writing similar to write(), but first reads the data at address and writes only if it is different from value. This is used to reduce the number of write cycles considering the EEPROM’s rewrite lifespan.

get_stream_helper()

auto&& get_stream_helper()
// The return type is long, so it is abbreviated as auto&&.

Obtains a helper object to perform read and write operations using the later described mwx::stream.

Input/output using the mwx::stream interface

Through the stream_helper helper object, use operators and methods of mwx::stream. Using mwx::stream allows reading and writing of integer types such as uint16_t and uint32_t, fixed-length arrays of uint8_t, and formatted output via format() objects.

auto&& strm = EEPROM.get_stream_helper();
// The helper object's type name is long, so auto&& is used to resolve it.

You can use the << operator and other interfaces defined in mwx::stream with this object.

strm.seek(1024); // Move to byte 1024

strm << format("%08x", 12345678); // Record 12345678 as an 8-character hexadecimal
strm << uint32_t(0x12ab34cd);     // Record 4 bytes of 0x12ab34cd
uint8_t msg_hello[16] = "HELLO WORLD!";
strm << msg_hello;                // Record byte sequence "HELLO WORLD!" (no terminator)

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

Using .seek(), the EEPROM address is moved to 1024.

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

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]; // Number of characters in "HELLO WORLD!"
strm >> msg2;
Serial << crlf << "MSG2=" << msg2;
// MSG2=HELLO WORLD!

Using .seek(), the EEPROM address is moved to 1024.

Reads the previously written data sequence. Reads an 8-byte string, a 4-byte integer, and a 16-byte string in order using the >> operator.

5 - PulseCounter

Pulse Counter (mwx::periph_pulse_counter)
The pulse counter is a circuit that counts pulses even when the microcontroller’s CPU is not operating.

There are two pulse counter systems. PC0 is assigned to PulseCounter0, and PC1 is assigned to PulseCounter1. The alias PulseCounter refers to PulseCounter1.

Methods

begin()

void begin(uint16_t refct = 0,
           E_PIN_INT_MODE edge = PIN_INT_MODE::FALLING,
           uint8_t debounce = 0)

Initializes the object and starts counting. The first parameter, refct, is the reference count used to determine whether an interrupt or availability condition is met. When the count exceeds this number, it is reported to the application. A value of 0 may also be specified, in which case it will not trigger a wake-up from sleep.

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

The third parameter, debounce, accepts values 0, 1, 2, or 3. Settings 1 through 3 reduce the effect of noise by requiring consecutive identical values for detection.

SettingConsecutive SamplesMax Detection Frequency
0-100kHz
123.7kHz
242.2kHz
381.2kHz

end()

void end()

Stops pulse detection.

available()

inline bool available()

If the specified count (refct in begin()) is 0, returns true when the count is 1 or more.

If the specified count (refct in begin()) is 1 or more, returns true when the count exceeds the specified threshold.

read()

uint16_t read()

Reads the count value. The value is reset to 0 after reading.

6 - Serial

UART0 port (mwx::serial_jen)
Implements mwx::stream and performs input/output on TWELITE’s UART0.
  • The Serial object is initialized at system startup with UART0 at 115200 bps, and the initialization process is handled within the library. It can be used from setup() in user code.
  • The Serial1 object is provided within the library but is not initialized. To enable UART1, perform the necessary initialization procedures Serial1.setup(), Serial1.begin().

setup()

void setup(uint16_t buf_tx, uint16_t buf_rx)

Initializes the object.

  • Allocates memory for TX/RX FIFO buffers
  • Allocates memory for the TWE_tsFILE structure
ParameterDescription
buf_txFIFO buffer size for TX
buf_rxFIFO buffer size for RX

begin()

void begin(unsigned long speed = 115200, uint8_t config = 0x06)

Initializes the hardware.

ParameterDescription
speedSpecifies the UART baud rate
configWhen the serial_jen::E_CONF::PORT_ALT bit is specified, UART1 is initialized on ~DIO14,15 instead of ~DIO11(TxD),9(RxD). If not specified, it initializes on ~DIO11(TxD),9(RxD) instead of ~DIO14(TxD),15(RxD).

end()

(Not implemented) Stops using the hardware.

get_tsFile()

TWE_tsFILE* get_tsFile();

Returns the structure in TWE_tsFILE* format used by the C library.

7 - SerialParser

Formatted input for serial port (mwx::serial_parser)
This built-in class is defined as an embedded object intended for use in formatted input via the serial port.

It is defined as the type mwx::serial_parser<mwx::alloc_heap<uint8_t>>, which allocates internal buffer space from the heap during initialization (begin()).

For details, refer to the class serparser.

8 - SPI

Performs reading and writing on the SPI bus (controller side).
Performs reading and writing on the SPI bus (controller side).

Notes

Constants

ConstantMeaning
const uint8_t
SPI_CONF::MSBFIRST
Set MSB as the first bit
const uint8_t
SPI_CONF::LSBFIRST
Set LSB as the first bit
const uint8_t
SPI_CONF::SPI_MODE0
Set to 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

Initialization and Termination

The procedure to use the SPI bus is via the begin() method.

begin()

void begin(uint8_t slave_select, SPISettings settings)
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)

Initializes the hardware.

ParameterDescription
slave_selectSpecifies the select pin of the target peripheral.
0 : DIO19``1 : DIO0 (DIO19 is reserved)``2 : DIO1 (DIO0,19 are reserved)
settingsSpecifies the SPI bus settings.

clock [Hz] specifies the SPI bus frequency. A divisor close to the specified frequency is selected. The value will be 16MHz or an even division of 16MHz.
bitOrder is either SPI_CONF::MSBFIRST or SPI_CONF::LSBFIRST.
dataMode is one of SPI_CONF::SPIMODE0..3.

Example

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

end()

void end()

Terminates the use of SPI hardware.

Reading and Writing

There are two types of procedures for reading and writing. Use either of them as needed.

Example

The following sample code reads the temperature every second from the Analog Devices temperature sensor ADT7310 and outputs it to the serial port.

#include <TWELITE>
#include <SM_SIMPLE>

enum class STATE : uint8_t {
    INTERACTIVE = 255,
    INIT = 0,
    INIT_WAIT,
    SENSOR,
    LOOP_WAIT
};
SM_SIMPLE<STATE> step;

struct SensorData {
    uint8_t highByte;
    uint8_t lowByte;
    uint16_t rawValue;
    int32_t tempValue16th;
    div_result_i32 temperature;
} sensorData;

void setup() {
    step.setup(); // Initialize the state machine
}

void loop() {
    do {
        switch (step.state()) {
        case STATE::INIT: // Initial state
            SPI.begin(0 /* Use DIO19 as chip select */
                      , { 400000UL /* Clock frequency */
                    , SPI_CONF::MSBFIRST
                    , SPI_CONF::SPI_MODE3
            }
                      );

            // Software reset
            SPI.beginTransaction();
            for (int i = 0; i < 4; i++) {
                SPI.transfer(0xFF);
            }
            SPI.endTransaction();

            // Start Continuous Read mode
            SPI.beginTransaction();
            SPI.transfer(0x54);
            SPI.endTransaction();

            step.set_timeout(300); // Set wait time
            step.next(STATE::INIT_WAIT);
            break;

        case STATE::INIT_WAIT: // Wait
            if (step.is_timeout()) {
                step.next(STATE::SENSOR);
            }
            break;

        case STATE::SENSOR: // Read sensor data
            SPI.beginTransaction();
            sensorData.highByte = SPI.transfer(0x00);  // Send dummy data to generate clock signal
            sensorData.lowByte = SPI.transfer(0x00);   // Send dummy data to generate clock signal
            SPI.endTransaction();

            sensorData.rawValue = (((uint16_t)sensorData.highByte << 8) | sensorData.lowByte) >> 3;
            if (sensorData.rawValue & 0x1000) {
                sensorData.tempValue16th = int32_t(sensorData.rawValue) - 0x2000;
            } else {
                sensorData.tempValue16th = sensorData.rawValue;
            }

            // Calculate temperature using div100()
            sensorData.temperature = div100((sensorData.tempValue16th * 100) / 16);

            // Output result to serial
            Serial << crlf << sensorData.temperature.format() << "°C";

            step.set_timeout(1000); // Wait until next capture
            step.next(STATE::LOOP_WAIT);
            break;

        case STATE::LOOP_WAIT: // Wait
            if (step.is_timeout()) {
                step.next(STATE::SENSOR);
            }
            break;

        default:
            break;
        }
    } while (step.b_more_loop());
}

Here, the member function interface is used.

8.1 - SPI (Member Function Version)

SPI (Method-based usage)
After initializing the hardware with the begin() method, use beginTransaction() to enable bus communication. When beginTransaction() is executed, the SPI select pin is asserted. Use the transfer() function for reading and writing. SPI performs read and write operations simultaneously.

beginTransaction()

void beginTransaction()
void beginTransaction(SPISettings settings)

Starts using the SPI bus. Asserts the SPI select pin.

When called with the settings parameter, it also configures the bus settings.

endTransaction()

void endTransaction()

Ends the use of the SPI bus. Deasserts the SPI select pin.

transfer(), transfer16(), transfer32()

inline uint8_t transfer(uint8_t data)
inline uint16_t transfer16(uint16_t data)
inline uint32_t transfer32(uint32_t data)

Performs read/write operations on the SPI bus. transfer() transfers 8 bits, transfer16() transfers 16 bits, and transfer32() transfers 32 bits.

8.2 - SPI (Helper Class Version)

SPI (How to use with helper class)
The helper class version is a more abstract implementation. By creating a read/write object transceiver, you start using the bus, and by destroying the object, you end the use of the bus.

By creating the object inside the conditional expression of an if statement, the lifetime of the object is limited to the scope within the if block, and when exiting the if block, the object is destroyed and the bus release procedure is performed at that point.

uint8_t c;
if (auto&& trs = SPI.get_rwer()) { // Create object and check device communication
   // The scope (curly braces) here is the lifetime of trs.
   trs << 0x00; // Write 0x00 using mwx::stream interface
   trs >> c;    // Store the read data into c.
}
// When exiting the if block, trs is destroyed and the bus usage ends

Also, since the read/write object implements the mwx::stream interface, operators like << can be used.

  • Matching the bus usage start and end with the object’s lifetime improves code readability and prevents omission of release procedures.
  • Unifies read/write procedures using the mwx::stream interface.

Read/Write

A reading method using a helper class that performs read operations and their termination procedures within the scope if() { ... }.

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

Above, the x object created by the get_rwer() method is used to perform byte-wise read/write.

  1. The x object is created inside the if(...). At the same time, the SPI bus select pin is set. (The type is resolved by universal reference auto&& with type inference.)
  2. The created x object defines operator bool () which is used for evaluation in the conditional expression. For SPI bus, it always evaluates to true.
  3. The x object defines the method uint8_t transfer(uint8_t), which performs an 8-bit read/write transfer to SPI when called.
  4. At the end of the if() { ... } scope, the destructor of x is called, releasing the SPI bus select pin.

get_rwer()

periph_spi::transceiver get_rwer()

Obtains a worker object used for SPI bus read/write.

Worker Object

transfer(), transfer16(), transfer32()

uint8_t transfer(uint8_t val)
uint16_t transfer16(uint16_t val)
uint32_t transfer32(uint32_t val)

Perform transfers of 8-bit, 16-bit, and 32-bit respectively, and return the read result with the same data width as the written data.

<< operator

operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)

int and uint8_t types perform 8-bit transfers.

uint16_t and uint32_t types perform 16-bit and 32-bit transfers respectively.

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

>> operator

operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)

null_stream(size_t i = 1)
operator >> (null_stream&& p)

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

If the read result is not needed, use the null_stream() object. It skips the number of bytes specified by i.

9 - TickTimer

System Timer (mwx::periph_ticktimer)
TickTimer is used internally by TWENET for control purposes and operates implicitly. The timer interval is 1 ms. It defines only the available() method for writing processing that occurs every 1 ms in the loop() function based on TickTimer events.

Methods

available()

inline bool available()

Set after the TickTimer interrupt occurs, and becomes true in the immediate loop() execution. It is cleared after the loop() ends.

10 - Timer0 .. 4

Timer, PWM (mwx::periph_timer)
Timers have two functions: generating software interrupts at specified intervals and producing PWM output at specified intervals. The TWELITE wireless module provides five timers, from 0 to 4.

Although the embedded object names are Timer0..4, this page refers to them as TimerX.

Methods

setup()

void setup()

Initializes the timer. This call allocates the required memory area.

begin()

void begin(uint16_t u16Hz, bool b_sw_int = true, bool b_pwm_out = false)

Starts the timer. The first parameter specifies the timer frequency in Hz. If the second parameter is set to true, software interrupts are enabled. If the third parameter is set to true, PWM output is enabled.

end()

void end()

Stops the timer operation.

available()

inline bool available()

Becomes true in the loop() immediately after a timer interrupt occurs, and turns false after the loop() finishes.

change_duty()

void change_duty(uint16_t duty, uint16_t duty_max = 1024)

Sets the duty cycle. The first parameter specifies the duty value (a smaller value brings the average waveform level closer to GND, while a larger value brings it closer to Vcc). The second parameter specifies the maximum duty value.

change_hz()

void change_hz(uint16_t hz, uint16_t mil = 0)

Sets the timer frequency. The second parameter specifies the fractional part (three decimal places) of the frequency as an integer. For example, to set 10.4 Hz, specify hz=10, mil=400.

11 - Wire

Read/write as 2-wire serial (I2C) master (controller) (mwx::periph_wire)
Performs read/write operations as a 2-wire serial (I2C) master (controller).

Definition

Alias Definition

using TwoWire = mwx::periph_twowire<MWX_TWOWIRE_RCVBUFF>;

mwx::periph_wire<MWX_TWOWIRE_RCVBUFF> can be referenced as TwoWire.

Type Definitions

The following typedefs are used for argument and return types:

typedef uint8_t size_type;
typedef uint8_t value_type;

Notes

Initialization and Termination

Creating a Wire instance

The instance and necessary initialization are handled within the library. In user code, call Wire.begin() to start using it.

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

Example: -DMWX_TWOWIRE_BUFF=16

begin()

void begin(
    const size_type u8mode = WIRE_100KHZ,
    bool b_portalt = false)

Initializes the hardware.

ParameterDescription
u8modeSpecifies the bus frequency. Default is 100KHz (WIRE_CONF::WIRE_100KHZ).
You can specify WIRE_CONF::WIRE_??KHZ where ?? can be 50, 66, 80, 100, 133, 160, 200, 266, 320, 400.
b_portaltChanges the hardware pin assignment.

Example

void setup() {
    ...
    Wire.begin();
    ...
}

void wakeup() {
    ...
    Wire.begin();
    ...
}

Reading and Writing

There are two procedures for reading and writing. Use either as needed.

Others

Probe (Device Presence Check)

bool probe(uint8_t address)

Checks if the device specified by address responds. Returns true if the device exists.

setClock()

void setClock(uint32_t speed)

This is intended to change the bus frequency, but it performs no action.

11.1 - Wire (Member Function Version)

Wire (using member functions)

This method using member functions has relatively low abstraction and follows a general API structure similar to that provided by C language libraries. It offers a more intuitive procedure for operating a two-wire serial bus.

However, you need to explicitly manage the start and end of bus usage.

Reading

requestFrom()

size_type requestFrom(
    uint8_t u8address,
    size_type length,
    bool b_send_stop = true)

Reads a specified number of bytes in one operation. The result is stored in a queue, so you should call the .read() method repeatedly until the queue is empty immediately afterward.

ParameterDescription
u8addressI2C address to read from
lengthNumber of bytes to read
b_send_stop=trueIf true, a STOP bit is issued at the end of reading
Return type size_typeNumber of bytes read. 0 indicates failure

Code Example

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.

Writing

The writing process starts with beginTransmission(), then proceeds with the write() method. After the entire write operation is complete, call endTransmission().

#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();

beginTransmission()

void beginTransmission(uint8_t address)

Initializes a write transfer. After the write process, call endTransmission() promptly.

ParameterDescription
u8addressI2C address to write to

write(value)

size_type write(const value_type value)

Writes a single byte.

ParameterDescription
valueByte to write
Return size_typeNumber of bytes written. 0 indicates an error.

write(*value, quantity)

size_type write(
  const value_type* value,
  size_type quantity)

Writes a sequence of bytes.

ParameterDescription
*valueByte sequence to write
size_typeNumber of bytes
Return size_typeNumber of bytes written. 0 indicates an error.

endTransmission()

uint8_t endTransmission(bool sendStop = true)

Finalizes the write operation.

ParameterDescription
sendStop = trueIssues a STOP bit
Return uint8_t0: success, 4: failure

11.2 - Wire (Helper Class Version)

Wire (How to use with helper classes)
The helper class version is a more abstract implementation. Generating objects reader, writer that correspond to reading and writing marks the start of bus usage, and destroying the object performs the bus usage termination procedure.

By creating objects inside the condition expression of an if statement, the object’s lifetime is limited to the scope of the if block. When exiting the if block, the object is destroyed, and at that time, the bus usage termination procedure is performed.

if (auto&& wrt = Wire.get_writer(...)) { // Object creation and device communication check
   // The scope (curly braces) is the lifetime of wrt.
   wrt << 0x00; // Write 0x00 using the mwx::stream interface
}
// wrt is destroyed when exiting the if block, and bus termination is performed

Also, since the read/write objects implement the mwx::stream interface, operators like << can be used.

  • Matching the start and end of bus usage with the object’s lifetime improves code readability and prevents omission of termination procedures.
  • Unified reading and writing procedures via the mwx::stream interface.

Reading

This is a reading method using helper classes to perform reading and its termination procedure within a scope if() { ... }.

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

Above, the rdr object generated by the get_reader() method is used to read one byte at a time. The method parameter specifies the two-wire serial ID to read.

  1. The rdr object is created inside if(...). (The type is resolved as a universal reference auto&& by type inference.)
  2. The created rdr object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
  3. The rdr object defines int operator () (void), which reads one byte from the two-wire serial bus when called. Returns -1 on failure, or the read byte value on success.
  4. The destructor of rdr is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

get_reader(addr, read_count=0)

periphe_wire::reader
get_reader(uint8_t addr, uint8_t read_count = 0)

Obtains a worker object used for I2C reading.

ParameterDescription
addrI2C address for reading
read_countNumber of bytes to read (if specified, a STOP bit is issued at the end of the transfer). If 0 is specified, no STOP bit is issued (some devices may work without it).

Writing (writer)

This is a writing method using helper classes to perform writing and its termination procedure within a scope if() { ... }.

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

Above, the wrt object generated by the get_writer() method is used to write one byte at a time. The method parameter specifies the two-wire serial ID to write.

  1. The wrt object is created inside if(...). (The type is resolved as auto to avoid long type names.)
  2. The created wrt object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
  3. The wrt object defines int operator () (void), which writes one byte to the two-wire serial bus when called. Returns -1 on failure, or the written byte value on success.
  4. The destructor of wrt is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

get_writer()

periph_wire::writer
get_writer(uint8_t addr)

Obtains a worker object used for I2C writing.

ParameterDescription
addrI2C address for writing

Worker Object (writer)

<< operator

operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)

int and uint8_t types transfer 8 bits. Data order is big-endian (higher byte transferred first).

() operator

operator() (uint8_t val)
operator() (int val)

Writes one byte.

Worker Object (reader)

>> operator

operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
operator >> (uint8_t(&c)[N]) // fixed array of N bytes

Reads the size of each data type. Data order is big-endian (first transferred byte is stored in the higher byte).

() operator

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

Reads one byte. Returns -1 on error, or the read byte on success.

If b_stop is set to true, a STOP bit is issued on that read.

Example

The following example is a measurement example for the environmental sensor pal’s temperature and humidity sensor SHTC3.

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;