Board v2 · Firmware in developmentDraft

Programs

Write motion programs in JSON, load them with upan-cli, and understand how the device checks them before the first step.

View .mdllms.txt

A program is a fixed sequence of motion and I/O steps that the device stores and runs on its own: moves, homing, waits, outputs and counted loops. Programs have no conditions and no variables, so the motion MCU can check the whole program — including every loop iteration — before the motor makes its first step. You write a program in JSON with human units, a host tool compiles it into protocol instructions, and the device either accepts it or tells you exactly which instruction is wrong. A running program keeps running if the USB or Wi-Fi connection drops.

Note: This page follows board revision A and firmware in development. The JSON format below is the one accepted by the host compiler in upan-cli. The network API carries the same instructions in a lower-level JSON form; see Network API.

A first program

{"program": [
  {"move_abs": "10mm", "velocity": "8mm/s"},
  {"loop": 3, "do": [
    {"move_rel": "20mm", "velocity": "10mm/s"},
    {"set_output": 1, "level": true},
    {"wait": "300ms"},
    {"set_output": 1, "level": false}
  ]},
  {"move_abs": "0mm"}
]}

Save it as feed.json. The program uses absolute moves, so home the axis first (or declare the current position with set-position). Then load and run it. The device checks the program on load, checks it again on run, and only then starts moving:

upan-cli -p COM5 home
upan-cli -p COM5 program load feed.json --run

Try it without a board on the simulator, started already homed at 0:

upan-cli -p sim --sim-arg=--homed program load feed.json --run

Check the syntax without any device. The listing shows the instruction numbers that error messages refer to:

upan-cli program check feed.json
#0    move_abs 10000 velocity=8000
#1    loop 3 ×
#2    move_rel 20000 velocity=10000
#3    set OUT1=1
#4    wait 300 ms
#5    set OUT1=0
#6    loop_end
#7    move_abs 0
8 instructions — syntax ok (limits are checked by the device on load and run)

File format

A program file is either a JSON list of steps or an object with a program list. Each step is an object with exactly one instruction key plus its options. Unknown keys are rejected.

Step Example Options
move_abs {"move_abs": "10mm"} velocity, acceleration
move_rel {"move_rel": "-5mm"} velocity, acceleration
home {"home": true} —
wait {"wait": "500ms"} —
wait_input {"wait_input": 2, "level": true, "timeout": "5s"} level (default true), timeout (default none)
set_output {"set_output": "OUT1", "level": true} level (required)
loop {"loop": 10, "do": [ … ]} do: list of steps
move_linear {"move_linear": {"0": "120mm", "1": "40mm"}} by, velocity, acceleration

Steps in detail

  • move_abs — move to a position. Needs a homed axis (see Rules).
  • move_rel — move by a signed distance. Works without homing.
  • home — run homing with the method from HardConfig. Refused if the method is HOME_NONE. See Configuration.
  • wait — pause for a time.
  • wait_input — wait until an input reaches level. With a timeout, the program is aborted when it expires (EVENT_PROGRAM_ABORTED with error ERROR_TIMEOUT). Without one, it waits indefinitely.
  • set_output — switch an output on (true) or off (false).
  • loop — repeat the steps in do a fixed number of times (at least 1).
  • move_linear — move several axes of a module stack together along a straight line (see Multi-axis steps).

velocity and acceleration are optional. If omitted, the device uses velocity_default and acceleration_default from SoftConfig. If given, they must be greater than zero.

Inputs and outputs are numbered as printed on the board: 1, 2, or "IN1", "OUT2". Board revision A has two inputs and two outputs. The protocol numbers them from 0; the compiler converts.

Units in strings

Kind Linear axis Rotary axis Plain number means
Distance, position "10mm", "250um", "1.5cm", "0.5in" "90deg", "500mdeg", "1rev" mm (linear) or degrees (rotary)
Velocity "8mm/s" "30deg/s" mm/s or deg/s
Acceleration "40mm/s2" (also mm/s^2, mm/s²) "100deg/s2" mm/s² or deg/s²
Time "300ms", "2s", "1min" same ms

The compiler converts with exact decimal arithmetic and rounds to the nearest base unit (µm or millidegree). Inches are exact: 1 in = 25 400 µm. Using a rotary unit on a linear axis, or the other way round, is an error.

Multi-axis steps

In a stack of UPAN Core and UPAN Axis modules, move_linear moves several axes so that they start together, travel proportionally and stop together.

