Firmware API

The CONDUYT Arduino library. Include with #include <Conduyt.h>.

Quick Start Sketch

#define CONDUYT_MODULE_SERVO
#include <Conduyt.h>

ConduytSerial transport(Serial, 115200);
ConduytDevice device("MyBoard", "1.0.0", transport);

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

    // Register a servo module (host discovers this via HELLO_RESP)
    device.addModule(new ConduytModuleServo());

    // Declare a writable datastream for servo angle
    device.addDatastream("angle", CONDUYT_TYPE_UINT8, "degrees", true);
    device.onDatastreamWrite("angle", [](ConduytPayloadReader &payload, ConduytContext &ctx) {
        uint8_t angle = payload.readUInt8();
        // Apply to servo (module handles this via MOD_CMD, but datastream is also an option)
        ctx.ack();
    });

    device.begin();
}

void loop() {
    device.poll();
}

This sketch demonstrates the three main firmware features: pin control (built-in, no extra code), a hardware module (servo), and a named datastream (angle). The API reference below covers each in detail.

ConduytDevice

Constructor

ConduytDevice(const char *name, const char *version, ConduytTransport &transport)
  • name: firmware name, max 16 characters, null-padded in HELLO_RESP
  • version: semver string parsed into major/minor/patch
  • transport: reference to a transport instance

Methods

MethodDescription
void begin()Initialize transport, register built-in handlers. Call in setup().
void poll()Process one inbound packet. Call in loop(). Non-blocking.
void addModule(ConduytModuleBase *module)Register a module. Call before begin().
void addDatastream(name, type, unit, writable)Declare a datastream. Call before begin().
void onDatastreamWrite(name, callback)Register write handler for a writable datastream.
void writeDatastream(name, float value)Push a FLOAT32 value to the host.
void writeDatastream(name, int32_t value)Push an INT32 value to the host.
void writeDatastream(name, bool value)Push a BOOL value to the host.
void writeDatastream(name, const uint8_t *data, size_t len)Push raw bytes to the host.
void sendPacket(uint8_t type, uint8_t seq, const uint8_t *payload, size_t len)Send a raw packet.
void sendAck(uint8_t seq)Send ACK with the given sequence number.
void sendNak(uint8_t seq, uint8_t errorCode)Send NAK with error code.

Accessors

AccessorReturn TypeDescription
firmwareName()const char*Firmware name string
versionMajor()uint8_tMajor version
versionMinor()uint8_tMinor version
versionPatch()uint8_tPatch version
moduleCount()uint8_tNumber of registered modules
datastreamCount()uint8_tNumber of declared datastreams
transport()ConduytTransport&Reference to the transport

Datastream Write Callback

void callback(ConduytPayloadReader &payload, ConduytContext &ctx)

Read values from payload using readFloat32(), readInt32(), readUint8(), etc. Call ctx.ack() or ctx.nak(errorCode) to respond.

ConduytModuleBase

Base class for all modules. Subclass this to create custom modules.

MethodDescription
const char* name()Module name (max 8 chars)
uint8_t versionMajor()Module major version
uint8_t versionMinor()Module minor version
void begin()Called during device.begin()
void handle(uint8_t cmd, ConduytPayloadReader &payload, ConduytContext &ctx)Handle a MOD_CMD
void poll()Called each device.poll() cycle
uint8_t pinCount()Number of claimed pins
const uint8_t* pins()Array of claimed pin numbers

Transport Classes

ClassMediumCOBSPlatform
ConduytSerialUART/USBYesAll boards
ConduytUSBSerialUSB CDCYesRP2040, SAMD, nRF52
ConduytMQTTMQTTNoESP32, ESP8266
ConduytBLEBLE (NUS)YesESP32, nRF52
ConduytTCPTCP ServerNoESP32, ESP8266
ConduytCLASPCLASPYesESP32

Construction Examples

// Serial (all boards)
ConduytSerial transport(Serial, 115200);

// USB CDC (RP2040, SAMD, nRF52)
ConduytUSBSerial transport(SerialUSB, 115200);

// MQTT (ESP32/ESP8266)
WiFiClient wifi;
ConduytMQTT transport(wifi, "broker.local", 1883, "device-001");

// BLE (ESP32, nRF52)
ConduytBLE transport("CONDUYT-Device");

// TCP (ESP32/ESP8266)
ConduytTCP transport(3000);  // listen port

// CLASP (ESP32)
ConduytCLASP transport;

Non-default transports require a compile define before #include <Conduyt.h>:

