Skip to content

computed-axial-lithography/OpenCAL

Repository files navigation

OpenCAL — Open-Source Computed Axial Lithography Software

OpenCAL is open-source software for building and operating a Computed Axial Lithography (CAL) 3D printer — a volumetric, layer-less resin printing technique. It is designed to run headless on a Raspberry Pi 5 and is controlled through a hardware LCD/encoder interface.

This project is in active development. Feedback and contributions are very welcome.

Links:


Table of Contents

  1. Overview
  2. Hardware Requirements
  3. Quick Start — Flashing the Pre-Built Image
  4. Building from Source
  5. Configuration
  6. Running as a systemd service
  7. Usage
  8. Video File Naming Convention
  9. Contributing
  10. License

Overview

OpenCAL runs on a Raspberry Pi 5 and orchestrates all hardware needed for a CAL print job. All parameters are tunable via opencal/utils/config.json.

Key components:

  • LCD GUI (gui/lcd_gui.py) — a state-machine menu controller for a 20×4 I2C LCD display. Drives all user interaction via a rotary encoder: file selection, settings, manual motor/LED control, alignment, and print start/stop.
  • Pygame GUI (gui/pygame_app.py) — manages the projector display for precise interactive visuals during calibration and printing.
  • Print Controller (hardware/print_controller.py) — orchestrates a full print job: spins up the stepper motor, activates LEDs, plays the video via mpv, and records via the camera.
  • Hardware Controller (hardware/hardware_controller.py) — initializes all hardware at startup; failures are caught individually so the system continues in degraded mode rather than crashing.
  • Stepper Controller (hardware/stepper/tic_usb.py) — controls the Pololu Tic T249 stepper motor controller over USB.
  • Projector Controller (hardware/projector_controller.py) — mpv-based video playback with crop/zoom calibration for the print resin vial.
  • Camera Controller (hardware/camera_controller.py) — picamera2-based capture and H264 video recording.
  • config.json (opencal/utils/config.json) — single source of truth for all GPIO pins, I2C addresses, LED counts, camera type, default RPM, and projector calibration values.
  • tic_settings.yaml (opencal/utils/tic_settings.yaml) — Pololu Tic T249 motor controller settings (current limit, step mode, acceleration) applied automatically on every startup.

GUI features:

  1. Print from USB — automatically reads RPM from filename if present (see naming convention)
  2. Manual Control — LEDs, stepper motor, image capture to USB
  3. Settings — alignment tool, vial width finder, calibration images, USB video save prompt
  4. Power Options — restart, power off, kill GUI
  5. About — credits

Hardware Requirements

Component Specification
Computer Raspberry Pi 5 (2GB+ RAM)
Storage microSD card ≥ 12GB
Motor Controller Pololu Tic T249 (USB connection)
Stepper Motor NEMA 17 (tested with 17HE19-2004S, 2.0A rated)
LED Array 8×8 SK6812 RGBW NeoPixel matrix (64 LEDs)
Display 20×4 I2C LCD (PCF8574 backpack, address 0x27)
Encoder Rotary encoder with push button (GPIO 5, 6, 19)
Camera Raspberry Pi Camera Module 3 (IMX708)
Projector Any HDMI projector (tested with NexiGo Nova Mini)

Dependencies (installed automatically on the pre-built image):

  • Pololu ticcmd — command-line tool for the Tic motor controller
  • pi5neo — NeoPixel/SK6812 control via SPI
  • picamera2 / libcamera — Raspberry Pi camera stack
  • mpv — video playback for the projector
  • udiskie — USB drive automounting

Quick Start — Flashing the Pre-Built Image

The easiest way to get started is to flash the pre-built Raspberry Pi 5 image directly to a microSD card.

Download: opencal_pi5.img.gz on Google Drive

Steps:

  1. Download opencal_pi5.img.gz from the link above
  2. Download and install Raspberry Pi Imager
  3. Open Raspberry Pi Imager
  4. Click "Choose OS""Use custom" → select opencal_pi5.img.gz
  5. Click "Choose Storage" → select your microSD card (≥ 12GB)
  6. Click "Write" and wait for it to complete
  7. Insert the SD card into your Raspberry Pi 5 and power on

The system will boot directly into OpenCAL. Default login credentials:

  • Username: opencal
  • Password: Set on first login (you will be prompted to change it)

Note: The pre-built image already has the systemd service enabled and all hardware configured — you do not need to run any additional setup commands. The only things intentionally cleared from the image for security are WiFi credentials and SSH host keys (regenerated automatically on first boot).

WiFi setup: Place a wifi.txt file in the boot partition with the contents:

SSID=YourNetworkName
PASS=YourPassword

Building from Source

Prerequisites: Raspberry Pi 5 running Raspberry Pi OS Bookworm

# Clone the repo
git clone https://github.com/computed-axial-lithography/OpenCAL.git
cd OpenCAL

# Install Python dependencies
pip install -r requirements.txt --break-system-packages

# Install Pololu Tic software (ticcmd)
# Download the ARM64 package from https://www.pololu.com/docs/0J71/3.2
# and install it with: sudo dpkg -i pololu-tic-*.deb

# Install udiskie for USB automounting
sudo apt install udiskie

Enable the user systemd service to start on boot:

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/opencal.path << 'EOF'
[Unit]
Description=Start OpenCAL when Wayland compositor is ready
[Path]
PathExists=%t/wayland-0
[Install]
WantedBy=default.target
EOF

