PC Integration via UART
Products Used
![]() | ![]() |
---|---|
TWELITE DIP | TWELITE R2 |
TWELITE Parent/Child | USB Adapter |
Extremely Simple! Standard App | - |
2 units | 1 unit |
Note: The TWELITE DIP and TWELITE R2 pair is functionally equivalent to a single MONOSTICK. You may also use the following combination:
![]() | ![]() |
---|---|
TWELITE DIP | MONOSTICK |
TWELITE Child | TWELITE Parent |
Extremely Simple! Standard App | Parent/Repeater App |
1 unit | 1 unit |
Installing the TWELITE STAGE App
The TWELITE STAGE app is a tool that offers features for configuring and writing firmware, as well as evaluating communication with the Parent.
The TWELITE STAGE app is included in the TWELITE STAGE SDK.
Receiving Data with the TWELITE STAGE App
The data sent from the Child running the Extremely Simple! Standard App includes information such as the input state of the DIx
/AIx
ports, power supply voltage, and the logical device ID of the sender.
Displaying Serial Strings
Data received by the Parent from the Child can be obtained by interpreting the strings output by the Parent over serial (UART). Let’s display this string first.
Start the TWELITE STAGE app and select the Parent in Serial Port Selection. Then open Main Menu > 1: Viewer > 1: Terminal.
When data is received from the Child, a message like the following will be displayed:
:78811501C98201015A000391000C2E00810301FFFFFFFFFB
By interpreting such strings output by the Parent, you can obtain the data sent by the Child.
The above string represents the following data
# | Data | Description | Value | |
---|---|---|---|---|
: | char | Header | : | |
78 | 0 | uint8 | Sender Logical Device ID | 0x78 |
81 | 1 | uint8 | Command Number | 0x81 |
15 | 2 | uint8 | Packet Identifier | 0x15 |
01 | 3 | uint8 | Protocol Version | 0x01 |
C9 | 4 | uint8 | LQI | 201/255 |
8201015A | 5 | uint32 | Sender Serial ID | 0x201015A |
00 | 9 | uint8 | Destination Logical ID | 0x00 |
0391 | 10 | uint16 | Timestamp | approx. 14.27 seconds |
00 | 12 | uint8 | Number of Relays | 0 |
0C2E | 13 | uint16 | Power Supply Voltage | 3118 mV |
00 | 15 | int8 | - | |
81 | 16 | uint8 | Digital Signal | DI1 L DI2 H DI3 H DI4 H (Periodic Tx) |
03 | 17 | uint8 | Digital Signal Mask | DI1 DI2 |
01 | 18 | uint8 | AI1 Converted Value | 16 mV |
FF | 19 | uint8 | AI2 Converted Value | Not used |
FF | 20 | uint8 | AI3 Converted Value | Not used |
FF | 21 | uint8 | AI4 Converted Value | Not used |
FF | 22 | uint8 | AIx Correction Value | AI1 0x03 |
FB | uint8 | Checksum | 0xFB | |
char | Footer | \r | ||
char | Footer | \n |
The strings output by the TWELITE Parent follow the format below:
Header | Payload | Checksum | Footer |
---|---|---|---|
: | repeated 00 -FF | LRC8 of payload | CRLF |
- All ASCII characters*
- Starts with
:
(0x3A
) - Ends with CRLF (
\r\n
/0x0D 0x0A
) - Big-endian
*Except for binary format in serial communication apps
Standard App Viewer
The above format is machine-friendly, but to check the content as a human, you need to interpret it. The TWELITE STAGE app provides a function to interpret and display strings representing data sent from Children running the Extremely Simple! Standard App. The Python library described below also performs this interpretation.
Return to Main Menu > 1: Viewer, and open 2: Standard App Viewer.
You can check the timestamp, logical device ID, serial ID, and the values of DIx
/AIx
.

Standard App Viewer screen
Sending Data with the TWELITE STAGE App
Conversely, you can send wireless packets from the Parent to the Child to change the output state of the Child.
Commander
Return to Main Menu > 1: Viewer, and open 5: Commander.
You can send packets simulating DIx
and AIx
inputs.

Commander screen
How to use:
- Select the destination and input state
- Click Send
Commander sends strings from the PC to the Parent over serial communication. In other words, it works in the opposite direction to the Standard App Viewer.
This string also starts with :
and ends with CRLF. For the detailed format, see the Extremely Simple! Standard App Manual.
Python Scripts
Both the “Standard App Viewer” and “Commander” in the TWELITE STAGE app simply exchange strings over serial communication. If you have an environment that supports serial communication, you can integrate your own application with the Parent.
DI1
, and then control the output of DO1
.By leveraging Python scripts, you can process and save received data, or control output ports from your own software.
Installing the MWings Library
The MWings library makes it easy to integrate TWELITE and Python scripts. Specifically, it interprets strings representing data received from the Child and constructs data to be sent to the Child.
It can be installed from PyPI.
Receiving Data with a Python Script
Let’s implement a Python script that displays a message when a button connected to DI1
on the TWELITE DIP is pressed.
Running the Sample Script
Create a script named dip_show_di1.py
with the following content:
# -*- coding:utf-8 -*-
# TWELTIE DIP start guide: receiving in python
import mwings as mw
def main():
twelite = mw.Twelite(mw.utils.ask_user_for_port())
@twelite.on(mw.common.PacketType.APP_TWELITE)
def on_app_twelite(packet):
if packet.di_state[0]:
print("DI1 Pressed")
try:
twelite.daemon = True
twelite.start()
print("Started receiving")
while True:
twelite.join(0.5)
except KeyboardInterrupt:
print("Flushing...")
twelite.stop()
print("Completed")
if __name__ == "__main__":
main()
When you run the script, it will output DI1 Pressed
when the Child’s DI1
goes Low.
If multiple TWELITE R series or MONOSTICK series devices are connected at runtime, select the serial port attached to the Parent.
poetry run python dip_show_di1.py
Multiple ports detected.
[1] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
[2] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
Select [1-2]: 2
Selected: /dev/cu.usbserial-R2xxxxxx
Started receiving
DI1 Pressed
DI1 Pressed
^CFlushing...
Completed
The above is an example for macOS. On Windows, COM port names are displayed.
Explanation of the Sample Script
Importing the MWings Library
import mwings as mw
The library is imported as the shorthand mw
, similar to pandas as pd
or numpy as np
.
Creating the Object
twelite = mw.Twelite(mw.utils.ask_user_for_port())
This creates a Twelite
object, which serves as the interface for communicating with the Parent.
You specify the serial port as an argument, but here we use the mw.utils.ask_user_for_port()
utility to select it dynamically. This function outputs an error message if no serial port exists, returns the port if only one is found, or prompts the user if multiple are present.
Registering a Receive Event Handler
@twelite.on(mw.common.PacketType.APP_TWELITE)
def on_app_twelite(packet):
if packet.di_state[0]:
print("DI1 Pressed")
This registers an event handler that is called when a packet is received from a Child running the Extremely Simple! Standard App.
Event handler registration uses a Python decorator (?). Here, since we are targeting data from the Extremely Simple! Standard App (App_Twelite), we use @twelite.on(mw.common.PacketType.APP_TWELITE)
. The function defined immediately after this decorator becomes the handler. The function name can be anything, but it must be defined after initializing the Twelite
object.
To detect when the Child’s DI1
becomes Low, the receive handler checks the first boolean value in the List-like object di_state
(digital interface state) of the packet
variable (mwings.parsers.app_twelite.ParsedPacket
). Each value in di_state
is True
when Low.
Handling Other Data
The packet
variable contains not only input states but also information about the sender and receiver. For example, by using to_json()
, you can output all data in JSON format as follows:
@twelite.on(mw.common.PacketType.APP_TWELITE)
def on_app_twelite(packet):
print(packet.to_json(verbose=True, spread=False))
Here, verbose
enables inclusion of system information, and spread
expands List-like objects.
If you have pandas installed, you can also obtain a DataFrame using to_df()
:
@twelite.on(mw.common.PacketType.APP_TWELITE)
def on_app_twelite(packet):
print(packet.to_df(verbose=False).to_string())
Waiting for Data
twelite.daemon = True
twelite.start()
print("Started receiving")
while True:
twelite.join(0.5)
Here, the script waits for data from the Parent in a separate thread.
The Twelite
object is a subclass of threading.Thread
, and this mechanism launches a daemon thread, blocking the main thread until the script ends.
Why use a while
loop and join(0.5)
?
Cleanup
print("Flushing...")
twelite.stop()
print("Completed")
When Ctrl-C is pressed, twelite.stop()
is called to terminate the subthread.
Sending Data with a Python Script
Next, let’s implement a Python script to blink an LED connected to DO1
on the TWELITE DIP from the PC.
Running the Sample Script
Create a script named dip_blink_led.py
with the following content:
# -*- coding:utf-8 -*-
# TWELITE DIP start guide: blinking from python
from time import sleep
import mwings as mw
def main():
twelite = mw.Twelite(mw.utils.ask_user_for_port())
initial = {
"destination_logical_id": 0x78,
"di_to_change": [True, False, False, False],
"di_state": [False, False, False, False],
}
command = mw.serializers.app_twelite.Command(**initial)
while True:
command.di_state[0] = not command.di_state[0]
twelite.send(command)
print(f"Flip DO1: {command.di_state[0]}")
sleep(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("...Aborting")
When you run the script, the Child’s DO1
will blink every second.
If multiple TWELITE R series or MONOSTICK series devices are connected at runtime, select the serial port attached to the Parent.
poetry run python dip_blink_led.py
Multiple ports detected.
[1] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
[2] /dev/cu.usbserial-R2xxxxxx TWE-Lite-R (Genuine)
Select [1-2]: 2
Selected: /dev/cu.usbserial-R2xxxxxx
Flip DO1: True
Flip DO1: False
Flip DO1: True
Flip DO1: False
Flip DO1: True
^C...Aborting
Explanation of the Sample Script
Importing the MWings Library
import mwings as mw
The library is imported as the shorthand mw
, similar to pandas as pd
or numpy as np
.
Creating the Object
twelite = mw.Twelite(mw.utils.ask_user_for_port())
This creates a Twelite
object, which serves as the interface for communicating with the Parent.
You specify the serial port as an argument, but here we use the mw.utils.ask_user_for_port()
utility to select it dynamically. This function outputs an error message if no serial port exists, returns the port if only one is found, or prompts the user if multiple are present.
Creating the Command to Send to the Parent
initial = {
"destination_logical_id": 0x78,
"di_to_change": [True, False, False, False],
"di_state": [False, False, False, False],
}
command = mw.serializers.app_twelite.Command(**initial)
This initializes a command
object (mwings.serializers.app_twelite.Command
), which represents the packet to be sent to a Child running the Extremely Simple! Standard App.
The dictionary initial
represents the initial state of the command. Here, the destination logical device ID is set to 0x78
(all Children), with DI1
as the target for change, and initially set to High.
Sending the Data
while True:
command.di_state[0] = not command.di_state[0]
twelite.send(command)
print(f"Flip DO1: {command.di_state[0]}")
sleep(1)
Here, the command data is converted to a string and sent to the Parent every second.
Blinking is achieved by toggling the first boolean value in the List-like object di_state
(digital interface state) of the command data, which controls the state of DOx
.