Skip to main content

Hardware drivers for the PUDA platform.

This project has been archived.

The maintainers of this project have marked this project as archived. No new releases are expected.

Project description

puda-drivers

Hardware drivers for the PUDA (Physical Unified Device Architecture) platform. This package provides Python interfaces for controlling laboratory automation equipment.

Features

  • Gantry Control: Control G-code compatible motion systems (e.g., QuBot)
  • Liquid Handling: Interface with Sartorius rLINE® pipettes and dispensers
  • Serial Communication: Robust serial port management with automatic reconnection
  • Logging: Configurable logging with optional file output to logs folder
  • Cross-platform: Works on Linux, macOS, and Windows

Installation

From PyPI

pip install puda-drivers

From Source

git clone https://github.com/zhao-bears/puda-drivers.git
cd puda-drivers
pip install -e .

Quick Start

Logging Configuration

Configure logging for your application with optional file output:

import logging
from puda_drivers.core.logging import setup_logging

# Configure logging with file output enabled
setup_logging(
    enable_file_logging=True,
    log_level=logging.DEBUG,
    logs_folder="logs", # Optional: default to logs
    log_file_name="my_experiment"  # Optional: custom log file name
)

# Or disable file logging (console only)
setup_logging(
    enable_file_logging=False,
    log_level=logging.INFO
)

Logging Options:

  • enable_file_logging: If True, logs are written to files in the logs/ folder. If False, logs only go to console (default: False)
  • log_level: Logging level constant (e.g., logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) (default: logging.DEBUG)
  • logs_folder: Name of the folder to store log files (default: "logs")
  • log_file_name: Custom name for the log file. If None or empty, uses timestamp-based name (e.g., log_20250101_120000.log). If provided without .log extension, it will be added automatically.

When file logging is enabled, logs are saved to timestamped files (unless a custom name is provided) in the logs/ folder. The logs folder is created automatically if it doesn't exist.

Gantry Control (GCode)

from puda_drivers.move import GCodeController

# Initialize and connect to a G-code device
gantry = GCodeController(port_name="/dev/ttyACM0", feed=3000)
gantry.connect()

# Configure axis limits for safety (recommended)
gantry.set_axis_limits("X", 0, 200)
gantry.set_axis_limits("Y", -200, 0)
gantry.set_axis_limits("Z", -100, 0)
gantry.set_axis_limits("A", -180, 180)

# Home the gantry
gantry.home()

# Move to absolute position (validated against limits)
gantry.move_absolute(x=50.0, y=-100.0, z=-10.0)

# Move relative to current position (validated after conversion to absolute)
gantry.move_relative(x=20.0, y=-10.0)

# Query current position
position = gantry.query_position()
print(f"Current position: {position}")

# Disconnect when done
gantry.disconnect()

Axis Limits and Validation: The move_absolute() and move_relative() methods automatically validate that target positions are within configured axis limits. If a position is outside the limits, a ValueError is raised before any movement is executed. Use set_axis_limits() to configure limits for each axis.

Liquid Handling (Sartorius)

from puda_drivers.transfer.liquid.sartorius import SartoriusController

# Initialize and connect to pipette
pipette = SartoriusController(port_name="/dev/ttyUSB0")
pipette.connect()
pipette.initialize()

# Attach tip
pipette.attach_tip()

# Aspirate liquid
pipette.aspirate(amount=50.0)  # 50 µL

# Dispense liquid
pipette.dispense(amount=50.0)

# Eject tip
pipette.eject_tip()

# Disconnect when done
pipette.disconnect()

Combined Workflow

from puda_drivers.move import GCodeController
from puda_drivers.transfer.liquid.sartorius import SartoriusController

# Initialize both devices
gantry = GCodeController(port_name="/dev/ttyACM0")
pipette = SartoriusController(port_name="/dev/ttyUSB0")

gantry.connect()
pipette.connect()

# Move to source well
gantry.move_absolute(x=50.0, y=-50.0, z=-20.0)
pipette.aspirate(amount=50.0)

# Move to destination well
gantry.move_absolute(x=150.0, y=-150.0, z=-20.0)
pipette.dispense(amount=50.0)

# Cleanup
pipette.eject_tip()
gantry.disconnect()
pipette.disconnect()

