WAGO Compact Controller 100 (751-9301) and a MESHLE Gateway linked over Ethernet for Modbus TCP control

Control MESHLE Bluetooth Mesh Devices from a WAGO PLC via Modbus TCP (CODESYS Guide)

·MESHLE

MESHLE devices are wirelessly connected over MESHLE Bluetooth Mesh — a proprietary, offline-first mesh that keeps your lighting, sensors, and actuators running without the cloud. The MESHLE Gateway bridges that wireless network into the rest of your building’s infrastructure, exposing the mesh over an open API surface — REST, MQTT, Modbus TCP/IP, and BACnet™ — so any wired control system you already run can read and control it.

This guide walks the Modbus bridge end to end. You’ll configure a WAGO CC100 PLC as a Modbus TCP client that reads from and writes to wirelessly connected MESHLE devices through the gateway’s holding registers, then exposes those values as IEC variables in a CODESYS 3.5 project. Modbus TCP is configured entirely within the CODESYS device tree — no additional tools required.

It’s written for building-automation and BMS integrators and for OEM engineers who need wireless MESHLE lighting under PLC control. The worked example uses a multichannel dimmer (Out32) at unit address 1, which exposes 4 output channels and a power state over Modbus TCP holding registers — but the same pattern bridges every MESHLE device type on the mesh into your wired control system.

Overview

This guide documents the full process of bridging a wirelessly connected MESHLE Bluetooth Mesh network into a wired control system: setting up a WAGO CC100 as a Modbus TCP client that reads from and writes to MESHLE Gateway devices, and exposing those values as IEC variables in a CODESYS 3.5 project.

The sample device used throughout is the WAGO CC100 (751-9301). Any other WAGO controller that supports CODESYS 3.5 follows the same process — only the device selection step when creating the CODESYS project differs.

The CC100 acts as the Modbus TCP client and the MESHLE Gateway acts as the server. The CODESYS Modbus TCP device driver handles all polling automatically once channels are configured. Your PLC program simply reads from and writes to the mapped array variables.

Prerequisites

Have the following hardware and software in place before you start.

Hardware

  • WAGO CC100 (751-9301) on firmware 6.4.6.x or later (the sample device used here — any compatible WAGO controller supporting CODESYS 3.5 works)
  • MESHLE gateway with Modbus TCP server enabled on port 502
  • PC on the same network as the CC100 and the MESHLE gateway

Software

  • CODESYS 3.5 SP22 Patch 1 or later
  • WAGO Devices and Libraries package 2.0.9.2 or later (installed via CODESYS Installer)

MESHLE Gateway requirements

  • Modbus TCP server running on port 502
  • Unit address of the target device is 1
  • Register map version 1 layout (fixed device bank starting at register 256, device stride 50)

Register Map Reference (Out32 Device, Unit Address 1)

The MESHLE gateway uses a fixed register layout. A device’s bank starts at 256 + (unit address − 1) × 50. For unit address 1, the device base is therefore register 256.

Status registers (read, FC03, starting at offset 256)

Register OffsetAddressNameDescription
10266STATUS_FLAGSbit0 = power actual, bit2 = device online
23279OUT32_CH1Channel 1 actual, 0..1000 (×10 percent)
24280OUT32_CH2Channel 2 actual, 0..1000 (×10 percent)
25281OUT32_CH3Channel 3 actual, 0..1000 (×10 percent)
26282OUT32_CH4Channel 4 actual, 0..1000 (×10 percent)

Command registers (write, FC16, starting at offset 286)

Command base: 256 + 30 = 286

Register OffsetAddressNameDescription
0286CMD_FLAGSbit0 = power on/off
7293CMD_OUT32_CH1Channel 1 target, 0..1000 (×10 percent)
8294CMD_OUT32_CH2Channel 2 target, 0..1000 (×10 percent)
9295CMD_OUT32_CH3Channel 3 target, 0..1000 (×10 percent)
10296CMD_OUT32_CH4Channel 4 target, 0..1000 (×10 percent)
Important: All register values use a ×10 scale. A brightness of 75% is stored as register value 750. Always divide reads by 10 and multiply writes by 10 in your PLC program.

Part 1: CODESYS Project Setup

