Build
Device firmware
Device SDK · nexting_device.h · C99
Use the portable C99 core to decode bounded BLE chunks, drive approval UI and retries, and apply Agent status without bringing an allocator or operating system into your firmware.
1. Design contract
The SDK is portable C99, performs no heap allocation, and owns no transport. Every buffer is caller-owned and fixed-capacity. Feed radio chunks in, render accepted state, and send encoded bytes through your BLE stack. A complete logical frame is at most 4096 bytes including its LF; an encode buffer therefore needs 4097 bytes to also hold the C NUL.
| Name | Type | Description |
|---|---|---|
NEXTING_DEVICE_REQUEST_ID_CAPACITYrequired | 65 bytes | 64 ASCII ID bytes plus C NUL. |
NEXTING_DEVICE_SUMMARY_CAPACITYrequired | 241 bytes | 240 UTF-8 summary bytes plus C NUL. |
NEXTING_DEVICE_DEFAULT_MAX_MESSAGE_BYTESrequired | 4096 bytes | Compact JSON and its terminating LF. |
encode output bufferrequired | 4097 bytes | Maximum frame plus the encoder's C NUL. |
NEXTING_DEVICE_MAX_TTL_MSrequired | 300000 ms | Largest accepted relative approval lifetime. |
NEXTING_DEVICE_ANSWER_RETRY_MSrequired | 1000 ms | Retry interval while resolution is missing. |
NEXTING_DEVICE_STATUS_LABEL_CAPACITYrequired | 65 bytes | 64 UTF-8 label bytes plus C NUL. |
2. Messages
nexting_device_message_t is the single decoded value. Its tagged fields cover all five wire messages; status carries at most eight unique agent slots.
NEXTING_DEVICE_MESSAGE_PRESENT
NEXTING_DEVICE_MESSAGE_ANSWER
NEXTING_DEVICE_MESSAGE_RESOLVED
NEXTING_DEVICE_MESSAGE_ERROR
NEXTING_DEVICE_MESSAGE_STATUS3. Codec
Decode and encode fail closed. Invalid UTF-8, duplicate known fields, unknown enums, non-canonical integers, out-of-range values, and embedded NUL never reach state or rendering code.
nexting_device_result_t nexting_device_decode(
const char *wire,
size_t wire_length,
nexting_device_message_t *output);
nexting_device_result_t nexting_device_encode(
const nexting_device_message_t *message,
char *output,
size_t output_capacity,
size_t *output_length);The same nexting_device_message_t carries navigation/1, keys/1, rotary/1, voice/1, text/1, usage/1, and config/1 through fixed-capacity interaction payloads. Device Info exposes one support boolean per profile. Sequence-bearing physical input is monotonic per source and resets with the connection.
nexting_device_message_t event = {0};
event.type = NEXTING_DEVICE_MESSAGE_KEY_EVENT;
event.interaction.slot = 3;
event.interaction.gesture = NEXTING_DEVICE_GESTURE_PRESS;
event.interaction.sequence = ++key_sequence;
nexting_device_encode(&event, wire, sizeof wire, &wire_length);4. Newline stream framing
ATT writes can split or combine frames. The stream parser appends bytes to your 4096-byte storage, emits one delimiter-stripped JSON message at each LF, never executes a partial message, and discards oversize input until the next LF. Reset it on disconnect.
uint8_t storage[NEXTING_DEVICE_DEFAULT_MAX_MESSAGE_BYTES];
nexting_device_stream_t stream;
nexting_device_stream_init(&stream, storage, sizeof storage);
nexting_device_stream_push(&stream, chunk, chunk_length, on_message, context);
nexting_device_stream_reset(&stream);5. Approval state machine
The phases are NEXTING_DEVICE_PHASE_IDLE,NEXTING_DEVICE_PHASE_PENDING, andNEXTING_DEVICE_PHASE_WAITING_RESOLUTION. Present replaces the current request. A local choice moves Pending to Waiting Resolution and arms an identical-answer retry every 1000 ms. A matchingnexting_device_state_on_resolved clears the request from Pending or Waiting Resolution; a mismatched ID changes nothing. In a periodic loop, call nexting_device_state_tick beforenexting_device_state_retry_answer: retry_answer also clears an expired Waiting Resolution state but returns false, so calling it first would hide the expiry transition from the UI. A press can race the deadline too: nexting_device_state_choose checks TTL, clears an expired SDK state to Idle, and returns an error. On a choose error, clear visible UI only when that call started in Pending and left the phase Idle; if the phase is still Pending—or the press was already stale in Idle—preserve the current UI so another valid press can retry.
| Name | Type | Description |
|---|---|---|
nexting_device_state_on_present | Idle → Pending | Copies the request and sets a monotonic deadline. |
nexting_device_state_choose | Pending → Waiting / Idle | Builds the first answer, or clears to Idle if TTL already expired. |
nexting_device_state_retry_answer | Waiting | Builds the same answer when the retry deadline arrives. |
nexting_device_state_on_resolved | Pending / Waiting → Idle | Clears only a matching request ID. |
nexting_device_state_tick | Any active → Idle | Expires against elapsed monotonic time. |
6. Status state
A valid status message is a full replacement: absent slots clear and an empty array clears all slots. Validation happens before mutation, so a malformed frame preserves the previous status atomically. Disconnect also clears every volatile slot. Only apply status when the device advertises the status/1 profile and a positivestatusSlots capability.
nexting_device_status_state_t status;
nexting_device_status_init(&status);
if (nexting_device_status_on_message(&status, message) == NEXTING_DEVICE_OK) {
render_status(&status);
}
nexting_device_status_disconnect(&status);7. Complete integration
This translation unit enforces a strict serialized app event loop. BLE, GPIO, timer, and disconnect callbacks only enqueue through the platform-owned queue; they never enter SDK state directly. The queue must copy each event and BLE payload before returning. Only one event-loop context calls app_event_loop_init andapp_event_loop_dispatch, and it must never reenter either function. The platform-neutral queue hook deliberately makes no ISR-safety claim: adapt callback context and wake-up mechanics to your RTOS or bare-metal target. Supply monotonic time, BLE Notify, UI, and queue hooks; the exact public APIs compile as C99 with the SDK source.
#include "nexting_device.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef enum {
APP_EVENT_BLE_BYTES,Configure, build, and run all C SDK tests directly from the exported source tree with these exact commands.
cmake -S /path/to/nexting/devices/sdk/c -B build/c-sdk -DCMAKE_BUILD_TYPE=Debug
cmake --build build/c-sdk --parallel
ctest --test-dir build/c-sdk --output-on-failureapproval/1. Adding status rendering requires a real slot mapping, a statusSlots declaration, and board verification.