Skip to main content

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's apiKey. 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 an id (how Cobblr and the commands refer to it), a kind, and its pin or pins. One chip can expose several devices.
  • commands: named actions Cobblr can fire, each targeting one device with an operation.
  • ingest and report: 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:

kindDirectionNotes
relay / digital_outouton / off / toggle / pulse; off at boot (fail-safe)
pwmoutduty 0 to 255, carried in the command's param slot
digital_ininreads 0 or 1
counterinmonotonic tick total, polled edge-detect with debounce
analoginADC reading times scale plus offset
ds18b20intemperature in Celsius
hx711ingrams via tare plus scale (the load-cell scale)
ina219incurrent in mA over I2C
rfidintag UID string over SPI

Two kinds are named but not yet functional in this build. State them as gaps, not features:

  • display is a recognized kind, but its show operation is a TODO, so a command against a display device is refused today.
  • dht and bme280 appear 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's param (default seconds), clamped to max (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 }
]
}
  • ingest is where the chip pushes: a Cobblr ingest URL, a token, and the connection id the events belong to.
  • report is what it pushes. A sensor uses a timer (every_s) with an on_change threshold 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.