Skip to content
Get started

Modbus Controller Sensor

The modbus_controller sensor platform creates a sensor from a modbus_controller component and requires Modbus Controller to be configured.

  • register_type (Required): type of the modbus register.

    • coil : Coils are 1-bit registers (ON/OFF values) that are used to control discrete outputs. They may be read and/or written. Modbus Function Code 1 (Read Coil Status) will be used.
    • discrete_input : discrete input register (read only coil) are similar to coils but can only be read. Modbus Function Code 2 (Read Input Status) will be used.
    • holding : Holding Registers - Holding registers are the most universal 16-bit register. They may be read and/or written. Modbus Function Code 3 (Read Holding Registers) will be used.
    • input : Input Registers - 16-bit registers used for input; may only be read. Modbus Function Code 4 (Read Input Registers) will be used. read is an accepted alias for this value.
  • address (Required, int): start address of the first register in a range (can be decimal or hexadecimal).

  • value_type (Optional): data type of the modbus register data. Defaults to U_WORD, a 16 bit integer in big endian format (MSB first).

    • U_WORD : unsigned 16 bit integer from 1 register = 16bit
    • S_WORD : signed 16 bit integer from 1 register = 16bit
    • U_WORD_S : unsigned 16 bit integer from 1 register with bytes swapped within the register = 16bit
    • S_WORD_S : signed 16 bit integer from 1 register with bytes swapped within the register = 16bit
    • U_DWORD : unsigned 32 bit integer from 2 registers = 32bit
    • S_DWORD : signed 32 bit integer from 2 registers = 32bit
    • U_DWORD_R : unsigned 32 bit integer from 2 registers low word first
    • S_DWORD_R : signed 32 bit integer from 2 registers low word first
    • U_QWORD : unsigned 64 bit integer from 4 registers = 64bit
    • S_QWORD : signed 64 bit integer from 4 registers = 64bit
    • U_QWORD_R : unsigned 64 bit integer from 4 registers low word first
    • S_QWORD_R : signed 64 bit integer from 4 registers low word first
    • FP32 : 32 bit IEEE 754 floating point from 2 registers
    • FP32_R : 32 bit IEEE 754 floating point - same as FP32 but low word first

WARNING

U_WORD_S and S_WORD_S are a rare, non-standard configuration: the two bytes within one 16-bit register are reversed (LSB first on the wire within that register). Modbus registers are big-endian (MSB first) per the specification. The _S suffix is not the same as _R (word order reversed / low word first across multiple registers).

  • bitmask (Optional, int): sometimes multiple values are packed in a single register’s response. The bitmask can be used to extract a value from the response. See Bitmasks.

  • skip_updates (Optional, int): Deprecated and no longer has any effect — every register range is polled on each update_interval. The key is still accepted (logging a config-time warning) and will be removed in 2027.3.0. To poll some registers less often, place those sensors on a second modbus_controller with the same address: and a slower update_interval:.

  • response_size (Optional, int): Size of the response for the register in bytes. If unset, defaults to the size implied by value_type (2 bytes per register).

  • reuse_previous_range (Optional, boolean or auto): How this item relates to the register range built just before it (same register type, ascending address order). auto (default) joins when the addresses are adjacent and the item’s position in the reply is exact; true joins unconditionally, reading across address gaps and past registers with a non-standard response_size; false always starts a new range here (later items may still extend it). See Register ranges. Replaces the deprecated force_new_range and register_count options, which will be removed in 2027.3.0 — the linked section covers migration.

  • lambda (Optional, lambda): Lambda to be evaluated every update interval to get the new value of the sensor.

    Parameters passed into the lambda

    • x (float): The parsed float value of the modbus data

    • data (std::span<const uint8_t>): span containing the complete raw modbus response bytes for this sensor note: because the response contains data for all registers in the same range you have to use data[item->offset] to get the first response byte for your sensor.

    • item (pointer to a SensorItem derived object): The sensor object itself.

    Possible return values for the lambda:

    • return <FLOATING_POINT_NUMBER>; the new value for the sensor.
    • return NAN; if the state should be considered invalid to indicate an error (advanced).
  • custom_pdu (Optional, list of bytes): The modbus PDU (function code + data) for a custom command. This allows using non-standard commands. The device address (taken from the controller’s address:) and the CRC are added automatically, so do not include a leading device-address byte. If custom_pdu is used, address and register_type can’t be used. Renamed from custom_command, which is now removed and raises a config-validation error. See Using custom_pdu for how to use custom_pdu

  • offset (Optional, int): Offset from start address in bytes (only required for uncommon response encodings). If more than one register is written in a command this value is used to find the start of this datapoint relative to start address. The component calculates the size of the range based on offset and size of the value type. For coil or discrete_input registers offset is the position of the coil/register because these registers encode 8 coils in one byte.

  • All other options from Sensor.

The example below will send 2 modbus commands (device address 1 assumed):

0x1 0x4 0x31 0x0 0x0 0x02 x7f 0x37 (read 2 registers starting at 0x3100)

0x1 0x3 0x90 0x1 0x0 0x1 0xf8 0xca (read 1 holding resister from 0x9001)

- platform: modbus_controller
modbus_controller_id: modbus1
id: pv_input_voltage
name: "PV array input voltage"
address: 0x3100
unit_of_measurement: "V" ## for any other unit the value is returned in minutes
register_type: input
value_type: U_WORD
accuracy_decimals: 1
filters:
- multiply: 0.01
- platform: modbus_controller
modbus_controller_id: modbus1
name: "Battery Capacity"
id: battery_capacity
register_type: holding
address: 0x9001
unit_of_measurement: "AH"
value_type: U_WORD

The modbus sensor platform allows you use a lambda that gets called before data is published using lambdas.

The example below logs the value as parsed and the raw modbus bytes received for this register range:

# Example configuration entry
sensor:
- platform: modbus_controller
modbus_controller_id: modbus1
id: battery_capacity
address: 0x9001
name: "Battery Capacity"
register_type: holding
value_type: U_WORD
lambda: |-
ESP_LOGI("","Lambda incoming value=%f - data array size is %d",x,data.size());
ESP_LOGI("","Sensor properties: adress = 0x%X, offset = 0x%X value type=%d",item->start_address,item->offset,item->sensor_value_type);
int i=0 ;
for (auto val : data) {
ESP_LOGI("","data[%d]=0x%02X (%d)",i,data[i],data[i]);
i++;
}
return x ;