TransportDefine
MQTTCONDUYT_TRANSPORT_MQTT
BLECONDUYT_TRANSPORT_BLE
TCPCONDUYT_TRANSPORT_TCP
CLASPCONDUYT_TRANSPORT_CLASP

Board Limits

BoardBufferMax ModulesMax SubscriptionsMax Datastreams
ATmega328 (Uno)128B484
ESP32512B81616
ESP8266512B81616
nRF52512B81616
RP2040 (Pico)512B81616
STM32512B81616
SAMD256B6128
Teensy512B81616

Override any limit with #define before including Conduyt.h:

#define CONDUYT_MAX_MODULES 12
#define CONDUYT_MAX_SUBS 32
#define CONDUYT_MAX_DATASTREAMS 32
#define CONDUYT_BUF_SIZE 1024
#include <Conduyt.h>

Built-in Modules

All modules are opt-in via compile defines. Define the flag before #include <Conduyt.h>.

Servo

Define: CONDUYT_MODULE_SERVO, Name: servo

CmdMethodPayload
0x01attachpin(u8) + min_us(u16) + max_us(u16)
0x02writeangle(u8)
0x03writeMicrosecondsus(u16)
0x04detach(none)

NeoPixel

Define: CONDUYT_MODULE_NEOPIXEL, Name: neopixel

CmdMethodPayload
0x01beginpin(u8) + count(u16) + type(u8)
0x02setPixelindex(u16) + r,g,b(u8) + w(u8)
0x03setRangestart(u16) + count(u16) + r,g,b(u8)
0x04fillr,g,b(u8) + w(u8)
0x05show(none)
0x06setBrightnesslevel(u8)

Requires: Adafruit_NeoPixel library.

Encoder

Define: CONDUYT_MODULE_ENCODER, Name: encoder

CmdMethodPayloadResponse
0x01attachpin_a(u8) + pin_b(u8)ACK
0x02read(none)MOD_RESP: count(i32)
0x03reset(none)ACK

Events: 0x01 tick, payload: count(i32) + delta(i16)

Stepper

Define: CONDUYT_MODULE_STEPPER, Name: stepper

CmdMethodPayload
0x01configstep_pin(u8) + dir_pin(u8) + en_pin(u8) + steps_per_rev(u16)
0x02movesteps(i32) + speed_hz(u16)
0x03moveToposition(i32) + speed_hz(u16)
0x04stop(none)

Events: 0x01 done, payload: position(i32)

DHT

Define: CONDUYT_MODULE_DHT, Name: dht

CmdMethodPayloadResponse
0x01beginpin(u8) + type(u8: 11 or 22)ACK
0x02read(none)MOD_RESP: temp(f32) + humidity(f32)

Requires: DHT sensor library (Adafruit).

OLED (SSD1306)

Define: CONDUYT_MODULE_OLED, Name: oled1306

CmdMethodPayload
0x01beginwidth(u8) + height(u8) + i2c_addr(u8)
0x02clear(none)
0x03textx(u8) + y(u8) + size(u8) + string(N)
0x04drawRectx(u8) + y(u8) + w(u8) + h(u8) + fill(u8)
0x05drawBitmapx(u8) + y(u8) + w(u8) + h(u8) + data(N)
0x06show(none)

Requires: Adafruit_SSD1306 + Adafruit_GFX.

PID Controller

Define: CONDUYT_MODULE_PID, Name: pid

CmdMethodPayload
0x01configkp(f32) + ki(f32) + kd(f32)
0x02setTargetvalue(f32)
0x03setInputpin(u8)
0x04setOutputpin(u8)
0x05enable/disableflag(u8: 0 or 1)

PID loop runs at 100ms intervals in poll().

I2C Passthrough

Define: CONDUYT_MODULE_I2C, Name: i2c

Not a traditional module. Exists as a capability marker in HELLO_RESP. Raw I2C operations are handled by the core I2C_WRITE, I2C_READ, and I2C_READ_REG commands.

Constants

Command Types (CONDUYT_CMD_*)

CONDUYT_CMD_PING            0x01
CONDUYT_CMD_HELLO           0x02
CONDUYT_CMD_PIN_MODE        0x10
CONDUYT_CMD_PIN_WRITE       0x11
CONDUYT_CMD_PIN_READ        0x12
CONDUYT_CMD_PIN_SUBSCRIBE   0x13
CONDUYT_CMD_PIN_UNSUBSCRIBE 0x14
CONDUYT_CMD_I2C_WRITE       0x20
CONDUYT_CMD_I2C_READ        0x21
CONDUYT_CMD_I2C_READ_REG    0x22
CONDUYT_CMD_SPI_XFER        0x30
CONDUYT_CMD_MOD_CMD         0x40
CONDUYT_CMD_STREAM_START    0x50
CONDUYT_CMD_STREAM_STOP     0x51
CONDUYT_CMD_DS_WRITE        0x60
CONDUYT_CMD_DS_READ         0x61
CONDUYT_CMD_DS_SUBSCRIBE    0x62
CONDUYT_CMD_OTA_BEGIN       0x70
CONDUYT_CMD_OTA_CHUNK       0x71
CONDUYT_CMD_OTA_FINALIZE    0x72
CONDUYT_CMD_RESET           0xF0

