Skip to main content

A simple library for asynchronous serial communication

Project description

EasyCom

EasyCom is a Python library designed for asynchronous serial communication with USB UART devices such as Arduino, ESP32, Raspberry Pi Pico, Teensy, and others. With non-blocking communication, automatic port detection and reconnection, and an easy-to-use API, EasyCom allows you to seamlessly interact with multiple devices. It also supports the dynamic registration of new devices at runtime.


Features

  • Asynchronous Communication: Uses threading to provide non-blocking read and write operations.
  • Automatic Port Detection and Reconnection: Matches devices using PID/VID for automatic connection and reconnection if disconnected.
  • Pre-configured and Dynamically Registered Devices: Built-in support for common devices, with the ability to add custom devices dynamically.
  • Thread-safe Writes: Queue-based data management ensures safe, consistent writes.
  • Customizable Data Handlers: Process incoming data using custom callback functions for maximum flexibility.

Installation

You can install EasyCom directly with pip:

pip install easycom

Getting Started

Basic Example: Arduino with Default Parameters

This example demonstrates setting up an Arduino device using default parameters. With automatic port detection, the library will attempt to connect to the first available port with matching PID/VID.

from easycom.devices import Arduino

# Define a simple callback function to handle received data
def handle_data(data):
    print(f"Received: {data}")

# Initialize Arduino with automatic port detection and default settings
arduino = Arduino(data_handler=handle_data)

# Keep the program running to receive data
input("Press Enter to disconnect...")

# Disconnect the Arduino
arduino.disconnect()

Overriding Parameters

EasyCom allows you to override specific parameters such as port, baudrate, timeout, and data_handler. Each example below illustrates how to set these parameters.

1. Overriding the port

If the device is connected to a specific port, you can set it explicitly:

arduino = Arduino(port="/dev/ttyUSB0", data_handler=handle_data)

2. Overriding the baudrate

To customize the baud rate (e.g., 115200), set it during initialization:

arduino = Arduino(baudrate=115200, data_handler=handle_data)

3. Overriding the timeout

Adjust the timeout (in seconds) for read and write operations:

arduino = Arduino(timeout=5, data_handler=handle_data)

Here’s an updated example using a struct in C++ on the Arduino side to send an integer and a float together as a single binary package. This structure will match the Python struct for decoding.


4. Using a struct-based Data Handler

In this example, we’ll send data as a structured binary package from Arduino and parse it in Python using the struct module. The Arduino will send an integer and a float in one structured transmission.

import struct
from easycom.devices import Arduino

# Define a data handler to parse binary data
def handle_data(data):
    # Parse data as an integer and a float
    parsed_data = struct.unpack("<if", data)  # "<if" represents little-endian int, float
    print(f"Parsed Data: Integer={parsed_data[0]}, Float={parsed_data[1]}")

# Initialize the Arduino device
arduino = Arduino(data_handler=handle_data)
Arduino Code with C++ Struct

On the Arduino, we’ll define a C++ struct to hold the data, then send this struct as binary data.

// Arduino code to send a struct with an integer and a float as binary data over Serial

struct DataPacket {
  int myInt;
  float myFloat;
};

// Create an instance of the struct and populate it
DataPacket packet = {42, 3.14};

void setup() {
  Serial.begin(9600);  // Ensure this matches the baud rate in EasyCom
}

void loop() {
  // Send the struct as binary data
  Serial.write((byte*)&packet, sizeof(packet));  // Send entire struct as binary

  delay(1000);  // Send data every second
}

In this code:

  • We define a DataPacket struct containing an integer (myInt) and a float (myFloat).
  • Serial.write((byte*)&packet, sizeof(packet)); sends the entire struct as a single binary transmission.
  • The data format sent by Arduino matches struct.unpack("<if", data) on the Python side, where:
    • "<if" specifies little-endian format: an integer (i) followed by a float (f).

This setup allows Python to parse the binary data from Arduino, resulting in correctly structured data for your application.

5. Combining All Parameter Overrides

You can combine multiple overrides, specifying port, baudrate, timeout, and a custom data_handler.

arduino = Arduino(
    port="/dev/ttyUSB0",
    baudrate=115200,
    timeout=3,
    data_handler=handle_data
)

Registering a New Device at Runtime

To add a new device dynamically, use register_device. The example below registers a custom device MyDevice with a unique PID/VID, baud rate, and timeout.

from easycom.devices import register_device

# Register a custom device class
MyDevice = register_device(
    name="MyDevice",
    pidvids=["1234:5678"],
    default_baudrate=57600,
    default_timeout=4
)

# Initialize and use the custom device
device = MyDevice(data_handler=handle_data)
device.write(b"Hello, MyDevice!")
device.disconnect()

Supported Devices

EasyCom includes pre-configured support for several common USB UART devices.

Device PID/VIDs Default Baudrate Default Timeout
Arduino 2341:0043, 2341:0010, 0403:6001 9600 2
Raspberry Pi Pico 2E8A:0005 115200 2
Teensy 16C0:0483, 16C0:0487 9600 2
ESP32 10C4:EA60, 1A86:7523 115200 3
STM32 0483:5740 9600 2
CP210x 10C4:EA60 115200 2
FTDI 0403:6001, 0403:6015 9600 2
CH340 1A86:7523 9600 3
Prolific 067B:2303 9600 2
RaspberryPi 2E8A:000A 115200 2
NRF52840 1915:520A 115200 2
MCP2200 04D8:00DD 9600 2
EFM32 10C4:E005 115200 2
STLink 0483:374B 9600 2
FT232H 0403:6015 3000000 2
FX2LP 04B4:8613 9600 2
LPC11Uxx 1FC9:009C 115200 2

Adding Additional Devices

You can extend this list by registering additional devices with register_device, as shown in the example above.


Advanced Features

Automatic Reconnection

If a device disconnects, EasyCom will attempt to reconnect automatically, simplifying communication in environments where devices are intermittently connected.

Debug Logging

Enable detailed logging to trace communication flow and troubleshoot issues:

import logging
logging.basicConfig(level=logging.DEBUG)

Common Usage Patterns

Continuous Data Handling

For applications requiring continuous data reading, the following setup ensures the program stays open until manually closed:

from easycom.devices import Arduino

def handle_data(data):
    print(f"Data Received: {data}")

arduino = Arduino(data_handler=handle_data)

try:
    while True:
        pass  # Keep the program running to process data
except KeyboardInterrupt:
    print("Disconnecting...")
    arduino.disconnect()

Sending Data Periodically

To send data at regular intervals, use a loop with a delay, allowing the program to handle data without blocking other operations.

import time

while True:
    arduino.write(b"Ping")
    time.sleep(1)

License

EasyCom is available under the MIT License.


Contributing

Contributions are welcome! Please submit issues or pull requests on GitLab to help improve EasyCom.


Resources


Authors


Summary

EasyCom simplifies working with USB UART devices, providing reliable, non-blocking communication, automatic port detection and reconnection, and dynamic device registration for fast, flexible interaction with serial devices.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easycom-0.0.8.tar.gz (10.8 kB view hashes)

Uploaded Source

Built Distribution

easycom-0.0.8-py3-none-any.whl (8.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page