Device Support

Motion Systems

  • QuBot (GCode) - Multi-axis gantry systems compatible with G-code commands
    • Supports X, Y, Z, and A axes
    • Configurable feed rates
    • Position synchronization and homing
    • Automatic axis limit validation for safe operation

Liquid Handling

  • Sartorius rLINE® - Electronic pipettes and robotic dispensers
    • Aspirate and dispense operations
    • Tip attachment and ejection
    • Configurable speeds and volumes

Error Handling

Axis Limit Validation

Both move_absolute() and move_relative() validate positions against configured axis limits before executing any movement. If a position is outside the limits, a ValueError is raised:

from puda_drivers.move import GCodeController

gantry = GCodeController(port_name="/dev/ttyACM0")
gantry.connect()

# Set axis limits
gantry.set_axis_limits("X", 0, 200)
gantry.set_axis_limits("Y", -200, 0)

try:
    # This will raise ValueError: Value 250 outside axis limits [0, 200]
    gantry.move_absolute(x=250.0, y=-50.0)
except ValueError as e:
    print(f"Move rejected: {e}")

# Relative moves are also validated after conversion to absolute positions
try:
    # If current X is 150, moving 100 more would exceed the limit
    gantry.move_relative(x=100.0)
except ValueError as e:
    print(f"Move rejected: {e}")

Validation errors are automatically logged at the ERROR level before the exception is raised.

Logging Best Practices

For production applications, configure logging at the start of your script:

import logging
from puda_drivers.core.logging import setup_logging
from puda_drivers.move import GCodeController

# Configure logging first, before initializing devices
setup_logging(
    enable_file_logging=True,
    log_level=logging.INFO,
    log_file_name="gantry_operation"
)

# Now all device operations will be logged
gantry = GCodeController(port_name="/dev/ttyACM0")
# ... rest of your code

This ensures all device communication, movements, and errors are captured in log files for debugging and audit purposes.

Finding Serial Ports

To discover available serial ports on your system:

from puda_drivers.core import list_serial_ports

# List all available ports
ports = list_serial_ports()
for port, desc, hwid in ports:
    print(f"{port}: {desc} [{hwid}]")

# Filter ports by description
sartorius_ports = list_serial_ports(filter_desc="Sartorius")

Requirements

  • Python >= 3.14
  • pyserial >= 3.5
  • See pyproject.toml for full dependency list

Development

Setup Development Environment

# Create virtual environment
uv venv

# Activate virtual environment
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install dependencies
uv sync

# Install package in editable mode
pip install -e .

Building and Publishing

# Build distribution packages
uv build

# Publish to PyPI
uv publish
# Username: __token__
# Password: <your PyPI API token>

Version Management

# Set version explicitly
uv version 0.0.1

# Bump version (e.g., 1.2.3 -> 1.3.0)
uv bump minor

Documentation

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

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

puda_drivers-0.0.6.tar.gz (33.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

puda_drivers-0.0.6-py3-none-any.whl (33.9 kB view details)

Uploaded Python 3

File details

Details for the file puda_drivers-0.0.6.tar.gz.

File metadata

  • Download URL: puda_drivers-0.0.6.tar.gz
  • Upload date:
  • Size: 33.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.17

File hashes

Hashes for puda_drivers-0.0.6.tar.gz
Algorithm Hash digest
SHA256 077b7fe35c7d9e4e4fc7f7ecbaa38b8f3b83e87571d1c92fc7afb6fe02a51365
MD5 18d1a0be5f29de22e37c040420f5ab8f
BLAKE2b-256 815088947da57b112d3e398e03caa4ebde23315d850492bd5e80e1e0ddaf0293

See more details on using hashes here.

File details

Details for the file puda_drivers-0.0.6-py3-none-any.whl.

File metadata

File hashes

Hashes for puda_drivers-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 7c464a5048a552c444851122815a4277b8d2ffd1b35196bfb3c7becfd37c3d6c
MD5 46737983bb4dad9c91f1d2318ab44cb9
BLAKE2b-256 3f59d8ddf0841aa1b96fb3b7a001efa6eaca0a446772f147bc0bb5dd3ffb97b0

See more details on using hashes here.

Supported by

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