{"program": [
  {"move_linear": {"0": "120mm", "1": "40mm"}, "velocity": "10mm/s"},
  {"wait": "1s"},
  {"move_linear": {"1": "-5mm", "2": "5mm"}, "by": true}
]}
  • Keys are axis numbers: 0 is the UPAN Core, 1…7 are UPAN Axis modules (the bus_node setting of each module).
  • Values are positions. With "by": true they are distances, and no homing is needed.
  • velocity and acceleration apply along the line. The line length is the square root of the sum of the squared axis distances.
  • Each axis may appear only once in a step.

The host still talks only to the Core; the Core coordinates the other axes over the module bus. Programs with move_linear are checked against the limits of every axis involved, see below.

Size and nesting limits

Limit Value
Instructions per program 256
Encoded program size 6 KB
Loop nesting depth 4
Loop count ≥ 1

A loop step compiles into two instructions (begin and end), so it counts twice. A program over the size limit is refused with ERROR_PROGRAM_TOO_LARGE. The host splits the upload into chunks that fit one protocol frame; you do not need to handle this yourself.

Rules checked by the device

The host compiler checks only the shape of the file. Limits, travel and loop structure are checked by the motion MCU, which trusts no host. The check happens twice.

On load (Commit)

When the last chunk is uploaded, the device checks the whole program before making it active:

  • loop structure: balanced begin/end, nesting ≤ 4, every count ≥ 1;
  • input and output numbers exist on this board;
  • home is possible with the configured homing method;
  • each move's velocity and acceleration against SoftConfig, and each move_abs target against the soft travel;
  • each move_linear step names valid, distinct axes;
  • the checksum matches the uploaded instructions.

On run

Limits may have changed since the upload, and the start position is only known at run time. So ProgramRun repeats the checks and adds:

  • the exact extreme positions of the whole program, starting from the current position and including every loop iteration and every repeat, against the soft travel;
  • for move_linear steps, the same analysis for every axis in the stack, against that axis's own limits; the line velocity and acceleration must not exceed the smallest maximum of the axes in the step.

Any refusal happens before the first step of the motor.

Homing rules

  • move_abs and programs need a homed axis (after Home or SetPosition).
  • Exception: a program may run on an axis that is not homed if its first motion step is home.
  • move_rel and relative move_linear ("by": true) do not need homing.
  • An absolute move_linear needs every axis in the step to be homed.

Otherwise ProgramRun is refused with ERROR_NOT_HOMED.

Errors and the instruction index

Instruction indexes start at 0 and refer to the compiled list shown by program check.

Error When Details in the response
ERROR_PROGRAM_INVALID on load: broken structure, unknown input/output, home without a homing method program_instruction_index
ERROR_LIMIT_EXCEEDED on load or run: a value outside the limits program_instruction_index, limit_violation (limit, requested, allowed) and, for move_linear, axis
ERROR_NOT_HOMED on run: absolute motion without homing program_instruction_index
ERROR_PROGRAM_TOO_LARGE on load: more than 256 instructions or 6 KB —
ERROR_PROGRAM_CHECKSUM on load: uploaded data does not match the checksum —
ERROR_NO_PROGRAM run without a loaded program —

upan-cli prints the reason in human units with the allowed value, for example:

device refused: LIMIT_EXCEEDED: TRAVEL_MAX — requested 120.000 mm, allowed 100.000 mm (program instruction #7)

Exit codes of upan-cli: 0 — success, 1 — the device refused or an error occurred during motion, 2 — error in the command or the file, 3 — no connection.

See Response and ErrorCode for the full list.

While a program runs

  • Telemetry reports the current instruction (program_pc) and repeat (program_repeat).
  • Moves inside a program do not send EVENT_MOVE_DONE; the end of the program sends EVENT_PROGRAM_DONE.
  • Pause stops after the current move; Resume continues; Abort stops like Stop and resets the program counter.
  • Stop, e-stop, a fault or a wait_input timeout abort the program with EVENT_PROGRAM_ABORTED. The event carries the error and the instruction index in detail; for a stack, axis names the module that refused.
  • A lost USB or network connection does not stop a running program.
upan-cli -p COM5 program run --repeat 10
upan-cli -p COM5 program pause
upan-cli -p COM5 program abort

Example: feed with a foot pedal

A woodworking feed: home, then 50 times wait for the pedal on IN1, feed 300 mm, pulse OUT1 for a saw, and return.

{"program": [
  {"home": true},
  {"loop": 50, "do": [
    {"wait_input": "IN1", "level": true},
    {"move_rel": "300mm", "velocity": "80mm/s", "acceleration": "400mm/s2"},
    {"set_output": "OUT1", "level": true},
    {"wait": "2s"},
    {"set_output": "OUT1", "level": false},
    {"move_rel": "-300mm"}
  ]}
]}
upan-cli -p COM5 program load feed-pedal.json --run

If the soft limits do not allow 80 mm/s, the device refuses on load and names instruction #3 with the allowed velocity.