1.1 Create a New Project

  1. Open CODESYS and go to File > New Project
  2. Select Standard Project and click OK
  3. When prompted to select a device, search for 751-9301 and select WAGO Compact Controller 100
  4. Set the programming language to Structured Text (ST)
  5. Click OK and save the project with Ctrl+S
CODESYS Standard Project dialog with WAGO CC100 (751-9301) selected

The project opens with the default device tree. Note the device is named Device at this stage.

CODESYS device tree right after project creation

1.2 Rename the Device

The device must be named CC100 in the device tree for consistency and to avoid issues with certain WAGO libraries.

  1. Right-click the top-level device node (Device (751-9301 WAGO Compact Controller 100))
  2. Select Properties
  3. Change the name to CC100
  4. Click OK
CODESYS device tree after renaming the device to CC100

1.3 Add Ethernet Adapter

The Modbus TCP Client must sit under an Ethernet adapter node. This represents the CC100’s network interface.

  1. Right-click CC100 in the device tree
  2. Select Add Device...
  3. Expand Ethernet Adapter
  4. Select Ethernet
  5. Click Add Device and close the dialog
CODESYS device tree with an Ethernet adapter added under CC100

1.4 Add Modbus TCP Client

  1. Right-click Ethernet (Ethernet) in the device tree
  2. Select Add Device...
  3. Expand Fieldbuses > Modbus
  4. Expand Modbus TCP Client
  5. Select Modbus TCP Client (CODESYS, version 4.5.0.0)
  6. Click Add Device and close the dialog
Add Device dialog showing the Fieldbuses > Modbus > Modbus TCP Client pathCODESYS device tree with the Modbus TCP Client added under Ethernet
Note: CODESYS names these devices Modbus TCP Client and Modbus TCP Server — not Master/Slave as some older documentation does. The Client is the CC100 (the Modbus master). The Server represents the remote device being polled (the MESHLE gateway).

1.5 Add Modbus TCP Server

  1. Right-click Modbus_TCP_Client in the device tree
  2. Select Add Device...
  3. Expand Fieldbuses > Modbus > Modbus TCP Server
  4. Select Modbus TCP Server (CODESYS, version 4.5.0.0)
  5. Click Add Device and close the dialog
Add Device dialog showing the Modbus TCP Server

The device tree now shows the full structure:

CC100
└── Ethernet (Ethernet)
    └── Modbus_TCP_Client (Modbus TCP Client)
        └── Modbus_TCP_Server (Modbus TCP Server)
CODESYS device tree showing the complete Modbus structure

Part 2: Modbus TCP Server Configuration

2.1 Configure Gateway IP and Port

  1. Double-click Modbus_TCP_Server in the device tree
  2. The General tab opens
  3. Set Server IP address to the IP address of your MESHLE gateway
  4. Set Response timeout (ms) to 5000
  5. Set Port to 502
General tab showing the gateway IP address and port 502

2.2 Configure Read Channel

  1. Click the Modbus Server Channel tab
  2. Click Add Channel
  3. Configure the channel as follows:
    • Name: ReadStatus
    • Access type: Read Holding Registers (Function Code 3)
    • Trigger: Cyclic
    • Cycle time (ms): 100
    • READ Register Offset: 256
    • Length: 27
    • Error handling: Keep last value
  4. Click OK
Modbus Channel dialog showing the ReadStatus configuration

2.3 Configure Write Channel

  1. Still on the Modbus Server Channel tab, click Add Channel again
  2. Configure the channel as follows:
    • Name: WriteCommands
    • Access type: Write Multiple Registers (Function Code 16)
    • Trigger: Cyclic
    • Cycle time (ms): 500
    • WRITE Register Offset: 286
    • Length: 11
  3. Click OK

Both channels should now appear in the channel list:

Modbus Server Channel tab showing the ReadStatus and WriteCommands channels

2.4 Assign Global Variables in I/O Mapping

  1. Click the ModbusTCPServer I/O Mapping tab
  2. You will see the two channel arrays listed with empty Variable columns
  3. Click the empty Variable cell on the top-level ReadStatus row
  4. Type ReadStatus and press Enter
  5. Do the same for the top-level WriteCommands row — type WriteCommands and press Enter
  6. Save with Ctrl+S

A mapping indicator appears in the Mapping column, confirming CODESYS has created the global variables bound to the channel arrays.

