Technical reference

How drDRO works under the hood

The architecture, the RS-485 line protocol that replaced Modbus, how settings are split between the board and the host, and the firmware-update flow.

Architecture

drDRO is a three-part system connected by one serial wire:

Host app

drdro-software-f4

The Kivy UI and all display / motion logic. Owns the serial link and the unit-dependent ratio maths.

Firmware

drdro-firmware-f4

STM32F411: reads scales, drives the servo, keeps persistent settings, and runs a dual-bank IAP bootloader.

Appliance

drdro-arch

The bootable Arch Linux ARM SD image that runs the host app on a Raspberry Pi.

The host talks to the board over a single half-duplex RS-485 bus at 115200 baud with auto-direction transceivers — one device, no addressing. A 30 Hz poll loop reads live state; UI actions and firmware transfers are serialised on the same wire through an async command queue so nothing interleaves.


The RS-485 line protocol

Instead of Modbus registers, the firmware speaks a compact, CLI-friendly text protocol. Access is by named variable, not register address.

Wire format

request → response
# Request:  command [args] [*HH]\n     (*HH = optional XOR-8 checksum)
get servo.max
# Response: key=value lines, a crc line, then a blank line
servo.max=3000.0
crc=1B
  • Terminators \r, \n or \r\n are all accepted; a lone \n repeats the last command.
  • The response ends with a crc=HH line (XOR-8 of the body) and a blank line; an error=… line means failure.
  • Arrays come back as one comma-joined line, e.g. scales.pos=12345,988,0,42.

App commands

sta   set   get   settings   save   load   bank   rollback   version   help   update   reset

The 30 Hz hot loop uses a single sta round-trip (scale positions & speeds, servo position / speed / target / mode); it benchmarks well over 100 Hz, leaving headroom to interleave other requests.

Variable registry

The variables the host reads and writes by name (mapped to firmware fields):

VariableTypeNR/WMeaning
scales.posi324RWScale positions
scales.speedi324ROScale speeds
scales.num / scales.deni324RWSync ratio numerator / denominator
scales.syncu164RWPer-scale sync enable
scales.filtu164RWEncoder input filter (0–15)
servo.maxf321RWMax speed (steps/s) — flash-stored
servo.accf321RWAcceleration — flash-stored
servo.jogf321RWJog speed (live)
servo.idxf321RWIndexing feedrate cap
servo.modeu161RW0=off, 1=sync/index, 2=jog
servo.pos / servo.speed1ROLive servo position / speed
servo.tgti321RWSteps-to-go (write = start indexed move)
diag.cycles / diag.intervalu321ROLoop diagnostics

Board vs host settings

Values that change rarely (only on a mechanical change) live in the board's flash and are the source of truth; frequently-changing and host-only values stay on the host.

On the board (flash)

  • servo.max, servo.acc — read on connect, set+save on change.
  • scales.filt — encoder filter, re-applied on connect.
  • Persisted in ping-pong A/B sectors with magic + CRC32, power-fail safe, and a motion-safe save.

On the host (YAML / ini)

  • Dynamic sync ratios (scales.num/den) — unit-dependent, pushed live, never saved to flash.
  • Axis names, transforms, offsets, tool state, ELS roles.
  • Formats, colours, fonts, display prefs; serial port & baud.

On connect the app reads the board's persisted values and syncs the UI to them, rather than re-pushing config every reconnect. Machine profiles snapshot both sides together.

Firmware update flow

Firmware is flashed over the same RS-485 wire via the board's bootloader — no ST-Link required.

  1. Enter the bootloader

    The app sends update; the board jumps to the bootloader and greets with bootloader=ready.

  2. Pick the inactive bank

    info reports the active / loaded banks and their validity; the updater targets the inactive one.

  3. Transfer the image

    flash <bank> streams the firmware .bin over YMODEM (1024-byte STX blocks, CRC-16, EOT handshake) with a live progress bar.

  4. Select & boot

    bank <bank> marks it active (persisted), then boot copies it to Exec and jumps in. rollback reverses it.

The dual-bank design means a bad flash never bricks the board — the previous bank is always intact and selectable.


Config & paths

PathWhat
config.ini (repo root)Serial port, baud, mode, scale count, display colour.
~/.config/drdro-software/Per-instance YAML for axes, inputs, servo, ELS, formats.
~/.config/drdro-software/profiles/Saved machine profiles.
/opt/drdro/app (appliance)The installed app + its .venv on the Pi image.
/var/log/drdro/ (appliance)App / Kivy logs on the Pi image.

Coming from rotary-controller-python?

drDRO is RCP with the driver layer swapped. If you're migrating:

  • New firmware required — the RS-485 line protocol, not the old Modbus firmware.
  • No Modbus address — one device on the bus; config.ini drops the address key.
  • Board-stored settings — servo speed/accel now read from board flash on connect.
  • New config dir~/.config/drdro-software/ (RCP YAML isn't auto-migrated; copy it over).
  • New: firmware updates from the UI — flash the board over RS-485.