Skip to content
Rodeo

sat_frame.h

Generated from the header

This page is rodeo-sdk/common/sat_frame.h — its comments and declarations. To change it, change the header.

The contract is hardware/rodeo-motherboard/docs/RODEO_SATELLITE_CHAIN_CONTRACT.md; section numbers below (§2.1) point into it. This header is the contract in code, and BOTH firmwares include it: the Seed’s chain driver in this SDK, and the satellite firmware in rodeo-satellite/ (by path). A byte layout lives here once, so the two ends cannot drift, and common/tests/sat_frame_gtest.cpp exercises it from both sides, down to a simulated chain of satellites.

Plain C99 with static inline functions, because the satellites are built with ch32v003fun, a C toolchain, on a part with 16 KB of flash and 2 KB of RAM. No allocation, no tables, nothing that isn’t used costs anything. C++ includes it unchanged. Pure logic: no hardware, no clock, no libDaisy.

SAT_BAUD

#define SAT_BAUD 1000000u // 8N1, so a byte takes 10 us

SAT_IDLE_GAP_BYTES

#define SAT_IDLE_GAP_BYTES 2u // a gap this long starts (or aborts) a frame

SAT_SATELLITE_BOOT_MS

#define SAT_SATELLITE_BOOT_MS 20u // a satellite passes frames this soon after power

SAT_FIRST_DISCOVER_MS

#define SAT_FIRST_DISCOVER_MS 50u // the Seed waits this long after power

SAT_SCAN_PERIOD_MS

#define SAT_SCAN_PERIOD_MS 4u // default: 250 scans a second

SAT_RING_LOST_AFTER

#define SAT_RING_LOST_AFTER 3u // missed or wrong SCAN replies in a row

SAT_SOF

#define SAT_SOF 0xA5u // protocol v1

SAT_HEADER_LEN

#define SAT_HEADER_LEN 4u

SAT_SLOT_LEN

#define SAT_SLOT_LEN 4u

SAT_MAX_SLOTS

#define SAT_MAX_SLOTS 64u

SAT_FRAME_LEN

#define SAT_FRAME_LEN(n) (SAT_HEADER_LEN + SAT_SLOT_LEN * (n))

SAT_MAX_FRAME_LEN

#define SAT_MAX_FRAME_LEN SAT_FRAME_LEN(SAT_MAX_SLOTS) // 260 bytes

SAT_DESCRIPTOR

#define SAT_DESCRIPTOR 0x80u

The one slot kind DISCOVER carries.

SAT_RESET_GUARD

#define SAT_RESET_GUARD 0x5Au

SAT_NUM_KINDS

#define SAT_NUM_KINDS 16u

SAT_KIND_TOLERANCE_X100

#define SAT_KIND_TOLERANCE_X100 30 // 0.30 of a kind step

How far a reading may sit from a kind number and still count as that kind. 1 % resistors land within 0.1 (§1.6); a reading further out than this is a fault, not a guess.

SAT_H_SOF … SAT_H_HOP

Header byte offsets.

  • SAT_H_SOF = 0
  • SAT_H_TYPE = 1
  • SAT_H_N = 2
  • SAT_H_HOP = 3

SAT_SCAN … SAT_COMMAND

Frame types (header byte 1).

  • SAT_SCAN = 0x01
  • SAT_DISCOVER = 0x02
  • SAT_COMMAND = 0x03

sat_slot

struct sat_slot

One slot: a kind, two data bytes, and a CRC over the first three.

Members

  • uint8_t kind
  • uint8_t d0
  • uint8_t d1
  • uint8_t crc

SAT_IN_EMPTY … SAT_IN_FAULT

Input slots, satellite to Seed. They report state, not events.

  • SAT_IN_EMPTY = 0x00
  • SAT_IN_POT = 0x01 — position 0..65535, little-endian
  • SAT_IN_BUTTON = 0x02 — D0 bit 0 pressed; D1 press counter (wraps at 256)
  • SAT_IN_ENCODER = 0x03 — D0 detent counter; D1 bit 0 pressed, bits 4-7 press counter
  • SAT_IN_SWITCH = 0x04 — D0 position index
  • SAT_IN_FAULT = 0x7F — D0 fault code, D1 detail

SAT_OUT_NONE … SAT_OUT_RGB

Output slots, Seed to satellite.

  • SAT_OUT_NONE = 0x00 — keep the current output state
  • SAT_OUT_LED = 0x10 — D0 brightness 0-255; D1 a SAT_LED_ mode
  • SAT_OUT_RGB = 0x11 — D0-D1 an RGB565 colour, little-endian (sat_rgb565())
  • SAT_LED_STEADY = 0
  • SAT_LED_SLOW_BLINK = 1 — 1 Hz
  • SAT_LED_FAST_BLINK = 2 — 4 Hz

SAT_CMD_CLOSE_RING_HERE … SAT_CMD_IDENTIFY

Command slots, in a COMMAND frame. Position 0 addresses every satellite.

  • SAT_CMD_CLOSE_RING_HERE = 0x40 — D0 target position
  • SAT_CMD_RING_AUTO = 0x41 — D0 target position, or 0
  • SAT_CMD_RESET = 0x42 — D0 target or 0; D1 must be SAT_RESET_GUARD
  • SAT_CMD_IDENTIFY = 0x43 — D0 target position; D1 seconds to blink

SAT_FAULT_ID_BETWEEN_BANDS … SAT_FAULT_SENSOR

Fault codes, D0 of a FAULT slot (§7).

  • SAT_FAULT_ID_BETWEEN_BANDS = 0x01 — D1: the reading as kind x 10
  • SAT_FAULT_KIND_UNSUPPORTED = 0x02 — D1: the kind
  • SAT_FAULT_SENSOR = 0x03

SAT_KIND_TEST_FIXTURE … SAT_KIND_NO_ID

  • SAT_KIND_TEST_FIXTURE = 0
  • SAT_KIND_POT = 1
  • SAT_KIND_BUTTON = 2
  • SAT_KIND_ENCODER = 3
  • SAT_KIND_SWITCH = 4
  • SAT_KIND_POT_BANK_4 = 5
  • SAT_KIND_ENC_BANK_4 = 6
  • SAT_KIND_BUTTON_BANK_4 = 7 — four buttons, an RGB LED beside each
  • SAT_KIND_CUSTOM = 14 — a board on its own firmware: slots as DISCOVER says
  • SAT_KIND_NO_ID = 15 — no ID resistor fitted: sends EMPTY

sat_kind_slots

static inline uint8_t sat_kind_slots(uint8_t kind);

Slots a board kind owns: 4 for the banks, 1 for everything else. A custom board (SAT_KIND_CUSTOM) declares its own in its DISCOVER descriptor, which is what the Seed goes by for every kind.

sat_kind_input

static inline uint8_t sat_kind_input(uint8_t kind);

The input slot kind a board kind reports.

sat_kind_supported

static inline bool sat_kind_supported(uint8_t kind);

Whether the stock satellite firmware runs this board kind. Not SAT_KIND_CUSTOM: those boards bring their own firmware.

sat_kind_from_adc

static inline int sat_kind_from_adc(uint16_t reading, uint16_t full_scale, uint8_t* tenths);

Board kind from the ID divider’s ADC reading: kind = round(15 x reading / full scale). Returns the kind, or -1 if the reading sits between bands; then *tenths (if given) holds the reading as kind x 10, the FAULT slot’s detail.

sat_crc8

static inline uint8_t sat_crc8(const uint8_t* p, size_t n);

CRC-8/SMBUS: polynomial 0x07, init 0, no reflection, no final XOR.

sat_slot_make

static inline sat_slot sat_slot_make(uint8_t kind, uint8_t d0, uint8_t d1);

sat_slot_ok

static inline bool sat_slot_ok(sat_slot s);

sat_slot_read

static inline sat_slot sat_slot_read(const uint8_t* p);

sat_slot_write

static inline void sat_slot_write(uint8_t* p, sat_slot s);

sat_pot

static inline sat_slot sat_pot(uint16_t position);

sat_pot_position

static inline uint16_t sat_pot_position(sat_slot s);

sat_button

static inline sat_slot sat_button(bool pressed, uint8_t presses);

sat_encoder

static inline sat_slot sat_encoder(uint8_t detents, bool pressed, uint8_t presses);

sat_switch

static inline sat_slot sat_switch(uint8_t position);

sat_fault

static inline sat_slot sat_fault(uint8_t code, uint8_t detail);

sat_delta8

static inline int sat_delta8(uint8_t now, uint8_t before);

How far an 8-bit counter moved, from the last good value to this one: -128..127, so a counter that wrapped still gives the right direction.

sat_delta4

static inline int sat_delta4(uint8_t now, uint8_t before);

The same for the encoder’s 4-bit press counter: 0..15 presses since.

sat_led

static inline sat_slot sat_led(uint8_t brightness, uint8_t mode);

sat_none

static inline sat_slot sat_none(void);

sat_rgb565

static inline uint16_t sat_rgb565(uint8_t r, uint8_t g, uint8_t b);

8-bit red, green and blue packed as RGB565: 5 bits red, 6 green, 5 blue.

sat_rgb

static inline sat_slot sat_rgb(uint16_t rgb565);

An RGB output slot: one LED’s colour (§3.2).

sat_rgb_unpack

static inline void sat_rgb_unpack(sat_slot s, uint8_t* r, uint8_t* g, uint8_t* b);

An RGB slot’s colour back to 8 bits a channel. Each channel’s top bits fill its low ones, so full scale stays 255 and 0 stays 0.

sat_descriptor

struct sat_descriptor

Members

  • uint8_t kind — board kind, 0..15
  • uint8_t slots — slots owned, 1..4
  • bool last — PRESENT high: nothing downstream
  • uint8_t fw_major — 0..15
  • uint8_t fw_minor — 0..15

sat_descriptor_slot

static inline sat_slot sat_descriptor_slot(sat_descriptor d);

sat_descriptor_read

static inline sat_descriptor sat_descriptor_read(sat_slot s);

sat_command

static inline sat_slot sat_command(uint8_t command, uint8_t target, uint8_t arg);

sat_command_for

static inline bool sat_command_for(sat_slot s, uint8_t position);

Whether the satellite at position should act on this command slot.

sat_header_write

static inline void sat_header_write(uint8_t* p, uint8_t type, uint8_t n, uint8_t hop);

sat_header_ok

static inline bool sat_header_ok(const uint8_t* p);

A header a receiver accepts: v1 SOF, a known type, at most 64 slots.

sat_header_onward

static inline void sat_header_onward(const uint8_t* in, uint8_t slots_owned, uint8_t* out);

The header a satellite sends on, given the one it received and the slots it owns. SCAN keeps N (it consumes s slots and appends s), DISCOVER adds the descriptor, COMMAND forwards everything; every type adds 1 to HOP. A SCAN with fewer than s slots is forwarded unchanged apart from HOP.

sat_satellite_pass

static inline size_t sat_satellite_pass(const uint8_t* in, size_t in_len, sat_descriptor me, const sat_slot* inputs, sat_slot* taken, bool* took, uint8_t* position, uint8_t* out);

What one satellite does with one whole frame: the reference model of §2.3.

The satellite firmware streams a frame through with a 4-byte lag rather than buffering it, but what it sends must equal this, byte for byte. Host tests chain calls to this to simulate a whole ring.

in/in_len the frame received; out gets the frame to send on, of the

returned length (0: the frame is dropped)

me this satellite’s descriptor (its kind and slots owned) inputs its current input slots, me.slots of them taken if not NULL, receives the output slots consumed from a SCAN

(me.slots of them); *took says whether there were any

position if not NULL, receives this satellite’s position, HOP_in + 1

sat_build_discover

static inline size_t sat_build_discover(uint8_t* out);

Build a DISCOVER frame; returns its length.

sat_build_scan

static inline size_t sat_build_scan(uint8_t* out, const sat_slot* outputs, uint8_t n);

Build a SCAN frame from n output slots in chain order; returns its length.

sat_build_command

static inline size_t sat_build_command(uint8_t* out, const sat_slot* commands, uint8_t n);

Build a COMMAND frame from n command slots; returns its length.

sat_result

enum sat_result

Why a reply was refused.

  • SAT_OK = 0
  • SAT_ERR_SHORT — fewer bytes than the header says, or no header
  • SAT_ERR_HEADER — bad SOF, type or N, or not the type sent
  • SAT_ERR_HOP — HOP isn’t what the chain says it should be
  • SAT_ERR_CRC — a slot failed its CRC
  • SAT_ERR_NOT_DESCRIPTOR — a DISCOVER slot that isn’t a descriptor
  • SAT_ERR_LAST — the “last” bit isn’t on the last satellite, and only there
  • SAT_ERR_VERSION — a satellite’s major version isn’t the one supported
  • SAT_ERR_TOO_MANY — more satellites than the caller has room for

sat_read_discover

static inline sat_result sat_read_discover(const uint8_t* in, size_t len, uint8_t fw_major, sat_descriptor* out, uint8_t max, uint8_t* count);

Read a DISCOVER reply into descriptors, in chain order (§4).

Checks that HOP equals the number of descriptors, every CRC is good, exactly the last satellite has the “last” bit, and every satellite’s major firmware version is fw_major. *count gets the number of satellites. An empty chain (nothing plugged in, the ring closed at the motherboard) returns SAT_OK with *count 0.

sat_check_scan

static inline sat_result sat_check_scan(const uint8_t* in, size_t len, uint8_t n, uint8_t satellites);

Check a SCAN reply’s header against the chain the Seed expects: the same N it sent, and HOP equal to the number of satellites. Slot CRCs are checked one by one by the caller, so one bad slot doesn’t cost the whole frame.

sat_lag_bytes

static inline uint8_t sat_lag_bytes(uint8_t type, uint8_t n, uint8_t slots_owned);

How many byte times a satellite holds a frame back (§2.3): the header and, in a SCAN, the slots it consumes, plus the byte in flight. It can’t send the first slot it forwards until that slot has arrived, so a one-slot satellite lags 5 bytes (50 us) and a four-slot bank 17 (170 us).

sat_ring_time_us

static inline uint32_t sat_ring_time_us(uint8_t n, uint8_t satellites);

Expected ring time in microseconds (§6.2): the frame’s bytes at 10 us, plus each satellite’s lag. For a SCAN, n is the total of the slots the satellites own, so the lags add up to (satellites + 4n) byte times. The timeout is twice this, and at least 1 ms.

sat_timeout_us

static inline uint32_t sat_timeout_us(uint8_t n, uint8_t satellites);