Board v2 · Firmware in developmentDraft
Network API
Draft of the device's local REST and WebSocket API — discovery, login, control ownership, JSON messages and units.
The network module of UPAN serves a local API over Wi-Fi: a WebSocket for commands, telemetry and events, and REST endpoints for scripts. It works in your local network without internet or a cloud account. The network module only translates: it checks login, control ownership and message format, and passes commands to the motion MCU. Motion limits are checked only by the motion MCU, and its refusals come back to you unchanged, with details.
Warning: This API is a draft. Endpoints, message names and status codes may change before release. The network module firmware is not written yet. Use this page to plan integrations, not to ship them.
Principles
- JSON mirrors the protocol 1:1. Field names are the same as in the protocol schema (snake_case). Enum values are strings with the schema name, for example
"STATE_MOVING"or"ERROR_LIMIT_EXCEEDED". One schema describes both the USB protocol and this API; see the API reference. - Base units. Numbers are integers in base units: µm, µm/s, µm/s² on a linear axis, millidegrees on a rotary axis. There are no millimetres or inches in the JSON; conversion happens in
upan.jsandupan-mcp. See Configuration. - One origin. The built-in page, apps on the device,
/lib/upan.jsand the API are served from the same address. No CORS, no mixed content, no internet needed.
Address map
| Path | Content | Login |
|---|---|---|
/ |
built-in page: login, state, list of apps, Wi-Fi settings | — |
/app/<name>/… |
apps stored on the device (static files) | the app logs in itself |
/lib/upan.js |
browser library for apps; its version follows the network module firmware | — |
/api/v1/info |
name, serial number, versions, axis_kind, capabilities |
not required |
/api/v1/auth/* |
login | — |
/api/v1/ws |
WebSocket: commands, telemetry, events | required |
/api/v1/… |
REST | required |
Port 80, plain HTTP in the local network. HTTPS is not part of the first version.
Finding the device
- mDNS: host name
upan-xxxx.local(xxxxis the end of the serial number) and service_upan._tcp. Works on PCs and iPhones; unreliable on Android. - IP address: the main path for phones. The built-in page shows a QR code with its own address — open it on a PC and scan it with the phone.
upan-mcpprints the device address too. - DHCP name
upan-xxxx, for routers that list client names.
Note: An app served by the device must not hard-code the device address. It is loaded from the device, so the API is always at
location.origin.
Authentication
Each device has its own password, printed on a label or card in the box. You can change it on the built-in page. The password is never sent over the network; login is a challenge-response:
-
GET /api/v1/auth/challenge{ "nonce": "<32 bytes, hex>", "salt": "<hex>", "iterations": 10000 } -
The client computes:
key = PBKDF2-SHA256(password, salt, iterations) proof = HMAC-SHA256(key, nonce) -
POST /api/v1/auth/login{ "nonce": "…", "proof": "…" }Response:
{ "token": "…", "expires_s": 86400 }and a cookieupan_session(HttpOnly, SameSite=Strict).
Rules:
- Browser apps use the cookie; the browser sends it with the WebSocket automatically.
upan-mcpand scripts sendAuthorization: Bearer <token>.- A nonce is single-use and valid for 30 s. After 5 failed attempts the device waits, with a growing delay.
crypto.subtleis not available on plainhttp://pages.upan.jstherefore includes its own SHA-256, HMAC and PBKDF2. Apps should log in throughupan.jsand not call Web Crypto themselves.
Warning: On a trusted local network the password cannot be sniffed, but an active attacker on the same network could take over a session. Such an attacker still cannot change the machine limits (those need USB and a button press on the device) and cannot bypass the hardware e-stop. Full channel encryption is not part of the first version.
Control ownership
Several clients can be connected at the same time — a phone, an agent, the built-in page. Only one may move the axis.
- Read (telemetry, events, configuration): every logged-in client.
- Move or change settings: only the controller. A client takes control with
{"cmd": "acquire_control"}over the WebSocket. - Control is released by
release_control, by closing the WebSocket, or after 10 s without any message from the controller. - If another client holds control,
acquire_controlfails withERROR_CONTROL_HELDand the name of the current controller. With"force": truethe client takes over; the previous controller receivescontrol_lost. stopandestopare accepted from anyone, without control.- If the controller's WebSocket closes during a jog, the network module sends
Stopto the motion MCU immediately, without waiting for the jog timeout.
WebSocket
/api/v1/ws — one socket per client, JSON text frames.
Client to device
Each command has an id (incremented by the client) and cmd; the other fields are the fields of that command in the protocol.
{ "id": 7, "cmd": "move_rel", "distance": 150000, "velocity": 50000 }
{ "id": 8, "cmd": "jog", "velocity": -20000, "timeout_ms": 300 }
{ "id": 9, "cmd": "subscribe", "telemetry_hz": 20, "events": true }
cmd is the name of a command in the protocol: move_abs, move_rel, jog, stop, estop, home, set_position, program_run, program_pause, program_resume, program_abort, soft_config_get, soft_config_set, hard_config_get, output_set, info_get, fault_clear. Network-only commands: subscribe, acquire_control, release_control, ping.
hard_config_set and bootloader_enter do not exist over the network. They are USB-only.
Device to client
{ "type": "response", "id": 7, "error": "ERROR_NONE" }
{ "type": "response", "id": 8, "error": "ERROR_LIMIT_EXCEEDED",
"limit_violation": { "limit": "LIMIT_VELOCITY", "requested": -20000, "allowed": -15000 } }
{ "type": "telemetry", "state": "STATE_JOGGING", "homed": true, "position": 81234, "velocity": -15000 }
{ "type": "event", "kind": "EVENT_LIMIT_HIT", "position": 0, "detail": 0 }
{ "type": "control_lost", "by": "upan-mcp@workshop-pc" }
- Each command gets exactly one
responsewith the sameid. - Telemetry over the network is limited to 50 Hz.
- A
jogmust be repeated more often than itstimeout_ms. It works like a throttle pedal: each repeat can carry a newvelocity, and the device changes speed smoothly with its acceleration limit.
The message bodies are Response, Telemetry and Event.
REST
For scripts and curl, where a socket is unnecessary. Request bodies are the same JSON as over the WebSocket, without id.
| Method and path | Purpose |
|---|---|
GET /api/v1/status |
latest telemetry |
POST /api/v1/cmd/<cmd> |
one command (move_rel, home, stop, …); needs control — REST takes it implicitly for the duration of the command if it is free |
GET / PUT /api/v1/config/soft |
soft limits |
GET /api/v1/config/hard |
machine limits, read only |
PUT /api/v1/program |
a whole program as JSON; the network module splits it into chunks, the motion MCU validates it |
GET /api/v1/apps |
list of apps on the device |
PUT /api/v1/apps/<name>/<path> |
upload an app file |
DELETE /api/v1/apps/<name> |
delete an app |
| HTTP status | Meaning |
|---|---|
200 |
accepted |
401 |
not logged in |
409 |
refused because of state: ERROR_BUSY, ERROR_NOT_HOMED, ERROR_CONTROL_HELD, … |
422 |
refused because of values: ERROR_LIMIT_EXCEEDED, ERROR_PROGRAM_INVALID, ERROR_INVALID_VALUE |
The response body is always a Response in JSON, so the client can see what to correct.
curl.exe -X POST http://upan-1a2b.local/api/v1/cmd/move_rel -H "Authorization: Bearer $env:UPAN_TOKEN" -H "Content-Type: application/json" -d '{\"distance\": 150000}'
Programs over REST
The program body uses protocol instructions directly, in base units and with inputs and outputs numbered from 0. Loops are written as loop_begin / loop_end.
{ "repeat": 1, "instructions": [
{ "home": {} },
{ "loop_begin": { "count": 10 } },
{ "move_rel": { "distance": 150000 } },
{ "set_output": { "output": 0, "level": true } },
{ "wait_time": { "ms": 500 } },
{ "set_output": { "output": 0, "level": false } },
{ "wait_input": { "input": 0, "level": true, "timeout_ms": 60000 } },
{ "loop_end": {} }
] }
The response arrives after the program is committed: either accepted, or ERROR_PROGRAM_INVALID with program_instruction_index. Start it separately with program_run. The checks are the same as over USB; see Programs.
Not in the first version
- HTTPS in the local network, with a certificate that phones accept without warnings.
- Remote access from outside the local network through a relay. If it comes, it will be optional; the device keeps working locally without it.
- Bluetooth LE as a fallback channel without Wi-Fi.
- Retargeting
move_abswhile moving — currently refused withERROR_BUSY.
See also Web apps on the device, upan.js and upan-mcp.