> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Modbus Serial Route

> Connect to PLCs and industrial devices using Modbus RTU over RS-232/RS-485 serial connections

## Modbus Serial Overview

The `MODBUS_SERIAL` route enables communication with industrial devices using Modbus RTU protocol over serial connections (RS-232 or RS-485). It supports the same TAG-based configuration as Modbus TCP but adds serial-specific parameters for baud rate, parity, and data bits.

<Tip>
  Modbus Serial (RTU) is commonly used for legacy equipment, long-distance communication, and multi-drop RS-485 networks where multiple devices share a single bus.
</Tip>

## Basic Syntax

```lot theme={null}
DEFINE ROUTE SerialDevice WITH TYPE MODBUS_SERIAL
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "COM1"
        WITH BAUD_RATE 9600
        WITH PARITY "NONE"
        WITH DATA_BITS 8
        WITH STOP_BITS "ONE"
        WITH SLAVE_ID 1
    ADD MAPPING SensorReadings
        WITH EVERY 1 SECOND
        ADD TAG Temperature
            WITH ADDRESS "100"
            WITH ADDRESS_TYPE "HOLDING_REGISTER"
            WITH DATA_TYPE "FLOAT"
            WITH SOURCE_TOPIC "serial/temperature"
```

***

## Serial Configuration

### MODBUS\_CONFIG Parameters

<ParamField path="CONNECTION_TYPE" type="string" required>
  Must be set to `SERIAL` for serial communication.
</ParamField>

<ParamField path="COM_PORT" type="string" required>
  Serial port name. Windows: `COM1`, `COM2`, etc. Linux: `/dev/ttyUSB0`, `/dev/ttyS0`, etc.
</ParamField>

<ParamField path="BAUD_RATE" type="integer" required>
  Communication speed in bits per second. Common values: 9600, 19200, 38400, 57600, 115200.
</ParamField>

<ParamField path="PARITY" type="string">
  Parity checking: `NONE`, `ODD`, `EVEN`, `MARK`, or `SPACE`. Default: NONE.
</ParamField>

<ParamField path="DATA_BITS" type="integer">
  Number of data bits per character: 7 or 8. Default: 8.
</ParamField>

<ParamField path="STOP_BITS" type="string">
  Number of stop bits: `ONE`, `TWO`, or `ONEPOINTFIVE`. Default: ONE.
</ParamField>

<ParamField path="SLAVE_ID" type="integer" required>
  Modbus slave/unit ID (1-247). Each device on the bus must have a unique ID.
</ParamField>

<ParamField path="READ_TIMEOUT" type="integer">
  Read operation timeout in milliseconds. Default: 1000.
</ParamField>

<ParamField path="WRITE_TIMEOUT" type="integer">
  Write operation timeout in milliseconds. Default: 1000.
</ParamField>

<ParamField path="RETRY_COUNT" type="integer">
  Number of retry attempts on failure. Default: 3.
</ParamField>

<ParamField path="RETRY_DELAY" type="integer">
  Delay between retries in milliseconds. Default: 100.
</ParamField>

***

## Common Serial Settings

Different devices use different serial settings. Here are common configurations:

<Tabs>
  <Tab title="Standard (Most Common)">
    ```lot theme={null}
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "COM1"
        WITH BAUD_RATE 9600
        WITH PARITY "NONE"
        WITH DATA_BITS 8
        WITH STOP_BITS "ONE"
        WITH SLAVE_ID 1
    ```

    <Tooltip tip="9600 baud, 8 data bits, No parity, 1 stop bit">Settings: 9600 8N1</Tooltip>
  </Tab>

  <Tab title="Even Parity">
    ```lot theme={null}
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "COM1"
        WITH BAUD_RATE 9600
        WITH PARITY "EVEN"
        WITH DATA_BITS 8
        WITH STOP_BITS "ONE"
        WITH SLAVE_ID 1
    ```

    <Tooltip tip="9600 baud, 8 data bits, Even parity, 1 stop bit. Common in older equipment.">Settings: 9600 8E1</Tooltip>
  </Tab>

  <Tab title="High Speed">
    ```lot theme={null}
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "/dev/ttyUSB0"
        WITH BAUD_RATE 115200
        WITH PARITY "NONE"
        WITH DATA_BITS 8
        WITH STOP_BITS "ONE"
        WITH SLAVE_ID 1
    ```

    <Tooltip tip="115200 baud, 8 data bits, No parity, 1 stop bit. For modern devices.">Settings: 115200 8N1</Tooltip>
  </Tab>

  <Tab title="Linux USB Adapter">
    ```lot theme={null}
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "/dev/ttyUSB0"
        WITH BAUD_RATE 9600
        WITH PARITY "NONE"
        WITH DATA_BITS 8
        WITH STOP_BITS "ONE"
        WITH SLAVE_ID 1
    ```

    <Note>
      On Linux, USB-to-serial adapters typically appear as `/dev/ttyUSB0`, `/dev/ttyUSB1`, etc.
    </Note>
  </Tab>
