- Quick start section at the top (3 commands) - Removed inline Bullseye build recipe (moved to 'why not' appendix) - Added build time estimates - Cleaner table formatting - gplan.so contents documented (cbang + camotics)
8.6 KiB
Onefinity CNC Firmware — Build, Flash & Backup
Quick Start
# 1. Build gplan.so (first time ~25min, then ~1sec)
.pi/build-gplan.sh
# 2. Build firmware package (frontend + AVR + Python, ~1min)
docker run --rm -v "$(pwd):/workspace" -w /workspace onefin-dev \
bash -c 'make all && python3 ./setup.py sdist'
# 3. Flash to controller
curl -X PUT -F "firmware=@dist/bbctrl-1.6.7.tar.bz2" \
-F "password=onefinity" http://10.1.10.55/api/firmware/update
Architecture Overview
The controller is a Raspberry Pi 2/3 (armv7l, Raspbian Stretch, Python 3.5) connected to an ATxmega192a3u AVR over serial. The Pi runs a Tornado web server that serves the UI, parses G-code, and plans motion. The AVR executes realtime step/direction pulses.
Browser ←WebSocket→ Pi (Tornado/Python) → GCode Planner → Serial → AVR → Stepper drivers
The firmware package (bbctrl-X.Y.Z.tar.bz2) contains:
| Component | Source | Description |
|---|---|---|
| Python backend | src/py/bbctrl/ |
Tornado web server, state machine, planner bridge |
| Web frontend | build/http/ |
Pug + Stylus + Svelte → static HTML/JS/CSS |
| AVR firmware | src/avr/bbctrl-avr-firmware.hex |
Realtime motion controller |
| gplan.so | src/py/camotics/gplan.so |
CAMotics G-code planner (native ARM C++ extension) |
| Install scripts | scripts/install.sh |
AVR flash, Python install, service restart |
Prerequisites
- Docker with QEMU binfmt support (default on Docker Desktop for Mac)
- devcontainer image:
docker build -t onefin-dev -f .devcontainer/Dockerfile .devcontainer/ - SSH access:
ssh bbmc@10.1.10.55(password:onefinity)
Building
Step 1: gplan.so
gplan.so is the CAMotics G-code planner — a C++ Python extension that must
be a 32-bit ARM binary linked against Python 3.5. It cannot be built in the
devcontainer (wrong arch + wrong Python + wrong glibc).
Build from source (recommended):
.pi/build-gplan.sh
This uses a Raspbian Stretch Docker image (balenalib/raspberry-pi-debian:stretch)
with the Pi's exact toolchain: GCC 6.3, Python 3.5, GLIBC 2.24. The image is
built once (~25min under QEMU), then cached — subsequent runs take ~1sec.
The image pre-compiles two C++ dependencies:
- cbang @
18f1e96— C++ utility library - camotics @
ec876c8— G-code planner with S-curve motion planning
To force a full rebuild: docker rmi onefin-gplan && .pi/build-gplan.sh
Alternatives (if Docker build fails):
# From official release
curl -sL https://github.com/OneFinityCNC/onefinity-firmware/releases/download/v1.6.6/onefinity-1.6.6.tar.bz2 \
| tar xjf - --include='*/gplan.so' -O > src/py/camotics/gplan.so
# From the running Pi
scp bbmc@10.1.10.55:/usr/local/lib/python3.5/dist-packages/bbctrl-*.egg/camotics/gplan.so src/py/camotics/
Verify — must show ELF 32-bit ... ARM ... libpython3.5m:
file src/py/camotics/gplan.so
readelf -d src/py/camotics/gplan.so | grep python
Step 2: Firmware package
docker run --rm -v "$(pwd):/workspace" -w /workspace onefin-dev \
bash -c 'make all && python3 ./setup.py sdist'
This builds inside the devcontainer (arm64 Bullseye — fine for frontend/AVR/Python):
| Component | Tool | Time |
|---|---|---|
| Node modules | npm install |
~30sec |
| Svelte components | vite build |
~5sec |
| Pug/Stylus → HTML | pug-cli, stylus |
~2sec |
| AVR firmware | avr-g++ (ATxmega192a3u) |
~10sec |
| Boot/Power/Jig MCUs | avr-gcc |
~5sec |
| Python sdist | setup.py sdist |
~2sec |
Produces: dist/bbctrl-X.Y.Z.tar.bz2 (~3-4MB)
bbserial.ko (kernel module — usually skip)
Cross-compiles against the Pi's kernel headers (4.9.59-v7+). The Pi already has
a working bbserial.ko installed. install.sh skips it gracefully if missing.
Flashing
Via web API (machine running)
curl -X PUT -F "firmware=@dist/bbctrl-1.6.7.tar.bz2" \
-F "password=onefinity" http://10.1.10.55/api/firmware/update
Or: make update HOST=10.1.10.55
Via SSH (web UI down or crash-looping)
scp dist/bbctrl-1.6.7.tar.bz2 bbmc@10.1.10.55:/tmp/
ssh bbmc@10.1.10.55 'echo onefinity | sudo -S bash -c "
systemctl stop bbctrl
mkdir -p /var/lib/bbctrl/firmware
cp /tmp/bbctrl-1.6.7.tar.bz2 /var/lib/bbctrl/firmware/update.tar.bz2
/usr/local/bin/update-bbctrl
"'
What happens during flash
update-bbctrlstops bbctrl, extracts tarball to/tmp/update/install.shruns:- Flashes AVR via
scripts/avr109-flash.py(serial bootloader protocol) setup.py install --force— installs Python package + frontend + gplan.so- Restarts
bbctrlsystemd service - May reboot if boot config or kernel module changed
- Flashes AVR via
Recovery from bad flash
SSH still works even when bbctrl is crash-looping:
- Check the error:
sudo python3 /usr/local/bin/bbctrl 2>&1 | head -20 - Common cause: wrong gplan.so architecture → replace with correct one (see above)
- Nuclear option: restore SD card from backup
Running Locally (demo mode)
Full stack in Docker with AVR emulator — no Pi needed:
# Build bbemu (AVR emulator, native in devcontainer)
docker run --rm -v "$(pwd):/workspace" -w /workspace/src/avr/emu onefin-dev make
# Run demo (needs arm64 gplan.so for the container, not armhf)
docker run --rm -d --name onefin-demo \
-v "$(pwd):/workspace" -w /workspace -p 8765:80 \
onefin-dev bash -c '
pip3 install -q tornado sockjs-tornado pyserial watchdog
cp src/avr/emu/bbemu /usr/local/bin/
pip3 install -q -e .
exec bbctrl --demo --port 80 --addr 0.0.0.0 --disable-camera
'
Open http://localhost:8765 — full UI with emulated controller.
Note: demo mode needs a container-arch gplan.so (arm64 + Python 3.9), not the
Pi one. The devcontainer build from the Makefile's gplan target produces this,
or it can be built following the procedure in scripts/gplan-build.sh.
SD Card Backup & Restore
# Backup (~50 min, streams raw dd from Pi, compresses locally)
./backup/onefinity-backup.sh backup
# Verify
./backup/onefinity-backup.sh verify backup/onefinity-20260430.img.gz
# Restore to local SD card
./backup/onefinity-backup.sh restore backup/onefinity-20260430.img.gz /dev/diskN
# Restore back to Pi over SSH
./backup/onefinity-backup.sh restore backup/onefinity-20260430.img.gz
Environment: ONEFINITY_HOST (default 10.1.10.55), ONEFINITY_USER (bbmc),
ONEFINITY_PASS (onefinity).
Python 3.5 Compatibility
The Pi runs Python 3.5.3. Avoid features added in later versions:
| Avoid | Use instead |
|---|---|
f"hello {name}" |
"hello %s" % name or "hello {}".format(name) |
subprocess.run(capture_output=True) |
stdout=subprocess.PIPE, stderr=subprocess.PIPE |
subprocess.run(text=True) |
.stdout.decode('utf-8') |
dataclasses |
plain classes with __init__ |
:= walrus operator |
separate assignment |
asyncio.run() |
loop.run_until_complete() |
dict[str, int] |
Dict[str, int] from typing |
Pi Details
| Host | 10.1.10.55 |
| SSH | bbmc / onefinity |
| OS | Raspbian Stretch (Debian 9) |
| Kernel | 4.9.59-v7+ |
| Python | 3.5.3 |
| GCC | 6.3.0 |
| GLIBC | 2.24 (max symbol: GLIBC_2.24) |
| GLIBCXX | 3.4.22 |
| Arch | armv7l (32-bit ARM, EABI5) |
| SD card | 30GB (~2.8GB used) |
| Service | systemctl {start,stop,restart,status} bbctrl |
| Log | /var/log/bbctrl.log or journalctl -u bbctrl |
| Config | /var/lib/bbctrl/config.json |
| Uploads | /var/lib/bbctrl/upload/ |
| Web root | /usr/local/lib/python3.5/dist-packages/bbctrl-*.egg/bbctrl/http/ |
| AVR serial | /dev/ttyAMA0 at 230400 baud |
Why Not Build gplan.so on Bullseye?
Documented for reference — we tried two approaches that don't work:
1. devcontainer (arm64 Bullseye): Wrong ELF class (64-bit vs 32-bit) and wrong
Python (3.9 vs 3.5). Cross-compiling with CXX=arm-linux-gnueabihf-g++ fails
because SCons ignores CC/CXX environment variables.
2. Bullseye armhf container: Correct architecture, but GCC 10 / glibc 2.31
produce objects requiring GLIBC_2.29+ and GLIBCXX_3.4.26+ symbols. The Pi's
Stretch only has GLIBC_2.24 / GLIBCXX_3.4.22. Even -static-libstdc++ -static-libgcc doesn't help — glibc symbols leak through the object files.
Relinking against Python 3.5m works but the GLIBC mismatch remains.
3. Plain debian:stretch armhf: The archived repos have broken package
metadata — apt-get install build-essential fails with unresolvable version
conflicts.
Solution: balenalib/raspberry-pi-debian:stretch with legacy.raspbian.org
repos. See .pi/Dockerfile.gplan.