-
Notifications
You must be signed in to change notification settings - Fork 1
Technical Telemetry Schema
The schema defines all DataEndpoint and DataType variants and is the source of truth for every language binding. All
nodes that exchange telemetry must use the exact same schema (including ordering), or decoding will be undefined.
Location:
telemetry_config.json (source) (
override path with SEDSPRINTF_RS_SCHEMA_PATH)
Optional board-local IPC overlay:
- Set
SEDSPRINTF_RS_IPC_SCHEMA_PATHto a second JSON file. - Its endpoints and types are merged after the base schema during code generation.
- Overlay endpoints are treated as link-local automatically.
- Base-schema endpoints are treated as non-link-local automatically.
- Overlay endpoint/type names must not collide with the base schema.
- The GUI editor can load/edit/save the base schema and IPC overlay separately.
Generated outputs:
- Rust enums and metadata:
define_telemetry_schema!in src/config.rs (source) - C header: C-Headers/sedsprintf.h (source)
- Python stubs: python-files/sedsprintf_rs/sedsprintf_rs.pyi (source)
The order of items in telemetry_config.json (source) defines the enum discriminants (with built-ins inserted as noted below). Those discriminants are sent on the wire and used in endpoint bitmaps. Reordering entries without updating every deployed system will break decode compatibility.
Built-ins:
-
TelemetryErrordata type is built-in and is appended after all schema types. -
TelemetryErrorendpoint is built-in and is appended after all schema endpoints. - Do not define
TelemetryErrorin the JSON schema.
Safe changes:
- Appending new endpoints/types to the end of the list.
- Updating documentation fields (
doc). - Adding board-specific link-local IPC endpoints/types in a separate overlay file via
SEDSPRINTF_RS_IPC_SCHEMA_PATH.
Risky changes:
- Reordering or deleting endpoints/types.
- Changing a type's element layout (data type or count).
Top-level keys:
-
endpoints: list ofDataEndpointdefinitions. -
types: list ofDataTypedefinitions.
-
rust: Rust enum variant name. -
name: wire/display name (typically ALL_CAPS). -
doc: optional description. -
link_local_only: derived by schema file. Endpoints coming from the IPC overlay are link-local-only; endpoints coming from the base schema are not.
Legacy config upgrade rules:
- Older schemas may still contain
broadcast_mode. -
build.rsanddefine_telemetry_schema!accept that legacy field and normalize it during schema loading. -
broadcast_mode = "Never"is upgraded tolink_local_only = true. -
broadcast_mode = "Default"andbroadcast_mode = "Always"are accepted but ignored. - Unknown
broadcast_modevalues are ignored with a build warning. - New schemas should omit
broadcast_modeentirely.
Remote forwarding is topology-driven:
- IPC/link-local endpoints are restricted to link-local/software-bus sides.
- Discovery advertisements tell routers which remote sides can currently reach which endpoints.
- If multiple local/remote candidates exist for the same endpoint, routing fans out to the discovered matches.
- If only one candidate exists, the packet is delivered only there.
- If discovery has not learned any route for a non-local endpoint yet, relay mode falls back to flooding.
Link-local scope further constrains forwarding:
- Base schema endpoint: the endpoint can use any side.
- IPC overlay endpoint: the endpoint is restricted to link-local/software-bus sides and is excluded from discovery advertisements sent on non-link-local sides.
To keep packet semantics unambiguous, a type must not mix link-local-only endpoints with normal endpoints.
-
rust: Rust enum variant name. -
name: wire/display name. -
doc: optional description. -
reliable: optional boolean (legacy).truemaps to ordered reliable delivery. -
reliable_mode: optional string (None,Ordered,Unordered). Overridesreliablewhen present. -
class:Data,Warning, orError(used for formatting and error handling). -
element: payload layout description (see below). -
endpoints: list of endpoint Rust variant names (used as metadata for defaults/validation).
-
kind:StaticorDynamic. -
data_type: primitive element type (Float32,UInt16,String,Binary, etc.). -
count: only forStatic(number of elements).
This library treats the payload as a raw byte slice. The schema tells it how to interpret that slice:
-
Static + numeric/bool: payload size must equal
count * element_width. -
Dynamic + numeric/bool: payload size must be a multiple of
element_width. - String: dynamic UTF-8 bytes; trailing NULs are ignored for validation.
- Binary: raw bytes (no UTF-8 requirements).
- NoData: zero-length payload (used for marker messages).
For static String or Binary payloads, the schema uses the compile-time limits:
STATIC_STRING_LENGTHSTATIC_HEX_LENGTH
These are configured in
src/config.rs (source)
and used by data_type_size.
The macro-generated metadata types are defined in src/lib.rs (source):
MessageMeta { name, element, endpoints, reliable }MessageElement::{Static, Dynamic}-
MessageDataType(primitive element type) -
MessageClass(Data/Warning/Error)
Helpers:
-
message_meta(ty): returns the fullMessageMeta. -
get_data_type(ty): returns the primitive element type. -
get_needed_message_size(ty): returns the static payload size (bytes). -
endpoints_from_datatype(ty): returns endpoints listed in the schema. -
is_reliable_type(ty): returns whether the type uses reliable delivery on the wire.
-
Packet::newvalidates payload sizes againstmessage_meta. -
Router::log*uses the schema to validate payload lengths before serialization. -
Packet::to_stringusesMessageClassandMessageDataTypeto format payloads.
{
"endpoints": [
{ "rust": "Radio", "name": "RADIO", "doc": "Downlink radio" }
],
"types": [
{
"rust": "GpsData",
"name": "GPS_DATA",
"doc": "GPS data",
"reliable": false,
"class": "Data",
"element": { "kind": "Static", "data_type": "Float32", "count": 3 },
"endpoints": ["Radio"]
}
]
}
An IPC overlay file uses the same shape. Its endpoints become link-local automatically because they come from the overlay file, not because they carry a separate flag.
Legacy schema example accepted during upgrade:
{
"endpoints": [
{
"rust": "SoftwareBus",
"name": "SOFTWARE_BUS",
"broadcast_mode": "Never"
}
],
"types": [
{
"rust": "IpcMessage",
"name": "IPC_MESSAGE",
"class": "Data",
"element": { "kind": "Dynamic", "data_type": "Binary" },
"endpoints": ["SoftwareBus"]
}
]
}
That legacy endpoint is treated exactly like:
{
"endpoints": [
{
"rust": "SoftwareBus",
"name": "SOFTWARE_BUS",
"link_local_only": true
}
],
"types": [
{
"rust": "IpcMessage",
"name": "IPC_MESSAGE",
"class": "Data",
"element": { "kind": "Dynamic", "data_type": "Binary" },
"endpoints": ["SoftwareBus"]
}
]
}
Before deploying a schema change: