Skip to main content

OSynaptic-TX — Quick Start

Three API tiers, three code examples. Start with API C on any 8-bit AVR.


Stack peak: ~21 bytes | Static RAM: 0 bytes

#include <OSynaptic-TX.h>

/* Sensor descriptor baked into Flash — 0 bytes of SRAM */
OSTX_STATIC_DEFINE(s_temp, 0x00000001UL, "T1", "Cel");

/* Emit callback — hand each byte directly to the Serial driver */
static void emit_byte(ostx_u8 b, void *ctx) {
(void)ctx;
Serial.write(b);
}

static ostx_u8 tid = 0;

void setup() {
Serial.begin(115200);
}

void loop() {
/* Value pre-scaled by OSTX_VALUE_SCALE (default 10000):
* 215000 = 21.5000 °C
* Substitute with your ADC reading × 10000 */
ostx_stream_pack(&s_temp, tid++,
(ostx_u32)(millis() / 1000UL),
215000L,
emit_byte, NULL);
delay(1000);
}

How it works: ostx_stream_pack() calls your emit callback once per output byte. Nothing is buffered — each byte goes directly to the transport, so static RAM stays at zero.


API B — Static Descriptor (lower stack than A)

Stack peak: ~51 bytes | Static RAM: 96 bytes (output buffer)

#include <OSynaptic-TX.h>

OSTX_STATIC_DEFINE(s_hum, 0x00000001UL, "H1", "Pct");

static ostx_u8 out_buf[OSTX_PACKET_MAX];
static ostx_u8 tid = 0;

void setup() { Serial.begin(115200); }

void loop() {
/* 650000 = 65.0000 % relative humidity */
int len = ostx_static_pack(&s_hum, tid++,
(ostx_u32)(millis() / 1000UL),
650000L,
out_buf);
if (len > 0)
Serial.write(out_buf, (size_t)len);
delay(1000);
}

When to use B vs C:

  • Use B when you need the complete frame in a buffer before sending (e.g., LoRa LoRa.write(buf, len) pattern).
  • Use C when you can send bytes as they are generated (Serial, UDP, CAN).

API A — Fully Dynamic

Stack peak: ~137 bytes | Static RAM: 96 bytes (output buffer)

#include <OSynaptic-TX.h>

static ostx_u8 out_buf[OSTX_PACKET_MAX];
static ostx_u8 tid = 0;

void setup() { Serial.begin(115200); }

void loop() {
int len = ostx_sensor_pack(
0x00000001UL, /* aid: agent ID (uint32) */
"T1", /* sensor_id (max 8 chars) */
"Cel", /* unit (max 8 chars, UCUM-style) */
tid++, /* tid: transaction ID 0–255 */
(ostx_u32)(millis() / 1000UL), /* ts_sec: Unix timestamp */
215000L, /* scaled value × OSTX_VALUE_SCALE */
out_buf
);
if (len > 0)
Serial.write(out_buf, (size_t)len);
delay(1000);
}

When to use A: When sensor ID and unit are determined at runtime (read from config, received over network, generated dynamically).


Multi-sensor Node (API C)

#include <OSynaptic-TX.h>

OSTX_STATIC_DEFINE(s_temp, 0x00000001UL, "T1", "Cel");
OSTX_STATIC_DEFINE(s_hum, 0x00000001UL, "H1", "Pct");
OSTX_STATIC_DEFINE(s_co2, 0x00000001UL, "CO2", "ppm");

static void emit(ostx_u8 b, void *) { Serial.write(b); }
static ostx_u8 tid = 0;

void loop() {
ostx_u32 ts = (ostx_u32)(millis() / 1000UL);
ostx_stream_pack(&s_temp, tid++, ts, read_temp_scaled(), emit, NULL);
ostx_stream_pack(&s_hum, tid++, ts, read_hum_scaled(), emit, NULL);
ostx_stream_pack(&s_co2, tid++, ts, read_co2_scaled(), emit, NULL);
delay(2000);
}

Each call to ostx_stream_pack() sends one complete frame independently.


Value Encoding Cheat-Sheet

real_value × OSTX_VALUE_SCALE = scaled argument

Examples (OSTX_VALUE_SCALE = 10000):
21.5 °C → 215000
65.3 % → 653000
0.0 m/s → 0
-5.2 °C → -52000
1013.25 → 10132500 (hPa)

For ADC values:

/* ADC gives 0–1023 on a 10-bit channel, Vref=3.3V, sensor 0–100°C */
int adc = analogRead(A0);
long scaled = (long)adc * 100L * 10000L / 1023L; /* temperature × 10000 */