Communicating with Child Devices Using Python (Web Server IoT)
Send data from child devices to a web server using Python
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
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.
*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.
For Raspberry Pi, please use the Lite version mwingslite
module.
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.
Verify that TWELITE STICK can interpret the received data.
Please run the sample script rx_print_json.py
available on GitHub.
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, typed
from zoneinfo import ZoneInfo
import mwings as mw
# Main function
def main() -> 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)
def on_app_aria(packet: mw.parsers.app_aria.ParsedPacket) -> None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_CUE)
def on_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)
def on_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)
def on_app_io(packet: mw.parsers.app_io.ParsedPacket) -> None:
print(packet.to_json(verbose=False, spread=True))
@twelite.on(mw.common.PacketType.APP_TWELITE)
def on_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)
def on_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)
def on_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)
def on_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)
def on_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)
def on_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)
def on_act(packet: mw.parsers.act.ParsedPacket) -> None:
print(packet.to_json(verbose=False, spread=True))
# Start receiving
try:
# Set as daemon thread
twelite.daemon = True
# Start the thread, Join to the main thread
twelite.start()
print("Started receiving")
while True:
twelite.join(0.5)
except KeyboardInterrupt:
# 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
.
{
"time_parsed": "2025-07-11T11:45:02.067804+09:00",
"packet_type": "APP_TWELITE",
"sequence_number": 4713,
"source_serial_id": "8201007F",
"source_logical_id": 120,
"lqi": 166,
"supply_voltage": 3254,
"destination_logical_id": 0,
"relay_count": 0,
"periodic": true,
"di_changed_1": false,
"di_changed_2": false,
"di_changed_3": false,
"di_changed_4": false,
"di_state_1": false,
"di_state_2": false,
"di_state_3": false,
"di_state_4": false,
"ai_voltage_1": 2000,
"ai_voltage_2": 2000,
"ai_voltage_3": 2000,
"ai_voltage_4": 2000
}
mwings.parsers.app_twelite.ParsedPacket
Key | Value Description |
---|---|
time_parsed | Reception time (default UTC, ISO8601 format) |
packet_type | Packet type |
sequence_number | Sequence number (represents time for App_Twelite) |
source_serial_id | Sender serial ID |
source_logical_id | Sender logical device ID |
lqi | Link Quality Indicator (8-bit) |
supply_voltage | Supply voltage (mV) |
destination_logical_id | Destination logical device ID |
relay_count | Relay count |
periodic | Indicates whether it is a periodic transmission |
di_changed | Whether each digital input has changed |
di_state | Current state of each digital input |
ai_voltage | Voltage at each analog input |
mwings_implementation | MWings implementation details (for future use) |
mwings_version | MWings library version |
hostname | Hostname of the receiver |
system_type | System type of the receiver host |
When spread=True
, list-type fields are split into keys like _1
, _2
, etc.
A sample script rx_export_csv_durable.py
is also available for logging data to a CSV file.
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
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.
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 function
def main() -> 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)
def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
print(packet.to_json(verbose=False, spread=True))
# Start receiving
try:
# Set as daemon thread
twelite.daemon = True
# Start the thread, Join to the main thread
twelite.start()
print("Started receiving")
while True:
twelite.join(0.5)
except KeyboardInterrupt:
# 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.
For a detailed explanation, refer to Script Breakdown in the MWings for Python Manual.
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.
Rewrite the on_app_twelite()
handler as follows:
def on_app_twelite(packet: mw.parsers.app_twelite.ParsedPacket) -> None:
print(f"\rDO1 LED: {"🔴" if packet.di_state[0] else "⚪"}", end='', flush=True)
If pressing the switch connected to the DI
pin of TWELITE DIP causes the red light to appear, it indicates success.
Example display
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")
while True:
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 function
def main() -> 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)
def on_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 state
def toggle_di1() -> None:
command.di_state[0] = not command.di_state[0]
twelite.send(command)
# Start receiving
try:
# Set as daemon thread
twelite.daemon = True
# Start the thread, Join to the main thread
twelite.start()
print("Started receiving")
while True:
toggle_di1() # Send
twelite.join(0.5) # Receive
except KeyboardInterrupt:
# 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.
Send data from child devices to a web server using Python
Build an application with a graphical UI to communicate with child devices using Python