</Tabs>

***

## RS-485 Multi-Drop Networks

RS-485 allows multiple devices on a single bus. Each device must have a unique slave ID:

```mermaid theme={null}
graph LR
    subgraph bus [RS-485 Bus]
        Master[Coreflux]
        Device1[Device 1<br/>ID: 1]
        Device2[Device 2<br/>ID: 2]
        Device3[Device 3<br/>ID: 3]
    end
    
    Master --- Device1
    Master --- Device2
    Master --- Device3
```

### Multi-Device Configuration

Create separate routes or use different slave IDs:

```lot theme={null}
DEFINE ROUTE TemperatureController WITH TYPE MODBUS_SERIAL
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "COM1"
        WITH BAUD_RATE 9600
        WITH SLAVE_ID 1
    ADD MAPPING TempController
        WITH EVERY 1 SECOND
        ADD TAG Temperature
            WITH ADDRESS "0"
            WITH ADDRESS_TYPE "HOLDING_REGISTER"
            WITH DATA_TYPE "FLOAT"
            WITH SOURCE_TOPIC "modbus/device1/temperature"

DEFINE ROUTE FlowMeter WITH TYPE MODBUS_SERIAL
    ADD MODBUS_CONFIG
        WITH CONNECTION_TYPE "SERIAL"
        WITH COM_PORT "COM1"
        WITH BAUD_RATE 9600
        WITH SLAVE_ID 2
    ADD MAPPING FlowMeter
        WITH EVERY 1 SECOND
        ADD TAG FlowRate
            WITH ADDRESS "0"
            WITH ADDRESS_TYPE "INPUT_REGISTER"
            WITH DATA_TYPE "FLOAT"
            WITH SOURCE_TOPIC "modbus/device2/flow"
```

<Warning>
  When sharing a serial port between multiple slave IDs, ensure polling intervals are set to avoid bus collisions. The route handles sequencing automatically, but very fast polling across many devices may cause timeouts.
</Warning>

***

## TAG Configuration

The TAG configuration for Modbus Serial is identical to Modbus TCP. See the [Modbus TCP documentation](./modbus-tcp) for complete TAG parameter reference.

### Quick Reference

```lot theme={null}
ADD TAG SensorValue
    WITH ADDRESS "100"
    WITH ADDRESS_TYPE "HOLDING_REGISTER"
    WITH DATA_TYPE "FLOAT"
    WITH SOURCE_TOPIC "serial/sensor"
    WITH SCALING 0.1
    WITH OFFSET 0
    WITH UNIT "°C"
    WITH DEADBAND 0.5
    WITH PUBLISH_MODE "JSON"
    WITH WRITABLE "true"
    WITH DESTINATION_TOPIC "serial/sensor/set"
    WITH BYTE_ORDER "BIGENDIAN"
    WITH WORD_ORDER "BIGENDIAN"
```

***

## Event-Based Operations

For on-demand Modbus Serial operations (not polling), use the EVENT syntax. Publish a message to SOURCE\_TOPIC to trigger the operation; the route executes it and publishes the result to DESTINATION\_TOPIC.

