The host link¶
What's in the firmware so far
UART and RS485, with JSON lines, native frames and events. I2C is described here ahead of the firmware. RS485 has been tried through a plain UART, not yet with a transceiver.
The host link lets another controller run the board over a wire: an Arduino or MarcDuino, a dome controller, another ESP32, or a Raspberry Pi. It plays scenes, shows clips, sets the LEDs and moves the holo, and hears back when a clip or scene has ended.
It uses three spare pins on the P2 header, and doesn't touch the USB console or Wi-Fi: all three work at once.
Choosing a transport¶
| Transport | Wires | Boards per bus | Speaks | Good for |
|---|---|---|---|---|
| UART | TX, RX, GND | one | JSON lines and native frames | One host close by: a Pi, an ESP32, an Arduino with a spare serial port. The simplest to start with |
| RS485 | A, B, GND, via a transceiver | up to 32, or more with low-load transceivers | JSON lines and native frames | Long runs through the body and dome, and several boards on one pair of wires |
| I2C | SDA, SCL, IRQ, GND | many, one address each | native frames | A host that already has an I2C bus in the dome |
CAN (with Cyphal) and plain GPIO triggers are planned for later.
JSON lines are the HTTP API, one request a line:
POST /scenes/apply {"name":"cantina"}
200 {"screen":{...},"leds":{...},"holo":{...}}
Native frames are small binary messages with a CRC, for hosts with little memory. The protocol reference specifies both.
Pins¶
| Role | Default | P2 pin | UART | RS485 | I2C |
|---|---|---|---|---|---|
| A | GPIO21 | 11 | TX (board → host) | DI of the transceiver | SDA |
| B | GPIO33 | 12 | RX (host → board) | RO of the transceiver | SCL |
| C | GPIO15 | 7 | ATTN, if turned on | DE and /RE of the transceiver | IRQ (open drain) |
A GND wire, P2 pin 1 or 5, goes with every transport. The pins can be changed in the settings, to
any of the free GPIOs that the LEDs and servos aren't using. While the link holds a pin, the gpio
command refuses to drive it.
Everything is 3.3 V, and no pin takes 5 V. Each transport's page says what to put between the board and a 5 V host.
Turning it on¶
The link is off until it's given a transport. The settings are saved, and can be changed from the USB console, the HTTP API, or the web app.
On the console:
link mode uart -b 115200
restart
After the restart, link shows it running:
link: uart on GPIO21 (TX), GPIO33 (RX), 115200 baud, protocol auto, address 1
requests 0, events given 0, bad lines 0, framing errors 0, overruns 0, ignored 0; last request never
events: [], pushed, 0 pending
Over the HTTP API:
curl -X PATCH -H "Content-Type: application/json" \
-d '{"mode":"uart","baud":115200}' http://holo-2db0.local/api/v1/link
In the web app: the Host link card on the Settings page.
Changing the transport, the pins, attn, the I2C address or general_call waits for a restart,
and the reply says so with "restart_required": true. The rest applies at once: the baud rate,
the address and groups, the protocol, and the events it starts with. A new baud rate takes effect
after the reply, which is sent at the old one.
The link command¶
| Command | Does |
|---|---|
link |
The transport, its pins and settings, the counters, and the events asked for |
link mode <off\|uart\|rs485> [-b <baud>] [-a <address>] |
The transport, from the next restart. -b is the baud rate, -a the board's address: 1–223 |
link proto <auto\|json\|native> |
What the board accepts on UART and RS485. I2C is always native |
link address <1-223> |
The board's address on UART and RS485 |
link groups [<group>…\|--clear] |
The groups it belongs to, 1–31, up to 8 |
link pins <a> <b> <c> |
The GPIOs for roles A, B and C |
link events [<kind>…\|--none] [--push\|--keep] |
The events the link starts with, and whether it pushes them |
link log [on\|off] |
Print every line and frame, in and out, on the USB console. For wiring up a host |
link reset |
Back to the firmware's defaults |
What a host can do¶
Over JSON lines, most of the HTTP API: the screen, the LEDs, the holo and servos, scenes, settings, the board's information, and the link itself. The protocol reference lists the paths. Files, firmware updates, the network and servo calibration stay on the USB console and Wi-Fi.
Over native frames, less, but what a show needs: apply a scene by its slot, end it, the screen's colour and backlight, the LEDs, a holo motion, and the board's status.
Scenes do the heavy lifting. Set up the scenes on the web app, give each one a slot, and a host needs only "scene 3" to change what the screen, the LEDs and the holo are all doing.
Events¶
A host can hear about things as they happen, rather than asking. They are the board's own events, the same ones the web app follows:
- what happened:
clip_started,clip_ended,scene_started,scene_ended, andtouch(when touch reporting is on); - what changed:
screen,leds,holo,scene,scenes,settings,network,otaandsystem, each with the resource as itsGETreturns it; ready: the link has started (always sent once on UART, after a restart).
Nothing but ready is sent until the host asks, with PATCH /link/events or the native
EVENTS_SET. On UART, the board sends events as they happen, with no extra wire. On RS485 and
I2C, the host fetches what happened, and every reply says how many are waiting.
Events has the details.
Status¶
link on the console, and GET /api/v1/link, count what the link has seen:
| Counter | Counts |
|---|---|
requests |
Requests answered |
events |
Events given to the host |
bad_lines |
JSON lines that didn't parse, or were too long |
crc_errors |
Native frames dropped for a bad CRC |
framing_errors |
Bytes the UART received damaged: usually a wrong baud rate, or noise |
overruns |
Bytes lost because the host sent faster than the board read |
ignored |
RS485 lines with no address |
A host that gets no reply at all is usually a wiring or baud-rate problem: link log on shows
whether anything arrives. Each transport's page has a first test to try.