cat > ~/.config/systemd/user/opencal.service << 'EOF'
[Unit]
Description=OpenCAL Printer Controller
[Service]
Type=simple
WorkingDirectory=/home/opencal/OpenCAL
ExecStart=/usr/bin/python3 -m opencal
Environment=SDL_VIDEODRIVER=wayland
Environment=WAYLAND_DISPLAY=wayland-0
Environment=XDG_RUNTIME_DIR=/run/user/1000
Environment=DISPLAY=:0
StandardOutput=journal
StandardError=journal
Restart=on-failure
RestartSec=5
TimeoutStopSec=10
EOF

systemctl --user enable opencal.path
systemctl --user start opencal.path
loginctl enable-linger opencal

Configuration

Edit opencal/utils/config.json to match your hardware:

{
  "stepper_motor": {
    "driver_mode": "tic_usb",
    "A_pin": 12,
    "B_pin": 13,
    "default_rpm": 9,
    "default_direction": "CW",
    "steps_per_revolution": 3200
  },
  "rotary_encoder": {
    "clk_pin": 5,
    "dt_pin": 6,
    "btn_pin": 19
  }
}

Motor controller settings are stored in opencal/utils/tic_settings.yaml and are automatically written to the Tic on every startup — no manual configuration of the Tic is needed.


Running as a systemd service

OpenCAL uses a user-level systemd path unit that watches for the Wayland display socket and starts the app automatically. See the setup commands in Building from Source above.

To check status or view logs:

systemctl --user status opencal.service
journalctl --user -u opencal.service -f

To temporarily stop the app (e.g. for development):

systemctl --user disable opencal.path
systemctl --user kill -s SIGKILL opencal.service

Re-enable with:

systemctl --user enable opencal.path

Usage

All interaction is through the rotary encoder on the LCD display:

  • Rotate — scroll through menu items or adjust a value
  • Click (press) — select an item, confirm a value, or exit a mode

Main Menu

> Print from USB
  Manual Control
  Settings
  Power Options
  About

Print from USB

Lists all .mp4 files found on the inserted USB drive. Selecting a file:

  1. If the filename encodes an RPM (e.g. part_9rpm.mp4), the motor speed is pre-set automatically.
  2. A RPM adjustment screen appears — rotate to change, click to confirm and start the print.
  3. The projector displays a black image while in this menu — this is intentional to avoid accidentally curing resin while browsing.
  4. A Print Status screen shows while the print is running. Click to stop.
  5. If USB video prompt is enabled (see Settings), you will be asked whether to save the camera recording to USB after stopping.

Note: Files with "recording" in the filename are hidden from this list (they are camera output files). Avoid using "recording" in your print filenames.

Recording format: The camera saves recordings as .h264 (raw H.264 bitstream), not .mp4. To convert for playback on a computer:

ffmpeg -i recording.h264 -c copy recording.mp4

Manual Control

Direct hardware control without running a full print job.

Item Action
Turn on LEDs Turns the LED array on to the default red colour
Turn off LEDs Turns the LED array off
Start stepper Starts the stepper motor rotating (uses last-set RPM)
Stop stepper Stops the stepper motor
Capture image Takes a still image with the camera. Saved to USB with a timestamp filename if a USB drive is mounted, otherwise saved locally. Displays "Image Captured" or "Image error" on the LCD.

Settings

Item Action
Calibration Images Browse and display calibration images from the opencal/utils/calibration/ directory on the projector.
Show Alignment Displays the cross-strut alignment tool image on the projector. Rotate the encoder to shift the image up/down for transverse alignment. Click to return.
USB video prompt Toggles whether you are asked to save the camera recording to USB after each print. Displays current state ("USB prompt: On/Off").
Find Vial Width Opens an interactive projector display showing a white vertical bar. Rotate the encoder to adjust the bar width to match your resin vial diameter. The pixel width is shown as an overlay. Click to confirm and save the value.

Power Options

Item Action
Kill GUI Stops the OpenCAL application without rebooting
Restart Reboots the Raspberry Pi
Power Off Shuts down the Raspberry Pi

About

Displays an animated scrolling credits screen with the project contributors. The LED array runs a blue/gold checkerboard animation while credits are shown. Click to exit at any time.


Video File Naming Convention

OpenCAL reads the RPM value directly from the video filename. Name your files as:

<part_name>_<rpm>rpm.mp4

Examples:

  • cylinder_9rpm.mp4 → motor set to 9 RPM automatically
  • part_v2_12rpm.mp4 → motor set to 12 RPM automatically

If no RPM is found in the filename, the menu opens with the last-used RPM value.


Contributing

Contributions are very welcome! Whether it's opening issues, reporting bugs, or submitting pull requests — all of it helps.

Guidelines:

  • Keep code modularized and flexible via configuration (config.json) rather than hardcoded values.
  • Follow the existing module structure — hardware drivers live in hardware/, GUI logic in gui/, utilities in utils/.
  • If you are unsure about a change or want to discuss a feature before building it, reach out on the Discord server.

Automated image builds are triggered by pushing a version tag of the form v... (e.g. v1.0.0). This kicks off the GitHub Actions workflow that produces a bootable Raspberry Pi 5 SD card image.


License

Copyright © 2026 The Regents of the University of California. All Rights Reserved.

This software is licensed under the UC Regents license — free for educational, research, and non-profit use. Commercial use requires a separate agreement with the UC Office of Technology Licensing.

See LICENSE for the full terms.

About

Opensource CAL 3D printer with configuration flexibility based on commercially available optical components and 3D printed components. Intended for research purposes.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors