Skip to content

AccelerationConsortium/dose_every_well

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dose Every Well

Automate liquid and solid powder dispensing into microplates using CNC machines.

A Python package for precise control of CNC machines designed for laboratory automation and high-throughput dispensing workflows.

Python License Platform

Features

CNC Control

  • Precise G-code control of CNC machines via serial communication
  • Automatic serial port detection across all platforms
  • Pre-configured for Genmitsu 3018-PROVer V2 and 4040 PRO
  • Built-in simulator for testing movements before execution
  • Cross-platform: Windows, Linux, Raspberry Pi, and macOS
  • Safety boundaries to prevent collisions
  • YAML-based configuration for easy machine setup
  • Motorized plate loader with collision avoidance for different plate types

Raspberry Pi Hardware Support

  • Motorized Plate Loader - Automated well plate loading with synchronized servo lift and lid control
  • Solid Powder Doser - Precise solid material dispensing with servo gate and DC motor auger
  • Power-Safe Operation - Sequential control designed for 5V 5A single-supply operation
  • Waveshare PCA9685 HAT - I2C servo control with relay-based motor switching

Quick Start

Installation

git clone https://github.com/yourusername/dose_every_well.git
cd dose_every_well
pip install -e .

For Raspberry Pi hardware support (Plate Loader, Solid Doser):

# Install with Raspberry Pi dependencies
pip install -e ".[rpi]"

# For Raspberry Pi 5, also install rpi-lgpio (RPi.GPIO compatibility layer)
pip install rpi-lgpio

Basic Usage

from dose_every_well import CNC_Controller, load_config, find_port

# Load configuration and connect
config = load_config("cnc_settings.yaml", "Genmitsu 4040 PRO")
controller = CNC_Controller(find_port(), config)

# Read position
coords = controller.read_coordinates()
print(f"Position: X={coords['X']}, Y={coords['Y']}, Z={coords['Z']}")

# Move to position
controller.move_to_point(10, 20)  # X=10mm, Y=20mm
controller.execute_movement()

Run Demos

# CNC control demos
python demo/simple_connect_demo.py
python demo/axis_movement_demo.py

# Raspberry Pi hardware demos (requires hardware)
python demo/plate_loader_demo.py
python demo/solid_doser_demo.py

Platform Support

Platform Status Notes
Windows 7/10/11 Supported Works out of box
Linux (Ubuntu/Debian) Supported Requires dialout group
Raspberry Pi 3/4/5 Supported Pi 5 requires rpi-lgpio
macOS (Intel/M1/M2) Supported Full support

Documentation

Complete Documentation

General

Raspberry Pi Hardware

Supported Hardware

CNC Machines

  • Genmitsu 4040 PRO (400×400×75mm work area)
  • Genmitsu 3018-PROVer V2 (300×180×45mm work area)
  • Any GRBL-compatible CNC machine (requires custom configuration)

Raspberry Pi Hardware (Optional)

Complete system for automated solid/liquid dispensing:

Component Purpose Channels
Waveshare PCA9685 HAT I2C servo driver (0x40) 16 channels
4× Servo Motors Gate, lifts, lid control Ch 0, 3, 6, 9
5V Relay Module DC motor ON/OFF GPIO 17
DC Motor Auger/screw feeder Via relay
5V 5A Power Supply Single plug powers all USB-C + distribution

See Wiring Guide for complete setup instructions

Plate Loader (Raspberry Pi)

Motorized plate loader with automatic collision avoidance for safe operation with different plate types:

from dose_every_well import PlateLoader

# Specify plate type for automatic safety settings
loader = PlateLoader(plate_type='shallow_plate')  # 96-well plates
loader = PlateLoader(plate_type='deep_well')      # Deep-well plates

# Operate safely with collision avoidance
loader.open_lid()
loader.raise_plate()
loader.close_lid()  # Auto-blocked if plate position would cause collision

# Switch plate types on the fly
loader.set_plate_type('custom_384_well')
loader.reload_config()  # Reload settings from plate_settings.yaml

Features:

  • Collision Avoidance - Prevents lid-plate crashes based on plate type
  • YAML Configuration - Customize plate types in plate_settings.yaml
  • Hot-Reload - Update settings without restarting
  • Multiple Plate Types - Pre-configured for shallow, deep-well, and custom plates

Requires Raspberry Pi with PCA9685 PWM HAT and servos. See plate_settings.yaml for configuration.

Testing

Verify your setup works correctly:

python test_platform.py

Project Structure