I/O Mapping tab showing the ReadStatus array registersI/O Mapping tab showing the WriteCommands array registers
Note: Typing a plain name (no dot prefix) in the Variable column creates an implicit global variable automatically. These variables are then accessible directly in PLC_PRG without any namespace prefix.

2.5 Set Unit ID

This is a critical step. The Unit-ID must match the Modbus unit address of the target device on the MESHLE gateway. A mismatch causes the gateway to reject all requests, leading to repeated connect/disconnect cycles that eventually exhaust the gateway’s connection limit.

  1. Click the ModbusTCPServer Parameters tab
  2. Locate Unit-ID
  3. Set it to the unit address of your device — 1 in this example
  4. Also set ResponseTimeout to 5000
ModbusTCPServer Parameters tab showing Unit-ID set to 1
Important: The default Unit-ID is 255. If you leave it at the default while your device has a different unit address, the gateway rejects all requests. The symptom is a repeated “Connected” / “Connection aborted: socket keep-alive expired” loop in the Modbus TCP Client log, with an ever-increasing error counter. Always set Unit-ID to match your target device.

Part 3: Configure Ethernet Interface

The Ethernet adapter must be bound to the CC100’s correct physical network interface.

  1. Double-click Ethernet (Ethernet) in the device tree
  2. Click Browse... next to the Network interface field
  3. Select br0 — this is the CC100’s bridged network interface
  4. Do NOT check Adjust operating system settings — this would overwrite the CC100’s network configuration
Note: The default network interface shown may be a 192.168.x.x address. Always verify the interface is br0 on the correct subnet before going online.

Part 4: PLC Program

Open PLC_PRG. The editor has two panels: the declaration section at the top and the body at the bottom.

Declaration

PROGRAM PLC_PRG
VAR
    (* Actual values read from device - scaled 0..100 % *)
    rCh1_Actual     : REAL;
    rCh2_Actual     : REAL;
    rCh3_Actual     : REAL;
    rCh4_Actual     : REAL;
    bPower_Actual   : BOOL;
    bOnline         : BOOL;

    (* Commands to send to device *)
    bPower_Cmd      : BOOL;
    rCh1_Setpoint   : REAL;
    rCh2_Setpoint   : REAL;
    rCh3_Setpoint   : REAL;
    rCh4_Setpoint   : REAL;
END_VAR

Body

(* --- Read actual state from device --- *)
(* Registers return 0..1000, divide by 10 to get 0..100 % *)
rCh1_Actual   := WORD_TO_REAL(ReadStatus[23]) / 10.0;
rCh2_Actual   := WORD_TO_REAL(ReadStatus[24]) / 10.0;
rCh3_Actual   := WORD_TO_REAL(ReadStatus[25]) / 10.0;
rCh4_Actual   := WORD_TO_REAL(ReadStatus[26]) / 10.0;
bPower_Actual := ReadStatus[10].0;    (* bit 0 = power on/off *)
bOnline       := ReadStatus[10].2;    (* bit 2 = device reachable *)

(* --- Send commands to device --- *)
(* Power: sent as a dedicated power command, independent of channels *)
WriteCommands[0] := BOOL_TO_WORD(bPower_Cmd);

(* Channels: sent independently, multiply by 10 to get 0..1000 register scale *)
WriteCommands[7]  := REAL_TO_WORD(rCh1_Setpoint * 10.0);
WriteCommands[8]  := REAL_TO_WORD(rCh2_Setpoint * 10.0);
WriteCommands[9]  := REAL_TO_WORD(rCh3_Setpoint * 10.0);
WriteCommands[10] := REAL_TO_WORD(rCh4_Setpoint * 10.0);
PLC_PRG showing the final declaration and body code

The array indices map directly to register offsets within the configured channel:

  • ReadStatus[23] = register offset 23 within the device block = OUT32_CH1 actual
  • WriteCommands[7] = register offset 7 within the command section = CMD_OUT32_CH1

Part 5: Connect and Download

5.1 Fix Build Errors

On the first build, you may see 41 errors referencing DED.DEVICE_STATE. These come from the Compact_Controller_100_Onboard_IO node and are caused by two missing system libraries. They are not related to your Modbus code.

To fix:

  1. Double-click Library Manager
  2. Click Download Missing Libraries in the toolbar
  3. Two libraries will be listed as missing:
    • Redundancy Implementation 3.5.21.0
    • CAA Device Diagnosis 3.5.21.0
  4. Ensure both are checked and click Download
  5. Build again with F11 — errors should clear

5.2 Go Online

  1. Confirm the CC100 physical RUN/STOP switch is in the RUN position
  2. Go to Online > Login
  3. When prompted about no active path, click Yes to scan the network
  4. Select CC100 from the scan results and click OK
  5. If a device version mismatch warning appears, click OK to proceed
  6. When prompted to download, check Update boot application and click Yes
  7. Press F5 to start the application
CC100 connected screen showing the active device in CODESYS

Part 6: Reading and Writing Values

Reading Values

Once online and running, the PLC_PRG watch panel shows live values. The rCh1_Actual through rCh4_Actual variables update automatically from the MESHLE device channels via the ReadStatus channel. bOnline confirms the device is reachable.

PLC_PRG watch panel showing live channel values and bOnline = TRUE

Writing Values

To write a value from CODESYS to the MESHLE device, update the setpoint variables and write them via the debug window.

How to write in the debug window

  1. In the watch panel, click the Prepared Value column next to rCh1_Setpoint
  2. Enter the desired value (0..100 representing percent)
  3. Do the same for any other setpoints and bPower_Cmd
  4. Click Write Values in the debug toolbar (or press Ctrl+F7)
  5. The values are scaled by 10, written to WriteCommands, and sent to the device via FC16
Watch panel with prepared values set before writingWatch panel after the write showing actual values updated to match the setpoints
Note: The setpoint variables start at 0 and only take effect when written via the debug window or set programmatically. The actual values reflect the current device state and update independently.

Known Issues and Troubleshooting

Repeated Connect / Disconnect Loop

Symptom: The Modbus TCP Client log shows a repeated cycle of “Connected” followed immediately by “Connection aborted: socket keep-alive expired”. The error counter increments rapidly. Other Modbus clients on the network may also get blocked.

Cause: The Unit-ID on the Modbus TCP Server does not match the unit address of the target device on the MESHLE gateway. The gateway rejects all requests and the CODESYS driver repeatedly reconnects without properly closing the previous socket. The gateway supports a maximum of 3 concurrent connections, so slots fill up quickly.

Fix: Go to the ModbusTCPServer Parameters tab and set Unit-ID to match the unit address of your target device. Stop the application (Online > Stop), log out (Online > Logout), wait 30 to 60 seconds for the gateway to release stale connections, then log in and restart.

ModbusTCPServer Parameters showing correct Unit-ID = 1 and a stable connected log

Build Errors: DED.DEVICE_STATE Not Defined

Cause: The CAA Device Diagnosis library is missing from the CODESYS installation. This library is required by the CC100’s onboard IO device node.

Fix: In Library Manager, click Download Missing Libraries. Install both Redundancy Implementation and CAA Device Diagnosis when prompted.

Values Showing 0 After Going Online

Cause: Either the Ethernet adapter is bound to the wrong network interface, or the Unit-ID does not match the target device.

Fix: Check the Ethernet adapter is set to br0 on the correct subnet. Verify the Unit-ID in ModbusTCPServer Parameters matches your device. Check the Modbus TCP Client log for connection errors.

No Separate Download Step Required

Modbus TCP requires only a single download operation — Online > Login in CODESYS. All Modbus configuration is embedded in the CODESYS project and downloaded with it.

Beyond Modbus — every device, every protocol

The read/write pattern you built here for one multichannel dimmer applies to every device on your MESHLE Bluetooth Mesh network. Sensors expose registers you only read; actuators like single-channel dimmers, HCL, and HSV expose registers you read and write — same channel workflow, just different register counts. Once the MESHLE Gateway is on the network, your whole wirelessly connected mesh is addressable from any wired control system as a standard Modbus TCP server.

Modbus TCP is one of four open paths off the gateway. If your stack isn’t Modbus-based, the same MESHLE devices are reachable over REST API, MQTT, and BACnet™. Building a BMS or PLC integration around MESHLE lighting? Talk to us about your project — we support integrators and OEMs through the full design-in.

Further reading

BACnet™ is a trademark of ASHRAE.