Modbus TCP PLC to Cumulocity
What you’ll build
Section titled “What you’ll build”A thin-edge.io gateway that polls a Modbus TCP PLC every two seconds. The PLC appears in Cumulocity as a child device of the gateway, with its registers as measurements, a bad reading flagged as such, and its writable registers as device parameters.
The guide uses the demo’s modbus-sim service (a pymodbus simulator on port 5020) as the
PLC, so you can follow it without hardware. The last section shows what to change for a real
PLC.
You need a Linux gateway with thin-edge.io
installed and connected to Cumulocity (tedge connect c8y), and Docker on the same host for the
simulator.
Set it up
Section titled “Set it up”-
Install tedge-dot. Set up the thin-edge.io community repository, then install the package. It also pulls in
tedge-parameter-plugin, which serves the Cumulocity Parameters tab:Terminal window sudo apt-get install -y tedge-dot-c # or tedge-dot-rssudo systemctl status tedge-dotThe service starts with an empty config per protocol in
/etc/tedge/plugins/ot/, and the flows that map its data for Cumulocity are installed active in/etc/tedge/mappers/c8y/flows/. See Install for the other routes and for choosing between the two packages. -
Start the PLC simulator. The package ships the demo compose file, which pulls the prebuilt simulator images. Start only the Modbus one:
Terminal window docker compose -f /usr/share/tedge-dot/demo/docker-compose.yaml up -d modbus-sim -
Enable the Modbus demo config. It replaces the empty default and points at the simulator on
127.0.0.1:5020:Terminal window sudo cp /usr/share/tedge-dot/demo/modbus.toml /etc/tedge/plugins/ot/The service notices the change within a few seconds and starts polling; no restart is needed.
sudo systemctl reload tedge-dotapplies it at once. -
Watch the data on the gateway. The connector publishes one sample envelope per point, and the
ot-measurementflow turns each number into a thin-edge.io measurement:Terminal window tedge mqtt sub 'te/device/plc1/ot/modbus/#' # what the connector publishestedge mqtt sub 'te/+/+/+/+/m/+' # the measurements the flows make of it -
Find the device in Cumulocity. The
ot-registrationflow registersplc1as a child device of the gateway the first time its link is up. Open the gateway in Device management, then Child devices →plc1: its Measurements tab shows the points, in a measurement group named after the protocol (modbus).
What the config says
Section titled “What the config says”The [connector] table names the protocol and polls every two seconds. Its report sets a
heartbeat: a point whose value has not changed is still published every 30 minutes, so that
Cumulocity keeps the device available.
[connector]protocol = "modbus"service_name = "tedge-dot-modbus"poll_interval = "2s"log_level = "info"
# Heartbeat (reporting policy, see doc/reducing-data-volume.md): publish every point at least# this often, even when its value has not changed, because Cumulocity marks a device unavailable# when it sends nothing within its required interval; 30m stays inside the usual 60m. A# heartbeat is always a fresh reading, never a replayed value. It is the point's next poll. A# polled point without a change filter already publishes every reading, so this matters for# points you give `on_change`, `deadband`, `min_interval` or `debounce`. Those keys can be set# here, on a device or on a point; "0" switches an inherited setting off.report = { max_interval = "30m" }demo/config/modbus.toml on GitHubThe device holds only what is specific to this PLC: its address and the point library its
points come from. The library, /usr/share/tedge-dot/points.d/modbus/demo-sim.toml, declares
each register once for every device of that type, with its datatype, scaling, unit, value map
and reporting policy.
[[device]]name = "plc1"protocol_address = { transport = "tcp", host = "127.0.0.1", port = 5020, unit_id = 1 }default_mode = "typed"points_from = ["demo-sim"]demo/config/modbus.toml on GitHubA few points in that library show what the connector can do before anything reaches the cloud:
temp_scaledappliesdecimal_shift = -3and the unit°Cto a raw register, and publishes only on a change of 0.5 °C or more (report = { deadband = 0.5, min_interval = "10s" }).boiler_statemaps the same register tostandby,heatingoroverheat. A label is a string, so it becomes no measurement, but alarms and events can match it.bad_pointreads an address the simulator rejects, so its samples havebadquality.temp_u16andcoil_rwareread_write, so they are device parameters. See Writing to devices from the cloud.
Connect your own PLC
Section titled “Connect your own PLC”Add a [[device]] for each PLC to /etc/tedge/plugins/ot/modbus.toml, with its address in
protocol_address, and give it points:
- inline, as
[[device.point]]entries under the device; or - from a library of your own in
/etc/tedge/plugins/ot/points.d/modbus/<name>.toml, referenced aspoints_from = ["<name>"]. A site’s library shadows a packaged one of the same name, and a device can add or adjust points on top of its libraries.
A Modbus RTU device uses the serial settings in [connection.serial]. The address and point
fields of both transports are in the Modbus connector reference,
and every other key in the configuration reference.
The config workbench writes and checks a file for you.
Before you put a device into the service, check the connection and the points from the gateway with the CLI, which needs no broker:
tedge-dot read -c /etc/tedge/plugins/ot/modbus.toml -d plc1 --json