Writing to devices from the cloud
What you’ll build
Section titled “What you’ll build”An OT device whose setpoints and switches an operator changes from the device’s Parameters tab in Cumulocity. Each edit is written to the device as one command with one result, the tab shows the device’s current values, and a state code shows up as a readable label that can be picked from a list.
The guide continues the Modbus guide with the demo’s
modbus-sim service, whose point library already has two writable points: temp_u16 and
coil_rw. It works the same way for every protocol.
How a write travels
Section titled “How a write travels”A point whose access is read_write or write is a device parameter. The packaged flows
do the rest:
ot-parameter-statekeeps one retained twin fragment per parameter set current, from the samples and from acknowledged writes. The thin-edge.io mapper mirrors it into the device’s managed object, which is what the Parameters tab shows.- An edit in the tab is a
c8y_ParameterUpdateoperation. Its template comes from tedge-parameter-plugin, a dependency of the package, andot-command-forwardturns it into onewrite-batchcommand for the connector. - The connector writes the points in order and reports one result;
ot-command-resultcompletes the operation.
Writes never open a second session to the device and never go through the tedge-dot write
CLI: they are thin-edge.io commands that the running connector carries out.
Parameter sets
Section titled “Parameter sets”Parameters are grouped into sets. One set is one twin fragment and one Cumulocity definition, and its name is a tenant-wide identifier, so it is derived from what the device is:
<device type, else the protocol>_<group, default "control">_parametersThe demo’s library declares type = "modbus-demo-sim", so temp_u16 lands in
modbus_demo_sim_control_parameters. coil_rw names two groups with
meta.parameter = { group = ["control", "commissioning"] }, so it is also in
modbus_demo_sim_commissioning_parameters, and an edit through either set keeps both in step.
Give every device type its own type (on the [[device]], or once in its point library).
Without it, every device of a protocol shares one set name, and two device types with different
points collide in the tenant. meta.parameter.set names a set outright, when a tenant already
has one. The rules are in contract §5.2
and the reasoning in RFC 0005.
Make the parameters editable
Section titled “Make the parameters editable”-
Declare the points writable. Set
access = "read_write"(or"write"for a point the device cannot read back) on the point.meta.parameterenriches the parameter’s schema withtitle,description,min,max,enum,defaultandorder; the demo’stemp_u16hasmeta = { parameter = { min = 0, max = 30000 } }. A read-only point can opt in withmeta.parameterand is shown read-only next to the controls, andmeta.parameter = falsekeeps a writable point out. -
Register the definitions once per tenant. The Parameters tab only lets you edit fragments that match a Digital Twin Manager definition.
tedge-dot describerenders them from the same configuration the service runs (by default every config in/etc/tedge/plugins/ot), so a tenant admin registers them, for example with go-c8y-cli:Terminal window defs=$(mktemp) one=$(mktemp)trap 'rm -f "$defs" "$one"' EXITtedge-dot describe --compact > "$defs" # one definition per linewhile read -r definition; doprintf '%s' "$definition" > "$one"C8Y_SETTINGS_CI=true c8y api POST /service/dtm/definitions/properties --data "@$one" </dev/null ||exit 1done < "$defs"The device never pushes definitions itself: device users do not hold the roles for it, and what a fleet may edit is a tenant decision. Without a definition the values are still in the device’s inventory, just not editable.
-
Edit. Open the child device’s Parameters tab, change a value and save. The operation goes to the device as one
write-batch, and the twin shows the new value once the write is acknowledged and the next sample confirms it.
Labels instead of codes
Section titled “Labels instead of codes”A value map (map) converts between the device’s code and a readable value, in both directions.
On a writable point it becomes a choice: with
map = { cases = [{ eq = 0, to = "off" }, { eq = 1, to = "auto" }, { eq = 2, to = "manual" }] }
the twin shows "auto", tedge-dot describe renders the property as an enum of the three
labels, and an operator who picks "manual" writes 2 to the register.
A write is refused, and nothing reaches the device, when the value is not one of the labels,
when the label belongs only to default or to a range without a write code, or when it has
the wrong type. The reason lists the accepted values. Parameter values are in engineering
units: the SDK applies the map, then inverts the point’s transform, before the connector
encodes the value.
See Mapping values for ranges, catch-alls and numeric text.
A parameter of its own: literal parameters
Section titled “A parameter of its own: literal parameters”A point that belongs to no set can be published as a fragment by itself.
meta.parameter = { fragment = "pump_speed" } makes the managed object hold
"pump_speed": 42 rather than an object around one value, and its definition is a primitive
schema. An edit of that fragment becomes a write-batch of one write to the point.
The fragment name is used verbatim, so it cannot be combined with set, group or key, and
one name is either a literal or a set: the first point to claim it keeps it, and
tedge-dot describe refuses the configuration.
To keep a set but choose a point’s key in it, use meta.parameter.key: key = "firmware.version"
publishes the point as version in a set called firmware. Keys are plain identifiers
([A-Za-z0-9_]) and unique per set on a device.
Writing from the gateway
Section titled “Writing from the gateway”The same commands are available on the broker, for local automation or testing. One point (the
contract’s write verb), or several in order with one result (write-batch):
tedge mqtt pub -r te/device/plc1/ot/modbus/cmd/write/w1 '{"status":"init","point":"temp_u16","value":4242}'tedge mqtt pub -r te/device/plc1/ot/modbus/cmd/write-batch/b1 '{"status":"init","writes":[{"point":"temp_u16","value":4242},{"point":"coil_rw","value":true}]}'The connector moves each command through executing to successful or failed on the same
topic. See contract §6.4.