The TWELITE STICK USB dongle integrates the TWELITE module, antenna, and the functionality of the TWELITE R3 into a single case. As the successor to the MONOSTICK series, it is ideal for linking TWELITE with a PC.
For product specifications, please refer to the data sheet.
TWELITE STICK
USB Dongle
The TWELITE STICK combines the TWELITE module and antenna with the functionality of the TWELITE R series USB adapter.
TWELITE STICK Configuration
It can relay packets from other TWELITE devices to the PC, or from the PC to other TWELITE devices. It can also act as a repeater when connected to a USB power source.
Difference from MONOSTICK
TWELITE STICK is fully compatible with the same packets used in the previous MONOSTICK and functions as its successor.
Its compact size—comparable to that of a typical USB flash drive—makes it less likely to interfere with adjacent USB ports.
Size comparison between MONOSTICK and TWELITE STICK
By using the second-generation GOLD series for the TWELITE module, it achieves approximately 10mA lower current consumption during receive standby compared to the previous RED series, while maintaining equivalent transmission power and slightly improved receive sensitivity.
Current consumption from receive standby to transmission on TWELITE STICK (4 retransmissions, LED disabled)
Current consumption from receive standby to transmission on MONOSTICK RED (4 retransmissions)
Wireless packets of the TWELITE BLUE / RED / GOLD series are mutually compatible and can communicate with each other.
TWELITE APPS
The factory-shipped TWELITE STICK comes pre-installed with the TWELITE APPS Unified Edition. This unified edition takes advantage of the program size of the TWELITE GOLD series to consolidate the functions of previous firmware into a single image.
By using Interactive Mode operations, you can instantly switch between these functions without rewriting the firmware. Some apps change the logo LED color according to their state and blink when receiving packets.
Parent
Repeater
Children
Magenta
Yellow
Cyan
By default, the Parent and Repeater App (magenta LED) runs in parent mode, just like on MONOSTICK.
Simple Wireless Communication
TWELITE can communicate immediately after startup. Pairing like Bluetooth is not required.
Broadcast communication is performed between devices set to the same frequency channel. Therefore, multiple devices cannot transmit simultaneously on the same channel. Packets not addressed to the device are ignored. You can think of it as working like a transceiver or intercom.
TWELITE can transmit without receiving, enabling the realization of devices with excellent power-saving performance.
What is the wireless communication standard?
TWELITE is a wireless module that uses 2.4GHz radio waves and complies with IEEE 802.15.4, which is a different standard from Bluetooth (IEEE 802.15.1). Other products conforming to IEEE 802.15.4 include Zigbee modules, but TWELITE is not a Zigbee module. Zigbee modules use the Zigbee protocol stack on top of IEEE 802.15.4, whereas TWELITE implements a simple, proprietary protocol stack instead.
Although it is not suitable for large-volume data communication, it is optimal for simple signal transmission and similar applications.
Small amounts of data can be transmitted efficiently.
1 - Evaluation and Configuration with TWELITE STAGE APP
How to evaluate communication and change settings using TWELITE STAGE APP
By using the TWELITE STAGE APP included in the TWELITE STAGE SDK development and evaluation environment, you can verify communication and change settings.
Much of the content on this page is also applicable to the MONOSTICK series.
Basic Operation Check
Connect the TWELITE STICK
Connect the TWELITE STICK to the USB port of your PC.
The factory-shipped TWELITE is set to Parent mode of the Parent and Repeater App. It can send and receive data with child devices of the TWELITE series, and the logo mark should light up magenta.
By using a TWELITE STICK in place of a TWELITE DIP parent, you can detect the button state on the PC side or control the LED from the PC.
Connecting the TWELITE STICK
Connect the TWELITE STICK to the USB port of your PC.
If it is set to Parent mode of the Parent and Repeater App, it will light up magenta.
Waiting in Parent Mode
Device Settings
No configuration changes are required. Communication will begin immediately using the factory default settings.
However, for confirmation, let’s check the current configuration.
To change TWELITE settings via UART, start the device in Interactive Mode. TWELITE STAGE APP provides functionality for working with Interactive Mode.
From the main menu, select Interactive Mode.
Main Menu
On the next screen, click anywhere or press the Enter key to continue.
Confirmation Screen
If the following screen appears, the mode has started successfully.
Interactive Mode
If the Channel value, which physically separates networks, is 18, and the Application ID value, which logically separates networks, is 67720102, communication with the factory-shipped TWELITE DIP will be possible.
How to Operate Interactive Mode
Enter the ID shown on the left to edit each item (e.g., enter a to edit the Application ID)
After selecting an item, type the value and press the Enter key to confirm, or press the ESC key to cancel
Press the S key to apply the settings you’ve entered
To reset all settings to default, press the R key to reset, then press the S key to apply
With the TWELITE STICK connected to your PC, press the switch connected to DI1 on the TWELITE DIP.
If operating correctly, the logo will glow red and brighter while the button is pressed.
Logo lights up red when DI1 is pressed
If a switch is connected to DI2, the TWELITE STICK will glow green. If AI1 is disconnected from VCC and a voltage between 0–2V is applied, the brightness will vary depending on the button press on DI1 or DI2.
Standard App Viewer
TWELITE STAGE APP includes a feature to display data received from child devices running the Extremely Simple! Standard App.
Let’s read the status of the switch connected to DI1.
Serial Number: Serial ID of the child device (engraved on the can)
Dx: State of the DIx pins
Ax: Voltage input to the AIx pins (in mV, ranging from 0mV to 2000mV)
Standard App Commander
TWELITE STAGE APP also includes a function to send data to child devices running the Extremely Simple! Standard App. Let’s try controlling the LED connected to DO1.
By using a TWELITE STICK as the parent device for TWELITE ARIA, you can obtain temperature, humidity, and magnetic proximity data on your PC.
Connecting the TWELITE STICK
Connect the TWELITE STICK to the USB port of your PC.
If it is set to Parent mode of the Parent and Repeater App, it will light up magenta.
Waiting in Parent Mode
Device Settings
No configuration changes are needed. Communication will start immediately with the factory default settings.
If the Channel, which physically separates networks, is set to 18, and the Application ID, which logically separates networks, is set to 67720102, then communication with the factory-configured TWELITE ARIA is possible.
Logo Lighting
With TWELITE STICK connected to the PC, bring a magnet close to the Hall sensor on TWELITE ARIA.
If working properly, TWELITE ARIA will send a packet each time a magnet is brought near, and TWELITE STICK’s logo will flash in response.
The logo will flash not only for ARIA packets but for any packets with the same channel and application ID.
Simple Monitor
TWELITE STAGE APP includes a simple monitor feature to display data received from TWELITE ARIA.
2 - Communicating with Child Devices Using Python (Basic)
How to communicate with child devices using Python
By using a dedicated library, you can communicate with TWELITE child devices via TWELITE STICK from Python.
Much of the content on this page also applies to the MONOSTICK series.
Basic Operation Check
Connect the TWELITE STICK
Connect the TWELITE STICK to your PC’s USB port.
The factory-default TWELITE is configured in Parent mode of the Parent and Repeater App. It can send and receive data to and from child devices in the TWELITE series, and the logo should light up magenta.
Factory default state
Installing the MWings Library
Prepare a Python 3.12 or later environment.
Install the MWings module, which interprets the output of the Parent and Repeater App.
pip install mwings
The MWings module allows communication with TWELITE child devices via a TWELITE parent device connected to the host.
Interprets data received from the parent and converts it into a dictionary, JSON, or pandas DataFrame format*
Sends commands generated from dictionaries to the parent device
*The Lite version does not support pandas
For detailed installation instructions and full feature documentation, refer to the MWings for Python Manual.
In modern Python development, managing Python versions and project-level dependencies is essential. The manual introduces how to set up an environment using pyenv and poetry.
This version removes the dependency on pandas, avoiding potential conflicts with numpy. Although it does not support DataFrame output, it retains output functions for dictionaries and JSON strings.
Because of its lightweight nature, it can also be a good option on regular PCs if you do not need pandas functionality.
Confirming Data Reception
Verify that TWELITE STICK can interpret the received data.
This script will print the contents of all types of packets received by the TWELITE STICK in JSON format to the terminal, allowing you to confirm the data.
# -*- coding:utf-8 -*-# Written for Python 3.12# Formatted with Black# MWings example: Receive data, print JSON, typedfrom zoneinfo import ZoneInfo
import mwings as mw
# Main functiondefmain() ->None:
# Create a twelite object twelite = mw.Twelite(mw.utils.ask_user_for_port())
# Use JST for received data twelite.set_timezone(ZoneInfo("Asia/Tokyo"))
# Register event handlers@twelite.on(mw.common.PacketType.APP_ARIA)
defon_app_aria(packet: mw.parsers.app_aria.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_CUE)
defon_app_cue(packet: mw.parsers.app_cue.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_CUE_PAL_EVENT)
defon_app_cue_pal_event(packet: mw.parsers.app_cue_pal_event.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_IO)
defon_app_io(packet: mw.parsers.app_io.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_TWELITE)
defon_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_PAL_AMB)
defon_app_pal_amb(packet: mw.parsers.app_pal_amb.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_PAL_MOT)
defon_app_pal_mot(packet: mw.parsers.app_pal_mot.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_PAL_OPENCLOSE)
defon_app_pal_openclose(packet: mw.parsers.app_pal_openclose.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_UART_ASCII)
defon_app_uart_ascii(packet: mw.parsers.app_uart_ascii.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_UART_ASCII_EXTENDED)
defon_app_uart_ascii_extended(
packet: mw.parsers.app_uart_ascii_extended.ParsedPacket,
) ->None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.ACT)
defon_act(packet: mw.parsers.act.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
# Start receivingtry:
# Set as daemon thread twelite.daemon =True# Start the thread, Join to the main thread twelite.start()
print("Started receiving")
whileTrue:
twelite.join(0.5)
exceptKeyboardInterrupt:
# Stop the thread print("Flushing...")
twelite.stop()
print("Completed")
if __name__ =="__main__":
# Call the main function main()
For example, if you have prepared a factory-default TWELITE DIP running the Extremely Simple! Standard App as a child device, just like in the previous section Using TWELITE STAGE APP, you should see an output like the following. To stop the script, press Ctrl+C.
It is useful as a command-line tool for logging received data over extended periods.
python rx_export_csv_durable.py -h
usage: rx_export_csv_durable.py [-h][-v][-s]Log packets from App_Wings to csv, line by line
options:
-h, --help show this help message and exit
-v, --verbose include system information
-s, --sort sort columns in the output
Sending and Receiving Arbitrary Data
Here, let’s create a script that specifically communicates with a TWELITE DIP child device prepared just like in Using TWELITE STAGE APP.
Example wiring of TWELITE DIP child device
The goal is to display the state of a switch connected to the DI1 pin of the TWELITE DIP and control an LED connected to the DO1 pin.
Receiving Only Standard App Data
Modify rx_print_json.py to create a simplified script that only receives data from the Extremely Simple! Standard App.
# -*- coding:utf-8 -*-from zoneinfo import ZoneInfo
import mwings as mw
# Main functiondefmain() ->None:
# Create a twelite object twelite = mw.Twelite(mw.utils.ask_user_for_port())
# Use JST for received data twelite.set_timezone(ZoneInfo("Asia/Tokyo"))
# Register event handlers@twelite.on(mw.common.PacketType.APP_TWELITE)
defon_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) ->None:
print(packet.to_json(verbose=False, spread=True))
# Start receivingtry:
# Set as daemon thread twelite.daemon =True# Start the thread, Join to the main thread twelite.start()
print("Started receiving")
whileTrue:
twelite.join(0.5)
exceptKeyboardInterrupt:
# Stop the thread print("Flushing...")
twelite.stop()
print("Completed")
if __name__ =="__main__":
# Call the main function main()
Script Mechanism
By calling twelite.start(), a thread for receiving is launched, and twelite.on registers an event handler for each application type (in this case, on_app_twelite()), which is invoked upon each packet reception.
In the previous script, we used the to_json() method to output all data as a JSON string.
Here, we’ll extract only the state of the DI1 pin from parsers.app_twelite.ParsedPacket using the di_state field and display a virtual LED in the terminal.
If pressing the switch connected to the DI pin of TWELITE DIP causes the red light to appear, it indicates success.
Example display
Sending Commands to the Parent Device
In addition to displaying data received by the TWELITE STICK, you can also send data from the TWELITE STICK.
In the script’s main loop, we call threading.Thread.join() every 0.5 seconds so that the receiving thread can also terminate when the main thread ends.
# Start the thread, Join to the main thread twelite.start()
print("Started receiving")
whileTrue:
twelite.join(0.5)
Using this mechanism, let’s control the DO1 pin of the TWELITE DIP from the main loop and try blinking the LED every 0.5 seconds.
Modify the previous script as follows. The full script below includes all changes made so far.
# -*- coding:utf-8 -*-from zoneinfo import ZoneInfo
from typing import Any
import mwings as mw
# Main functiondefmain() ->None:
# Create a twelite object twelite = mw.Twelite(mw.utils.ask_user_for_port())
# Use JST for received data twelite.set_timezone(ZoneInfo("Asia/Tokyo"))
# Register event handlers@twelite.on(mw.common.PacketType.APP_TWELITE)
defon_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) ->None:
print(f"\rDO1 LED: {"🔴"if packet.di_state[0] else"⚪"}", end='', flush=True)
# Initialize command initial: dict[str, Any] = {
"destination_logical_id": 0x78, # All child devices"di_to_change": [True, False, False, False], # Enable DI1"di_state": [False, False, False, False], # Initial state of DIx }
command = mw.serializers.app_twelite.Command(**initial)
# Toggle the DI1 statedeftoggle_di1() ->None:
command.di_state[0] =not command.di_state[0]
twelite.send(command)
# Start receivingtry:
# Set as daemon thread twelite.daemon =True# Start the thread, Join to the main thread twelite.start()
print("Started receiving")
whileTrue:
toggle_di1() # Send twelite.join(0.5) # ReceiveexceptKeyboardInterrupt:
# Stop the thread print("Flushing...")
twelite.stop()
print("Completed")
if __name__ =="__main__":
# Call the main function main()
Script Mechanism
First, the contents of serializers.app_twelite.Command are initialized. Then, a closure toggle_di1() is defined to invert di_state[0] and send it. By calling toggle_di1() from the main loop, the LED is made to blink.
When you run this script, it displays a virtual LED just like before.
Example display
At the same time, the LED connected to the DO1 pin of TWELITE DIP should continuously blink. When you stop the script, the blinking stops.
Packet reception and command transmission can be handled asynchronously.
2.1 - Communicating with Child Devices Using Python (Web Server IoT)
Send data from child devices to a web server using Python
As a practical application, this guide demonstrates how to build an IoT system using a web server.
The content on this page also applies to the MONOSTICK series.
Collecting Temperature and Humidity Data from TWELITE ARIA
Let’s build a simple IoT system that receives temperature and humidity data from the sensor tag TWELITE ARIA, sends it to a web server, and displays it on a graph.
In the basic script for TWELITE DIP, we only performed simple operations on DI1 and DO1. However, in an actual IoT system, it is necessary to send acquired data to an upstream server using methods such as REST APIs.
The former does not require TWELITE R2/R3, while the latter allows smoother operation.
Starting TWELITE ARIA
Insert a CR2032 battery to power up TWELITE ARIA.
Insert CR2032 battery
Writing and Running the Script
Installing Required Modules
Prepare Python 3.12 or later and install the mwings (or mwingslite) and requests modules.
pip install mwings requests
Creating the Script
Create the script stick_aria_thingspeak.py as shown below. Use mwings.parsers.app_aria to receive data and the requests module to send HTTP GET requests to ThingSpeak.
Replace the initial API_KEY with the key you saved earlier.
# -*- coding:utf-8 -*-from zoneinfo import ZoneInfo
from time import perf_counter
import mwings as mw
import requests
API_KEY ="XXXXXXXXXXXXXXXX"# Replace with your ThingSpeak API keyBASE_URL ="https://api.thingspeak.com/update"SEND_MIN_INTERVAL =20# Minimum interval in seconds to send data to ThingSpeak# Main functiondefmain() ->None:
# Create a twelite object twelite = mw.Twelite(mw.utils.ask_user_for_port())
# Use JST for received data twelite.set_timezone(ZoneInfo("Asia/Tokyo"))
# Initialize last send time last_send_time = perf_counter() - SEND_MIN_INTERVAL
# Initialize the target serial ID target_serial_id =-1# Register event handlers@twelite.on(mw.common.PacketType.APP_ARIA)
defon_app_aria(packet: mw.parsers.app_aria.ParsedPacket) ->None:
# Filter packets by serial IDif target_serial_id <0:
# Set the serial ID from the received packet target_serial_id = packet.source_serial_id
print(f"Serial ID set to {target_serial_id:08X}")
elif packet.source_serial_id != target_serial_id:
# Ignore packets from other serial ID devices print(
f"Ignoring packet from serial ID {packet.source_serial_id:08X}, expected {target_serial_id:08X}" )
return# Throttle sending to ThingSpeakif perf_counter() - last_send_time < SEND_MIN_INTERVAL:
print("Skipping send due to minimum interval")
return# Skip sending if within the minimum interval last_send_time = perf_counter() # Update last send time# Send data to ThingSpeak payload = {
"api_key": API_KEY,
"field1": f"{packet.temp_100x /100.0:.2f}", # Temperature"field2": f"{packet.humid_100x /100.0:.2f}", # Humidity"field3": f"{packet.supply_voltage}", # Supply voltage (mV)"field4": f"{packet.lqi}", # Link Quality Indicator }
response = requests.get(BASE_URL, params=payload)
# Check the response statusif response.status_code ==200:
print(f"OK: entry ID = {response.text}")
else:
print(f"NG: status code = {response.status_code}")
# Start receivingtry:
# Set as daemon thread twelite.daemon =True# Start the thread, Join to the main thread twelite.start()
print("Started receiving")
whileTrue:
twelite.join(0.5) # ReceiveexceptKeyboardInterrupt:
# Stop the thread print("Flushing...")
twelite.stop()
print("Completed")
if __name__ =="__main__":
# Call the main function main()
Running the Script
Execute the script.
python stick_aria_thingspeak.py
If data is successfully received from TWELITE ARIA and sent correctly, you will see the following output showing entry IDs in sequence:
Started receiving
Serial ID set to 8201C2DC
OK: entry ID = 1
OK: entry ID = 2
OK: entry ID = 3
OK: entry ID = 4
OK: entry ID = 5
...
Click the “Private View” tab on ThingSpeak. The transmitted data should appear as a graph.
By default, the Y-axis range adjusts dynamically. You can configure the min and max values for each graph via the ✏️ icon.
For example, based on the operating voltage of TWELITE, you can set Y-Axis Min and Y-Axis Max for the Field 3 Chart.
Displaying approximately 2 days of data
You’ve successfully built a simple IoT system integrated with a web server!
Script Overview
Filtering for Sending and Receiving
To prevent overloading the server, data is sent only at intervals defined by SEND_MIN_INTERVAL.
To avoid mixing data from multiple devices, the serial ID of the first device is used as the target.
The data sent to the server is constructed in the following section:
# Send data to ThingSpeakpayload = {
"api_key": API_KEY,
"field1": f"{packet.temp_100x /100.0:.2f}", # Temperature"field2": f"{packet.humid_100x /100.0:.2f}", # Humidity"field3": f"{packet.supply_voltage}", # Supply voltage (mV)"field4": f"{packet.lqi}", # Link Quality Indicator}
response = requests.get(BASE_URL, params=payload)
From mwings.parsers.app_aria, we retrieve the temperature, humidity, coin cell voltage, and signal quality (LQI) expressed as a value between 0 and 255, then convert these to strings to construct the query for the GET request.
In the basic script for TWELITE DIP, we only performed simple operations with DI1 and DO1. However, applications designed for actual monitoring require a user interface like the one found in TWELITE STAGE APP.
Dear PyGui (DPG) is a UI toolkit based on Dear ImGui written in C++ and OpenGL.
It is commonly used in development tool interfaces. While it is not suited for highly customized designs, it features lightweight performance and simple syntax.
Wiring and Starting the TWELITE DIP
In this application, when a signal is received via DIx or AIx of the TWELITE DIP, a signal is simultaneously output to DOx or PWMx.
In addition to VCC and GND, freely connect any of the following 16 pins:
Type
Pin
1
2
3
4
Range
Notes
Digital Input
DIx
#15
#16
#17
#18
0.0V - VCC
Internal pull-up
Analog Input
AIx
#22
#23
#24
#25
0.0V - 2.0V
Invalid if exceeded
Digital Output
DOx
#5
#8
#9
#12
0.0V - VCC
Connect LED cathode
PWM Output
PWMx
#4
#6
#7
#11
0% - 100%
Voltage at VCC level
Creating and Running the Script
Installing Modules
Install the mwings (or mwingslite) and dearpygui modules.
The initialization of Dear PyGui is handled by initialize_viewport().
This function sets up the font file and defines the size of the OS window (referred to as the Viewport).
definitialize_viewport(self) ->None:
"""Set up Dear PyGui context, fonts, and viewport."""...
The interface within the OS window is defined in create_windows().
Child windows on the screen are created using dpg.window(), and components are defined within them.
By assigning a tag to a component, you can modify its values and attributes from the program (similar to the id= in HTML).
defcreate_windows(self) ->None:
"""Create and configure all Dear PyGui windows and their contents."""...
Buttons, combo boxes, sliders, and checkboxes that accept user input can register a callback.
defon_select_port(self, sender: Any, app_data: str, user_data: Any) ->None:
"""Handle serial port selection from the combo box."""...
As with other scripts, data received from a child TWELITE is handled by an event handler.
When building an executable with Nuitka
When building an executable (e.g., .exe) using Nuitka, converting method bindings into C may cause self to be duplicated, resulting in too many arguments. In such cases, implement the callback by returning a closure as shown below.
from typing import Any, Callable
...defcb_for_select_port(self) -> Callable[[Any, str, Any], None]:
"""Make a callback for serial port selection from the combo box."""defcallback(sender: Any, app_data: str, user_data: Any) ->None:
self.selected_port = app_data
return callback
... dpg.add_combo(
... callback=self.cb_for_select_port(),
)
In on_app_twelite(), data to be reflected in DOx is registered immediately using tag, and data for PWMx is passed to update_plot() via member variables.
defon_app_twelite(self, packet: mw.parsers.app_twelite.ParsedPacket) ->None:
"""Update the GUI based on incoming TWELITE DIP packet data."""...
When the “Connect” button is pressed, start() is called.
This function initializes mwings.Twelite and registers the receive handler.
Since the receive handler is defined as a method, the add_listener method is used instead of the decorator @on used previously.
defstart(self) ->None:
"""Start TWELITE communication and register listeners."""...
Because Dear PyGui is built on a low-level API, the application controls frame rendering directly, allowing you to define your own rendering loop. While the OS window is open, loop() is repeatedly called.
Once connected to the serial port, loop() performs the following three tasks:
defloop(self) ->None:
"""Perform periodic updates while the application is running."""...
The entire application runs when show() is called.
defshow(self) ->None:
"""Main execution loop of the application."""...
The program exits the rendering loop either when dpg.is_dearpygui_running() becomes False after closing the window, or upon receiving a KeyboardInterrupt (Ctrl+C), and then cleans up mwings.Twelite and dearpygui.