Open Colorimeter - Raspberry Pi Web Version
Web-based colorimeter using Raspberry Pi with CircuitPython (Blinka) for hardware control.
Table of Contents
- Quick Start Workflow
- Features
- Hardware Requirements
- Hardware Setup
- Software Setup
- Auto-start on Boot
- Updating the Application
- Configuration
- Usage
- Project Files
- Troubleshooting
- Quick Reference
- Support
Quick Start Workflow
Initial Setup (one time):
- Clone repository to Raspberry Pi
- Install dependencies:
pip install -r requirements.txt - Install service:
./install_service.sh
Development Workflow:
- Make changes on your development machine
- Commit and push:
git commit -am "message" && git push - On Raspberry Pi:
./update.sh
Done! The service automatically restarts with your changes.
Features
Hardware Control:
- 4-channel DC motor control (Adafruit Motor HAT)
- 8-channel solenoid valve control (MCP23017)
- TSL2591 light sensor for absorbance measurements
- Multiplexer support for multiple sensors (future)
Web Interface:
- Access from any device on your network
- Real-time sensor readings (raw, transmittance, absorbance)
- Interactive motor and solenoid controls
- Visual status indicators for all hardware
- Built-in JSON editors for configuration files
- No SSH required for configuration changes
Automation:
- Command sequences for automated testing
- Mix motors, solenoids, waits, blanking, and measurements
- Save and load multiple named sequences
- Real-time progress monitoring
- Named tests with sensor/calibration mapping
- Share sequences via JSON configuration
Calibration & Testing:
- Multiple calibration curves support
- Named test configurations (e.g., Phosphate, Nitrate)
- Automatic sensor switching via multiplexer (when configured)
- JSON-based calibration storage
Deployment:
- Automated service installation scripts
- One-command updates via git pull
- Virtual environment support
- Auto-restart on boot
- No physical display or keyboard required
Hardware Requirements
- Raspberry Pi (3/4/5 recommended)
- TSL2591 light sensor (I2C)
- Adafruit DC & Stepper Motor HAT for Raspberry Pi (Product #2348)
- 2-4 DC motors (4.5V-13.5V, <1.2A continuous)
- 12V power supply for motors
- Adafruit I2C 8-Channel Solenoid Driver (MCP23017, optional)
- Solenoid valves (12V recommended, optional)
- LEDs for illumination
Hardware Setup
Motor HAT Installation
- Stack the Motor HAT
- Power off your Raspberry Pi
- Align the Motor HAT with the 40-pin GPIO header
- Press firmly to seat all pins
- The HAT should sit flush on top of the Pi
- Connect Power
- Connect your 12V power supply to the green terminal block on the Motor HAT
- Polarity: + (positive) and - (negative/ground)
- Important: Motor power is separate from Pi power - don’t connect 12V to Pi!
- Connect Motors
- Connect your DC motors to the blue terminal blocks:
- M1: Motor 1 (appears as “Motor 1” in web interface)
- M2: Motor 2 (appears as “Motor 2” in web interface)
- M3: Motor 3 (optional)
- M4: Motor 4 (optional)
- Motor wire polarity determines direction (can swap if backwards)
- Connect your DC motors to the blue terminal blocks:
- Connect Sensor (Optional)
- TSL2591 light sensor connects via I2C:
- VIN → Pi 3.3V or 5V
- GND → Pi GND
- SCL → Pi SCL (GPIO 3)
- SDA → Pi SDA (GPIO 2)
- Can use breadboard or direct wiring
- TSL2591 light sensor connects via I2C:
- Connect Solenoid Driver
- Adafruit I2C 8-Channel Solenoid Driver connects via I2C:
- VIN → Pi 3.3V or 5V
- GND → Pi GND
- SCL → Pi SCL (GPIO 3)
- SDA → Pi SDA (GPIO 2)
- Connect 12V power supply to driver’s power input
- Connect solenoid valves to channels 1-8
- All I2C devices share the same SCL/SDA bus
- Adafruit I2C 8-Channel Solenoid Driver connects via I2C:
- Connect Sensor Multiplexer (Optional)
- For multiple sensors, connect I2C multiplexer (e.g., TCA9548A)
- Each sensor gets its own multiplexer channel
- Configure test-to-sensor mapping in
mappings.json - System will automatically switch sensors based on test being run
I2C Addresses
- Motor HAT (PCA9685): 0x60 (default)
- Motor HAT (PWM): 0x70 (auxiliary chip)
- TSL2591 Sensor: 0x29 (default)
- Solenoid Driver (MCP23017): 0x20 (default)
- Sensor Multiplexer (TCA9548A): 0x70 (configurable to avoid conflicts)
Software Setup
Choose your platform:
Raspberry Pi OS Setup
1. Prepare System
# Update system
sudo apt update
sudo apt upgrade -y
# Install required packages
sudo apt install -y python3-pip python3-dev i2c-tools git
2. Enable I2C
sudo raspi-config
# Navigate to: Interface Options → I2C → Enable
# Reboot
sudo reboot
3. Clone/Copy Project Files
# Option A: Clone from git
git clone <your-repo-url>
cd open_colorimeter_pi
# Option B: Copy files manually
# scp -r /path/to/open_colorimeter_pi pi@raspberrypi.local:~/
4. Install Python Dependencies
Option A: Using Virtual Environment (Recommended)
# Install venv support
sudo apt install python3-venv
# Create virtual environment
cd ~/open_colorimeter_pi
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
Option B: System-wide Installation
pip3 install -r requirements.txt
5. Verify I2C Devices
# Check that sensor and motor controller are detected
sudo i2cdetect -y 1
# Should show:
# 0x20 (Solenoid driver - MCP23017) - if connected
# 0x29 (TSL2591 sensor) - if connected
# 0x60 (Motor HAT - PCA9685)
# 0x70 (Motor HAT - additional chip)
#
# Example output with all devices:
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
# 00: -- -- -- -- -- -- -- -- -- -- -- -- --
# 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 20: 20 -- -- -- -- -- -- -- -- 29 -- -- -- -- -- --
# 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 70: 70 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
6. Run the Application
If using virtual environment:
# The start.sh script will automatically activate venv if it exists
./start.sh
# Or manually:
source venv/bin/activate
python app.py
If installed system-wide:
python3 app.py
7. Access Web Interface
Open browser to: http://raspberrypi.local:5000
Or use the Pi’s IP address: http://192.168.1.XXX:5000
Ubuntu Server Setup
1. Prepare System
# Update system
sudo apt update
sudo apt upgrade -y
# Install required packages
sudo apt install -y python3 python3-pip python3-dev i2c-tools git build-essential
2. Enable I2C
# Load I2C kernel modules
sudo modprobe i2c-dev
# Make I2C load on boot
echo "i2c-dev" | sudo tee -a /etc/modules
# Add user to i2c group for permissions
sudo usermod -aG i2c $USER
# If i2c group doesn't exist, create it
sudo groupadd -f i2c
# Set I2C device permissions
echo 'KERNEL=="i2c-[0-9]*", GROUP="i2c", MODE="0660"' | sudo tee /etc/udev/rules.d/99-i2c.rules
# Reload udev rules
sudo udevadm control --reload-rules
sudo udevadm trigger
# Reboot to apply changes
sudo reboot
3. Clone/Copy Project Files
# Clone from git
git clone <your-repo-url>
cd open_colorimeter_pi
# Or copy files via scp, etc.
4. Install Python Dependencies
Option A: Using Virtual Environment (Recommended for Ubuntu 24.04+)
# Install venv support
sudo apt install python3-venv python3-full
# Create virtual environment
cd ~/open_colorimeter_pi
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate
# Upgrade pip and install dependencies
pip install --upgrade pip
pip install -r requirements.txt
Option B: System-wide Installation (may require –break-system-packages on newer Ubuntu)
# Upgrade pip first
pip3 install --upgrade pip
# Install project dependencies
pip3 install -r requirements.txt
Note: Ubuntu 24.04+ uses Python 3.13 which enforces PEP 668 (externally managed environments). Virtual environments are strongly recommended.
5. Verify I2C Devices
# Check available I2C buses
ls /dev/i2c-*
# Scan for devices (usually bus 1 on Pi, may vary on other boards)
sudo i2cdetect -y 1
# Should show:
# 0x20 (Solenoid driver - MCP23017) - if connected
# 0x29 (TSL2591 sensor) - if connected
# 0x60 (Motor HAT - PCA9685)
# 0x70 (Motor HAT - additional chip)
#
# Example output with all devices:
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
# 00: -- -- -- -- -- -- -- -- -- -- -- -- --
# 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 20: 20 -- -- -- -- -- -- -- -- 29 -- -- -- -- -- --
# 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 70: 70 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# If not on bus 1, try bus 0 or other buses
sudo i2cdetect -y 0
6. Run the Application
If using virtual environment:
# The start.sh script will automatically activate venv if it exists
./start.sh
# Or manually:
source venv/bin/activate
python app.py
If installed system-wide:
python3 app.py
7. Access Web Interface
Open browser to: http://<your-server-ip>:5000
Example: http://192.168.1.100:5000
Optional: Auto-start on Boot
Automated Installation (Recommended)
Use the provided installation script to automatically set up the service:
cd ~/open_colorimeter_pi
./install_service.sh
The script will:
- Detect if you’re using a virtual environment
- Create the systemd service file with correct paths
- Enable and start the service automatically
- Show you the web interface URLs
That’s it! The service is now running and will start automatically on boot.
Manual Installation (Alternative)
If you prefer to set up the service manually:
Create systemd service (works on both Raspberry Pi OS and Ubuntu)
sudo nano /etc/systemd/system/colorimeter.service
Add this content:
If using virtual environment:
[Unit]
Description=Open Colorimeter Web Interface
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/open_colorimeter_pi
ExecStart=/bin/bash -c 'source /home/pi/open_colorimeter_pi/venv/bin/activate && python app.py'
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
If using system Python:
[Unit]
Description=Open Colorimeter Web Interface
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/open_colorimeter_pi
ExecStart=/usr/bin/python3 /home/pi/open_colorimeter_pi/app.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Note: Change User=pi and paths if your username or install location is different.
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable colorimeter
sudo systemctl start colorimeter
# Check status
sudo systemctl status colorimeter
# View logs
sudo journalctl -u colorimeter -f
Service Management Commands
# Check status
sudo systemctl status colorimeter
# View logs
sudo journalctl -u colorimeter -f
# Stop service
sudo systemctl stop colorimeter
# Restart service
sudo systemctl restart colorimeter
# Disable auto-start
sudo systemctl disable colorimeter
# Uninstall service (automated)
./uninstall_service.sh
# Or manually remove service
sudo systemctl stop colorimeter
sudo systemctl disable colorimeter
sudo rm /etc/systemd/system/colorimeter.service
sudo systemctl daemon-reload
Updating the Application
Automated Update (Recommended)
When you push new changes to the repository, update your Raspberry Pi with:
cd ~/open_colorimeter_pi
./update.sh
The update script will:
- Pull latest changes from git
- Install any new dependencies
- Restart the service automatically
Manual Update (Alternative)
cd ~/open_colorimeter_pi
git pull
pip install -r requirements.txt # or: source venv/bin/activate && pip install -r requirements.txt
sudo systemctl restart colorimeter
Configuration
Device Names and Descriptions
You can customize the names and descriptions of motors, solenoids, and tests by editing mappings.json:
{
"motors": {
"1": {
"name": "Sample",
"description": "Sample pump"
},
"2": {
"name": "Reagent",
"description": "Reagent pump"
}
},
"solenoids": {
"1": {
"name": "Inlet Valve",
"description": "Sample inlet"
}
},
"tests": {
"Phosphate": {
"sensor": 1,
"calibration": "Phosphate"
},
"Nitrate": {
"sensor": 2,
"calibration": "Nitrate"
}
}
}
Test Configuration:
- Each test maps to a sensor number (for multiplexer support)
- Specifies which calibration curve to use
- Test names can be used in sequences with
measure TestName
Web-Based Config Editor
You can edit all three configuration files directly from the web interface:
- Go to the Config tab
- Edit
mappings.json,sequences.json, orcalibrations.jsonin the text editors - Click the corresponding Save button for each file
- The system validates JSON syntax before saving
- Changes are applied immediately (device names may require page reload)
Benefits:
- No need to SSH into the Raspberry Pi
- Syntax validation prevents errors
- Edit all three files in one place
- Auto-formatted JSON with proper indentation
- Separate files for better organization
Usage
- Navigate to the web interface
- Take measurements with the “Measure” button
- Control motors from the Motors tab
- Control solenoids from the Solenoids tab (8 channels)
- Create and save automated sequences in the Sequences tab
- Load saved sequences from the dropdown menu
- Manage calibrations in the Calibrations tab
- Edit configuration files in the Config tab (web-based JSON editor)
- Customize device names, test definitions, sequences, and calibration curves
Command Sequences
The Sequences tab allows you to automate multiple device operations. Enter commands one per line:
Motor Commands:
Sample, 30 # Run Sample motor forward for 30 seconds
Reagent, -15 # Run Reagent motor reverse for 15 seconds
Solenoid Commands:
Solenoid 1, on # Turn on Solenoid 1
Solenoid 1, off # Turn off Solenoid 1
Wait Commands:
wait, 5 # Wait 5 seconds before next command
Sensor Commands:
blank # Blank the sensor (takes ~1 second)
measure Phosphate # Take a measurement for specific test
measure Nitrate # Measure different test (switches to configured sensor)
measure # Measure all calibrations on current sensor
Complete Example:
Sample, 30
Solenoid 1, on
wait, 5
Solenoid 1, off
Reagent, -15
wait, 10
blank
wait, 2
measure Phosphate
Multiple Tests Example:
blank
measure Phosphate
wait, 5
blank
measure Nitrate
The sequence will execute each command in order, showing progress in real-time. When using measure TestName, the system will automatically use the sensor and calibration configured for that test (useful when you have a multiplexer with multiple sensors). Measurement results (absorbance and calibrated values) are displayed in the status log. Use the Stop button to cancel execution.
Saved Sequences
Save frequently-used sequences for quick access:
- Create a sequence - Type commands in the editor
- Click “Save As…“ - Give it a name and description
- Load anytime - Select from dropdown and click “Load”
- Delete - Select and click “Delete” to remove
Saved sequences are stored in sequences.json:
{
"sequences": {
"Phosphate Test": {
"description": "Full phosphate measurement cycle",
"commands": [
"Sample, 30",
"Solenoid 1, on",
"wait, 5",
"Solenoid 1, off",
"Reagent, -15",
"wait, 10",
"blank",
"wait, 2",
"measure Phosphate"
]
},
"Nitrate Test": {
"description": "Nitrate test sequence",
"commands": [
"Sample, 25",
"blank",
"measure Nitrate"
]
}
}
}
This allows you to:
- Build a library of standard operating procedures
- Share sequences between team members
- Quickly switch between different test protocols
- Backup sequences in version control
Project Files
Core Application:
app.py- Flask web server and REST API endpointscolorimeter.py- Hardware control (sensor, motors, solenoids)requirements.txt- Python dependencies
Configuration:
mappings.json- Device names, test definitions, sensor mappingssequences.json- Saved command sequencescalibrations.json- Calibration curves for each test
Web Interface:
templates/index.html- Main web page with tabsstatic/style.css- Styling (ocean blue theme)static/script.js- Interactive controls and sequence execution
Automation Scripts:
install_service.sh- Automated systemd service installationuninstall_service.sh- Remove serviceupdate.sh- Pull changes and restart servicestart.sh- Manual start with venv support
Example Configuration:
open_colorimeter_pi/
├── app.py # Flask server
├── colorimeter.py # Hardware control
├── mappings.json # Device names & test mappings
├── sequences.json # Saved command sequences
├── calibrations.json # Calibration curves
├── requirements.txt # Dependencies
├── install_service.sh # Service installer
├── update.sh # Update script
├── templates/
│ └── index.html # Web UI
└── static/
├── style.css # Styles
└── script.js # Frontend logic
Troubleshooting
Motor HAT Not Detected (No 0x60 in i2cdetect)
Check Physical Connection:
# Power off Pi
sudo shutdown -h now
# Check that Motor HAT is properly seated on GPIO pins
# Reseat the HAT if necessary
# Power back on
Check I2C is Enabled:
# Raspberry Pi OS
sudo raspi-config
# Interface Options → I2C → Enable
# Ubuntu - check if i2c-dev module is loaded
lsmod | grep i2c
Motors Not Running
Check Power:
- Verify 12V power supply is connected to Motor HAT green terminal
- Check polarity (+ and -)
- Motor power is separate from Pi power
Check Motor Connections:
- Motors connected to blue terminal blocks (M1, M2, etc.)
- Terminals are screw terminals - make sure wires are secure
Test in Web Interface:
- Go to Motors tab
- Move slider for Motor 1
- If motor doesn’t move, try swapping motor wires (reverses direction)
Web Interface Not Accessible
Check Flask is Running:
# Check if app is running
ps aux | grep python
# Check what's using port 5000
sudo netstat -tulpn | grep 5000
Check Firewall (Ubuntu):
sudo ufw status
sudo ufw allow 5000
Access from Same Network:
- Make sure your device is on the same WiFi/network as the Pi
- Try using Pi’s IP address:
http://192.168.1.XXX:5000 - Check Pi’s IP:
hostname -I
Motor Direction is Backwards
Simply swap the two motor wires on the terminal block - this reverses the direction.
Python Package Errors
If using Ubuntu 24.04+ and getting externally-managed-environment error:
# Use virtual environment (recommended)
cd ~/open_colorimeter_pi
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Quick Reference
Common Commands:
# First-time setup
./install_service.sh
# Update application
./update.sh
# Service management
sudo systemctl status colorimeter
sudo systemctl restart colorimeter
sudo journalctl -u colorimeter -f
# Manual run
source venv/bin/activate
python app.py
Web Interface:
- Local:
http://raspberrypi.local:5000 - Network:
http://[PI_IP_ADDRESS]:5000 - Find IP:
hostname -I
API Endpoints:
GET /api/status- System statusGET /api/measure- Current measurementPOST /api/blank- Blank sensorGET /api/configuration- Device configurationGET /api/calibrations- Available calibrationsGET /api/config/load- Load config files for editingPOST /api/mappings/save- Save mappings.jsonPOST /api/sequences/save- Save sequences.jsonPOST /api/calibrations/save- Save calibrations.jsonPOST /api/motor/<num>/throttle- Control motorPOST /api/solenoid/<num>/set- Control solenoid
Configuration Files:
mappings.json- Device names and test definitionssequences.json- Saved command sequencescalibrations.json- Calibration curves
Support
For issues or questions:
- Check the GitHub Issues
- Review the Adafruit Motor HAT Guide
- Review the Adafruit Solenoid Driver Guide