dose_every_well/
├── src/dose_every_well/       # Main package
│   ├── cnc_controller.py      # Core CNC controller
│   ├── plate_loader.py        # Raspberry Pi plate loader (3 servos)
│   ├── solid_doser.py         # Raspberry Pi solid doser (servo + motor)
│   ├── cnc_settings.yaml      # CNC machine configs
│   ├── plate_settings.yaml    # Plate loader configs
│   └── __init__.py
├── demo/                       # Example scripts
│   ├── simple_connect_demo.py
│   ├── axis_movement_demo.py
│   ├── plate_loader_demo.py
│   └── solid_doser_demo.py
├── docs/                       # Documentation
│   ├── wiring_guide.md        # Hardware setup (START HERE!)
│   ├── solid_doser.md
│   ├── plate_loader.md
│   ├── installation.md
│   └── troubleshooting.md
├── test_platform.py           # Platform compatibility test
└── README.md                  # This file

Safety

Important Safety Practices:

  • Test in simulation before hardware execution
  • Verify movement boundaries match your machine
  • Keep emergency stop accessible during operation
  • Clear work area before automated runs
  • Monitor Z-axis movements to prevent crashes

Usage Examples

Example 1: CNC-Only Liquid Dispensing

from dose_every_well import CNC_Controller, load_config, find_port

# Setup
config = load_config("cnc_settings.yaml", "Genmitsu 4040 PRO")
controller = CNC_Controller(find_port(), config)

# 96-well plate parameters
well_spacing = 9.0  # mm
start_x, start_y = 10.0, 10.0
dispense_height = 5.0

# Visit each well
for row in range(8):  # A-H
    for col in range(12):  # 1-12
        x = start_x + col * well_spacing
        y = start_y + row * well_spacing
        
        controller.move_to_point(x, y)
        controller.move_to_height(dispense_height)
        controller.execute_movement()
        
        print(f"Dispensing to well {chr(65+row)}{col+1}")

Example 2: Complete Solid Dosing Workflow (Raspberry Pi)

from dose_every_well import CNC_Controller, PlateLoader, SolidDoser, load_config, find_port
import time

# Initialize all controllers
cnc = CNC_Controller(find_port(), load_config("cnc_settings.yaml", "Genmitsu 4040 PRO"))
plate_loader = PlateLoader(i2c_address=0x40)
solid_doser = SolidDoser(i2c_address=0x40, motor_gpio_pin=17)

try:
    # 1. Load plate onto balance
    print("Loading plate...")
    plate_loader.load_sequence()
    
    # 2. Dispense solid into each well
    well_spacing = 9.0  # mm
    start_x, start_y = 10.0, 10.0
    
    for row in range(8):
        for col in range(12):
            well = f"{chr(65+row)}{col+1}"
            print(f"Dispensing to well {well}...")
            
            # Move CNC to well position
            x = start_x + col * well_spacing
            y = start_y + row * well_spacing
            cnc.move_to_point(x, y)
            cnc.move_to_height(5.0)
            cnc.execute_movement()
            
            # Dispense solid material (motor + gate servo)
            solid_doser.dispense(duration=2.0)
            
            # Move up
            cnc.move_to_height(20.0)
            cnc.execute_movement()
    
    # 3. Unload plate
    print("Unloading plate...")
    plate_loader.unload_sequence()
    
finally:
    solid_doser.shutdown()
    plate_loader.shutdown()
    cnc.disconnect()

Example 3: Solid Doser Only (No CNC)

from dose_every_well import SolidDoser

doser = SolidDoser(i2c_address=0x40, motor_gpio_pin=17)

try:
    # Simple dispense for 5 seconds
    doser.dispense(duration=5.0)
    
    # Precise low-flow dispense (30° gate opening)
    doser.dispense(duration=3.0, gate_angle=30)
    
finally:
    doser.shutdown()

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run python test_platform.py to verify
  5. Submit a pull request

For major changes, open an issue first to discuss.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Yang Cao

Citation

If you use this package in your research, please cite:

@software{dose_every_well,
  author = {Cao, Yang},
  title = {Dose Every Well: CNC-based Automated Dispensing},
  year = {2025},
  url = {https://github.com/AccelerationConsortium/dose_every_well}
}

Acknowledgments

Built for laboratory automation workflows with a focus on microplate dispensing applications. Uses GRBL firmware for CNC control.


Need help? Check the documentation or open an issue on GitHub.

About

A CNC-gantry based solid dosing module for microplates

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages