Menu

Publishing Data

How to send data from your device to the OhioIoT platform — and what happens when it arrives.

Topic prefixing with ~/ and ~/~/

Every topic on the broker must begin with your MQTT username, and most device-scoped topics also include your device ID as the second segment. Typing those out on every publish call is noisy, so the SDK gives you two shortcuts: ~/ and ~/~/. The SDK rewrites them at publish time — they never leave the device.

// ~/~/ expands to  username/deviceID/
mqtt.publish("~/~/numb/temperature/temp", 72.5);
// actual topic on the broker:
// larry/a8f3k2m1/numb/temperature/temp  →  72.5

// ~/ expands to  username/  (device ID is skipped)
mqtt.publish("~/outdoor/humidity", 45.2);
// actual topic on the broker:
// larry/outdoor/humidity  →  45.2

// no prefix — topic is sent verbatim
mqtt.publish("larry/fleet/alert", "rollover");
// actual topic on the broker:
// larry/fleet/alert  →  rollover

The username comes from your MQTT_USER credential, and the device ID comes from device_id.get_or_set() at boot.

Only a leading ~/ or ~/~/ is rewritten. A ~ anywhere else in the topic is treated as a literal character and sent through unchanged.

Which prefix to use

~/~/ for device-scoped topics — the default choice. Any data that belongs to a specific device: sensor readings, status, config echoes, per-device commands. This is what the OhioIoT dashboard expects for graphs and device cards.

~/ for account-wide topics — when a message belongs to your account but not to one specific device. Group broadcasts, fleet-wide commands, or shared topics that multiple devices publish to or subscribe to.

No prefix — when you want full control over the topic string, or when you're copying topics from another source. You are responsible for making sure the topic starts with your username; the broker will reject it otherwise.

Data types

The third segment of your topic tells the platform what kind of data you're sending. Each type is handled differently:

TypeWhat it doesExample
numbAggregated over time, plotted on a number graph in the dashboard.numb/temperature/temp → 72.5
boolSent immediately (no aggregation), plotted on a boolean graph.bool/climate/heating → true
statusStored as a key-value field on the device card in the Devices screen.status/firmware → v1.2.0
configLike status, but for settings the device receives from the dashboard.config/setpoint → 72

For numb and bool, the topic has five segments: username / device / type / graph name / series name. Multiple series on the same graph share the graph name.

For status and config, the topic has four segments: username / device / type / key name. The payload is the value.

Publish overloads

The mqtt.publish() function accepts several payload types. The topic is always a const char *.

// string payload
mqtt.publish("~/~/status/firmware", "v1.2.0");

// float payload — converted to string with 2 decimal places
mqtt.publish("~/~/numb/temperature/temp", 72.53);

// integer payload — converted to string
mqtt.publish("~/~/numb/counters/boot_count", 14);

// empty payload — publishes with an empty string
mqtt.publish("~/~/numb/heartbeat/ping");
The platform coerces payloads based on the data type in the topic. If you publish "72.5" as a string to a numb topic, it's parsed as a number. If you publish "true", "yes", "on", or "1" to a bool topic, it's parsed as true. You don't need to worry about types on the device side.

Multiple series on one graph

To plot multiple values on the same graph, use the same graph name with different series names:

// both go on the "temperature" graph
mqtt.publish("~/~/numb/temperature/indoor",  72.5);
mqtt.publish("~/~/numb/temperature/outdoor", 58.3);

// both go on the "climate" graph
mqtt.publish("~/~/bool/climate/heating", "true");
mqtt.publish("~/~/bool/climate/cooling", "false");

Each unique series name becomes a separate line on the graph. The graph is created automatically the first time the platform sees the graph name — you don't need to configure anything.

Status and config

status and config values appear on the device card in the Devices screen. Use status for read-only values the device reports about itself:

mqtt.publish("~/~/status/firmware", "v1.2.0");
mqtt.publish("~/~/status/uptime",  millis() / 1000);
mqtt.publish("~/~/status/ip",      WiFi.localIP().toString().c_str());

Use config when the device echoes back a setting it received from the dashboard — this confirms to the user that the setting took effect:

// device receives a new setpoint from the dashboard,
// applies it, and echoes back confirmation:
mqtt.publish("~/~/config/setpoint", 72);