Event Types (CONDUYT_EVT_*)

CONDUYT_EVT_PONG            0x80
CONDUYT_EVT_HELLO_RESP      0x81
CONDUYT_EVT_ACK             0x82
CONDUYT_EVT_NAK             0x83
CONDUYT_EVT_PIN_EVENT       0x90
CONDUYT_EVT_PIN_READ_RESP   0x91
CONDUYT_EVT_I2C_READ_RESP   0xA0
CONDUYT_EVT_SPI_XFER_RESP   0xB0
CONDUYT_EVT_MOD_EVENT       0xC0
CONDUYT_EVT_MOD_RESP        0xC1
CONDUYT_EVT_STREAM_DATA     0xD0
CONDUYT_EVT_DS_EVENT        0xD1
CONDUYT_EVT_DS_READ_RESP    0xD2
CONDUYT_EVT_LOG             0xE0
CONDUYT_EVT_FATAL           0xFF

Error Codes (CONDUYT_ERR_*)

CONDUYT_ERR_UNKNOWN_TYPE        0x01
CONDUYT_ERR_CRC_MISMATCH       0x02
CONDUYT_ERR_PAYLOAD_TOO_LARGE  0x03
CONDUYT_ERR_INVALID_PIN        0x04
CONDUYT_ERR_PIN_MODE_UNSUPPORTED 0x05
CONDUYT_ERR_I2C_NOT_AVAILABLE  0x06
CONDUYT_ERR_I2C_NACK           0x07
CONDUYT_ERR_MODULE_NOT_LOADED  0x08
CONDUYT_ERR_UNKNOWN_MODULE_CMD 0x09
CONDUYT_ERR_MODULE_BUSY        0x0A
CONDUYT_ERR_SUB_LIMIT_REACHED  0x0B
CONDUYT_ERR_OUT_OF_MEMORY      0x0C
CONDUYT_ERR_UNKNOWN_DATASTREAM 0x0D
CONDUYT_ERR_DATASTREAM_READONLY 0x0E
CONDUYT_ERR_OTA_INVALID        0x0F
CONDUYT_ERR_VERSION_MISMATCH   0x10

Type Codes (CONDUYT_TYPE_*)

CONDUYT_TYPE_BOOL     0x01
CONDUYT_TYPE_INT8     0x02
CONDUYT_TYPE_UINT8    0x03
CONDUYT_TYPE_INT16    0x04
CONDUYT_TYPE_UINT16   0x05
CONDUYT_TYPE_INT32    0x06
CONDUYT_TYPE_FLOAT32  0x07
CONDUYT_TYPE_STRING   0x08
CONDUYT_TYPE_BYTES    0x09

Pin Capabilities (CONDUYT_PIN_CAP_*)

CONDUYT_PIN_CAP_DIGITAL_IN   (1 << 0)
CONDUYT_PIN_CAP_DIGITAL_OUT  (1 << 1)
CONDUYT_PIN_CAP_PWM_OUT      (1 << 2)
CONDUYT_PIN_CAP_ANALOG_IN    (1 << 3)
CONDUYT_PIN_CAP_I2C_SDA      (1 << 4)
CONDUYT_PIN_CAP_I2C_SCL      (1 << 5)
CONDUYT_PIN_CAP_SPI          (1 << 6)
CONDUYT_PIN_CAP_INTERRUPT    (1 << 7)

Pin Modes (CONDUYT_PIN_MODE_*)

CONDUYT_PIN_MODE_INPUT        0x00
CONDUYT_PIN_MODE_OUTPUT       0x01
CONDUYT_PIN_MODE_PWM          0x02
CONDUYT_PIN_MODE_ANALOG       0x03
CONDUYT_PIN_MODE_INPUT_PULLUP 0x04

Subscription Modes (CONDUYT_SUB_*)

CONDUYT_SUB_CHANGE      0x01
CONDUYT_SUB_RISING      0x02
CONDUYT_SUB_FALLING     0x03
CONDUYT_SUB_ANALOG_POLL 0x04