Modbus Serial supports the same EVENT operations as Modbus TCP. See the [Modbus TCP Event-Based Operations](./modbus-tcp#event-based-operations) for the full supported operations table and query parameter details.

### Read Example

Read 10 holding registers on demand:

```lot theme={null}
ADD EVENT ReadRegisters
    WITH SOURCE_TOPIC "serial/commands/read"
    WITH DESTINATION_TOPIC "serial/responses/read"
    WITH QUERY "{operation: READ_HOLDING_REGISTERS, start_address: 0, count: 10}"
```

### Write Example

Write a single register on demand:

```lot theme={null}
ADD EVENT WriteRegister
    WITH SOURCE_TOPIC "serial/commands/write"
    WITH DESTINATION_TOPIC "serial/responses/write"
    WITH QUERY "{operation: WRITE_SINGLE_REGISTER, address: 100, value: 500}"
```

***

## Complete Examples

<Tabs>
  <Tab title="Single Device">
    Basic connection to a single Modbus RTU device:

    ```lot theme={null}
    DEFINE ROUTE EnergyMeter WITH TYPE MODBUS_SERIAL
        ADD MODBUS_CONFIG
            WITH CONNECTION_TYPE "SERIAL"
            WITH COM_PORT "COM3"
            WITH BAUD_RATE 9600
            WITH PARITY "EVEN"
            WITH DATA_BITS 8
            WITH STOP_BITS "ONE"
            WITH SLAVE_ID 1
            WITH READ_TIMEOUT 1000
            WITH RETRY_COUNT 3
        ADD MAPPING Readings
            WITH EVERY 1 SECOND
            ADD TAG Voltage
                WITH ADDRESS "0"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "FLOAT"
                WITH SOURCE_TOPIC "meter/voltage"
                WITH UNIT "V"
            ADD TAG Current
                WITH ADDRESS "2"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "FLOAT"
                WITH SOURCE_TOPIC "meter/current"
                WITH UNIT "A"
            ADD TAG Power
                WITH ADDRESS "4"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "FLOAT"
                WITH SOURCE_TOPIC "meter/power"
                WITH UNIT "kW"
    ```
  </Tab>

  <Tab title="Temperature Controller">
    Read process value and write setpoint:

    ```lot theme={null}
    DEFINE ROUTE TempController WITH TYPE MODBUS_SERIAL
        ADD MODBUS_CONFIG
            WITH CONNECTION_TYPE "SERIAL"
            WITH COM_PORT "/dev/ttyUSB0"
            WITH BAUD_RATE 9600
            WITH PARITY "NONE"
            WITH DATA_BITS 8
            WITH STOP_BITS "ONE"
            WITH SLAVE_ID 1
        ADD MAPPING Control
            WITH EVERY 500 MILLISECONDS
            ADD TAG ProcessValue
                WITH ADDRESS "0"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "INT16"
                WITH SOURCE_TOPIC "tempctrl/pv"
                WITH SCALING 0.1
                WITH UNIT "°C"
            ADD TAG Setpoint
                WITH ADDRESS "1"
                WITH ADDRESS_TYPE "HOLDING_REGISTER"
                WITH DATA_TYPE "INT16"
                WITH SOURCE_TOPIC "tempctrl/sp"
                WITH SCALING 0.1
                WITH UNIT "°C"
                WITH WRITABLE "true"
                WITH DESTINATION_TOPIC "tempctrl/sp/set"
            ADD TAG Output
                WITH ADDRESS "2"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "INT16"
                WITH SOURCE_TOPIC "tempctrl/output"
                WITH SCALING 0.1
                WITH UNIT "%"
    ```
  </Tab>

  <Tab title="VFD (Variable Frequency Drive)">
    Control a motor drive:

    ```lot theme={null}
    DEFINE ROUTE VFDControl WITH TYPE MODBUS_SERIAL
        ADD MODBUS_CONFIG
            WITH CONNECTION_TYPE "SERIAL"
            WITH COM_PORT "COM2"
            WITH BAUD_RATE 19200
            WITH PARITY "EVEN"
            WITH DATA_BITS 8
            WITH STOP_BITS "ONE"
            WITH SLAVE_ID 1
        ADD MAPPING DriveStatus
            WITH EVERY 200 MILLISECONDS
            ADD TAG ActualSpeed
                WITH ADDRESS "8451"
                WITH ADDRESS_TYPE "HOLDING_REGISTER"
                WITH DATA_TYPE "UINT16"
                WITH SOURCE_TOPIC "vfd/speed/actual"
                WITH SCALING 0.1
                WITH UNIT "Hz"
            ADD TAG MotorCurrent
                WITH ADDRESS "8448"
                WITH ADDRESS_TYPE "HOLDING_REGISTER"
                WITH DATA_TYPE "UINT16"
                WITH SOURCE_TOPIC "vfd/current"
                WITH SCALING 0.01
                WITH UNIT "A"
            ADD TAG SpeedReference
                WITH ADDRESS "8502"
                WITH ADDRESS_TYPE "HOLDING_REGISTER"
                WITH DATA_TYPE "UINT16"
                WITH SOURCE_TOPIC "vfd/speed/reference"
                WITH SCALING 0.1
                WITH UNIT "Hz"
                WITH WRITABLE "true"
                WITH DESTINATION_TOPIC "vfd/speed/set"
            ADD TAG ControlWord
                WITH ADDRESS "8501"
                WITH ADDRESS_TYPE "HOLDING_REGISTER"
                WITH DATA_TYPE "UINT16"
                WITH SOURCE_TOPIC "vfd/control"
                WITH WRITABLE "true"
                WITH DESTINATION_TOPIC "vfd/control/set"
    ```
  </Tab>

  <Tab title="Combined (Cyclic + On-Demand)">
    Continuous monitoring with on-demand read and write events in the same route:

    ```lot theme={null}
    DEFINE ROUTE FullSerialSetup WITH TYPE MODBUS_SERIAL
        ADD MODBUS_CONFIG
            WITH CONNECTION_TYPE "SERIAL"
            WITH COM_PORT "COM3"
            WITH BAUD_RATE 9600
            WITH PARITY "EVEN"
            WITH DATA_BITS 8
            WITH STOP_BITS "ONE"
            WITH SLAVE_ID 1
        
        ADD MAPPING ProcessReadings
            WITH EVERY 1 SECOND
            ADD TAG Voltage
                WITH ADDRESS "0"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "FLOAT"
                WITH SOURCE_TOPIC "meter/voltage"
                WITH UNIT "V"
            ADD TAG Current
                WITH ADDRESS "2"
                WITH ADDRESS_TYPE "INPUT_REGISTER"
                WITH DATA_TYPE "FLOAT"
                WITH SOURCE_TOPIC "meter/current"
                WITH UNIT "A"
        
        ADD EVENT ReadRegisters
            WITH SOURCE_TOPIC "serial/commands/read"
            WITH DESTINATION_TOPIC "serial/responses/read"
            WITH QUERY "{operation: READ_HOLDING_REGISTERS, start_address: 0, count: 10}"
        
        ADD EVENT WriteRegister
            WITH SOURCE_TOPIC "serial/commands/write"
            WITH DESTINATION_TOPIC "serial/responses/write"
            WITH QUERY "{operation: WRITE_SINGLE_REGISTER, address: 100, value: 0}"
    ```

    The MAPPING continuously reads voltage and current. To trigger an on-demand register read, publish any message to `serial/commands/read`. To write, publish to `serial/commands/write`.
  </Tab>
</Tabs>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="No Communication">
    * Verify COM port is correct and not in use by another application
    * Check physical wiring (TX/RX, A/B for RS-485)
    * Ensure baud rate matches device settings
    * Verify slave ID is correct
    * Check device power and status indicators
  </Accordion>

  <Accordion title="CRC Errors">
    * CRC errors indicate data corruption
    * Check cable quality and length
    * For RS-485, ensure proper termination resistors (120Ω at each end)
    * Reduce baud rate for long cable runs
    * Check for electrical interference
  </Accordion>

  <Accordion title="Timeout Errors">
    * Increase `READ_TIMEOUT` value
    * Verify serial settings match device exactly
    * Check for bus conflicts (multiple masters)
    * For RS-485, ensure proper biasing
  </Accordion>

  <Accordion title="Incorrect Values">
    * Verify register addresses in device documentation
    * Check `BYTE_ORDER` and `WORD_ORDER` settings
    * Confirm `DATA_TYPE` matches register size
    * Some devices use 1-based addressing (subtract 1 from documented address)
  </Accordion>

  <Accordion title="Port Not Found">
    * Windows: Check Device Manager for correct COM port number
    * Linux: Check `/dev/tty*` with `ls -l /dev/tty*`
    * Ensure USB-to-serial driver is installed
    * Verify user has permissions to access serial port
  </Accordion>
</AccordionGroup>

***

## RS-485 Wiring Tips

<Note>
  Proper wiring is critical for reliable RS-485 communication.
</Note>

| Wire   | Function | Description             |
| ------ | -------- | ----------------------- |
| A (D-) | Data -   | Negative data line      |
| B (D+) | Data +   | Positive data line      |
| GND    | Ground   | Signal ground reference |

**Best Practices:**

* Use twisted pair cable for A and B lines
* Add 120Ω termination resistors at both ends of the bus
* Keep cable length under 1200m (4000ft)
* Use bias resistors if experiencing idle-line issues
* Ensure all grounds are connected

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Modbus TCP" icon="network-wired" href="./modbus-tcp">
    Connect to Modbus devices over Ethernet.
  </Card>

  <Card title="Siemens S7" icon="microchip" href="./siemens-s7">
    Connect directly to Siemens PLCs.
  </Card>
</CardGroup>
