Device profiles
:::warning Not yet run on hardware This firmware is compile-verified and hardware-verified by nobody yet. It is a faithful implementation of the contract rather than a tested binary, so treat every claim here as what the code intends to do, and check the behaviour on a bench before you wire it to anything that matters. See Edge firmware for the full status. :::
One firmware runs any actuator or sensor use-case; the behavior is a JSON profile held on the chip. The profile names the peripherals wired to the pins, the commands Cobblr can fire, and (for inbound use-cases) where to push readings. It is the firmware's whole intelligence. Everything domain-specific (which plant, which part, which schedule) stays in a Cobblr wire or bundle.
This is the profile for a two-channel irrigation controller with a temperature and a moisture probe:
{
"name": "Greenhouse irrigation (2-channel)",
"token": "REPLACE_WITH_A_LONG_RANDOM_SECRET",
"wifi": { "ssid": "REPLACE_SSID", "pass": "REPLACE_PASS" },
"devices": [
{ "id": "pump", "name": "Pump", "kind": "relay", "pin": 26, "active_high": true },
{ "id": "valve", "name": "Drip valve", "kind": "relay", "pin": 27, "active_high": true },
{ "id": "temp", "name": "Soil temp", "kind": "ds18b20", "pin": 4, "unit": "C" },
{ "id": "moist", "name": "Soil moisture","kind": "analog", "pin": 34, "unit": "%", "scale": 0.0244, "offset": 0 }
],
"commands": {
"open-valve": { "device": "valve", "op": "pulse", "param": "seconds", "max": 600 },
"pump-on": { "device": "pump", "op": "on" },
"pump-off": { "device": "pump", "op": "off" }
}
}
What a profile declares
name: a label for the device.token: the bearer secret the chip requires on its data routes. Set the same value as the Cobblr connection'sapiKey. An empty token leaves the routes open, which is for the bench only.wifi: the network the chip joins after provisioning.devices: the logical inputs and outputs, each with anid(how Cobblr and the commands refer to it), akind, and its pin or pins. One chip can expose several devices.commands: named actions Cobblr can fire, each targeting one device with an operation.ingestandreport: the inbound half, for sensors that push into Cobblr. See readings that push into Cobblr.
A profile is validated when it loads and when it is saved. A device with an empty id, a negative pin, or an unrecognized kind is rejected, and a command or report that targets a device id that isn't in the list is rejected. An invalid stored profile makes the chip boot unprovisioned rather than run half-configured.
Peripheral kinds
The firmware's interpreter supports a fixed set of kinds. These are the ones the current build actually reads or drives:
kind | Direction | Notes |
|---|---|---|
relay / digital_out | out | on / off / toggle / pulse; off at boot (fail-safe) |
pwm | out | duty 0 to 255, carried in the command's param slot |
digital_in | in | reads 0 or 1 |
counter | in | monotonic tick total, polled edge-detect with debounce |
analog | in | ADC reading times scale plus offset |
ds18b20 | in | temperature in Celsius |
hx711 | in | grams via tare plus scale (the load-cell scale) |
ina219 | in | current in mA over I2C |
rfid | in | tag UID string over SPI |
Two kinds are named but not yet functional in this build. State them as gaps, not features:
displayis a recognized kind, but itsshowoperation is a TODO, so a command against a display device is refused today.dhtandbme280appear in the spec as planned, but the firmware's kind parser does not accept them yet, so a profile naming one is rejected when it loads. They need a firmware update before a profile can use them.
Commands
A command maps a name to a device and an operation. Operations:
on,off,toggle: set a relay's state and hold it.pulse: set the pin, then auto-release after a number of seconds read from the command'sparam(defaultseconds), clamped tomax(default 600). The auto-release is a watchdog, so a lost network connection can never leave a valve open.
Cobblr fires a command by POSTing { command, params }; a wire supplies the
params from an entity's fields. The flow is covered in
the edge-adapter contract.
Readings that push into Cobblr
Inbound kinds can be read on demand, and they can also push into Cobblr on their own schedule, which is what the differentiated use-cases need (a weight that decrements stock, a tap that checks out a tool, a tick that increments a build). A profile turns that on with two blocks:
{
"devices": [
{ "id": "scale", "name": "Filament shelf", "kind": "hx711", "pin": 16, "clk_pin": 17,
"unit": "g", "scale": 1.0, "offset": 0, "tare": 8345 }
],
"ingest": {
"url": "https://REPLACE_COBBLR_HOST/api/v1/orgs/REPLACE_SLUG/modules/core-devices/ingest",
"token": "REPLACE_WORKSPACE_API_TOKEN",
"connection": "REPLACE_DIGIFAB_CONNECTION_ID"
},
"report": [
{ "device": "scale", "every_s": 60, "on_change": 2.0 }
]
}
ingestis where the chip pushes: a Cobblr ingest URL, a token, and the connection id the events belong to.reportis what it pushes. A sensor uses a timer (every_s) with anon_changethreshold that suppresses a push until the value has moved by that much. A tap or a tick source sets"on": "event"and pushes each event.
The chip picks the event kind from the device: an RFID tag becomes a scanned
event, a counter becomes a counted event, and every other inbound sensor becomes
a reading. Cobblr resolves the event to the linked entity and a wire acts on it.
A failed push is dropped and never blocks the command loop.
Pins for the two-pin kinds
Most kinds use one pin. The load cell and the RFID reader use a second pin,
which the profile carries under a kind-specific key: clk_pin for the HX711 clock,
rst_pin for the RFID reset. The firmware reads whichever is present as the
device's second pin.