esphome: name: uart-mock-modbus-mesh host: api: logger: level: VERBOSE external_components: - source: type: local path: EXTERNAL_COMPONENT_PATH # Dummy uart entry to satisfy modbus's DEPENDENCIES = ["uart"] # The actual UART bus used is the uart_mock component below uart: baud_rate: 115200 port: /dev/null # Shared 3-bus mesh (see the shared_yaml markers): addr 1 = typed registers # backed by writable globals, addr 5 = the read/write 0x17 target, addr 2/3/6 # on the second server hub. auto_start everywhere: the controller polls at # boot, so the forwarding must already be live or early requests generate warnings. # Every test presses Start Scenario, so all merged actions fire in every test. uart_mock: - id: virtual_uart_server baud_rate: 9600 auto_start: true debug: on_tx: - then: - uart_mock.inject_rx: id: virtual_uart_controller data: !lambda return data; - uart_mock.inject_rx: id: virtual_uart_server_2 data: !lambda return data; - id: virtual_uart_server_2 baud_rate: 9600 auto_start: true debug: on_tx: - then: - uart_mock.inject_rx: id: virtual_uart_server data: !lambda return data; - uart_mock.inject_rx: id: virtual_uart_controller data: !lambda return data; - id: virtual_uart_controller baud_rate: 9600 auto_start: true debug: on_tx: - then: - uart_mock.inject_rx: id: virtual_uart_server data: !lambda return data; - uart_mock.inject_rx: id: virtual_uart_server_2 data: !lambda return data; globals: - id: stored_1 type: uint16_t initial_value: "0" - id: stored_u_word type: uint16_t initial_value: "99" - id: stored_u_word_s type: uint16_t initial_value: "4660" - id: stored_s_word type: int16_t initial_value: "-99" - id: stored_s_word_s type: int16_t initial_value: "-2" - id: stored_u_dword type: uint32_t initial_value: "16909060" - id: stored_s_dword type: int32_t initial_value: "-16909060" - id: stored_u_dword_r type: uint32_t initial_value: "67305985" - id: stored_s_dword_r type: int32_t initial_value: "-67305985" - id: stored_u_qword type: uint64_t initial_value: "72623859790382856" - id: stored_s_qword type: int64_t initial_value: "-72623859790382856" - id: stored_u_qword_r type: uint64_t initial_value: "578437695752307201" - id: stored_s_qword_r type: int64_t initial_value: "-578437695752307201" - id: stored_fp32 type: float initial_value: "3.14" - id: stored_fp32_r type: float initial_value: "2.5" - id: stored_bit_2 type: bool initial_value: "false" - id: stored_bit_3 type: bool initial_value: "true" modbus: - uart_id: virtual_uart_server id: virtual_modbus_server role: server - uart_id: virtual_uart_server_2 id: virtual_modbus_server_2 role: server - uart_id: virtual_uart_controller id: virtual_modbus_client role: client turnaround_time: 10ms modbus_controller: - address: 1 modbus_id: virtual_modbus_client id: modbus_controller_1 update_interval: 1s - address: 2 modbus_id: virtual_modbus_client id: modbus_controller_2 update_interval: 1s - address: 3 modbus_id: virtual_modbus_client id: modbus_controller_3 update_interval: 1s - address: 6 modbus_id: virtual_modbus_client id: modbus_controller_6 update_interval: 1s modbus_server: - address: 1 modbus_id: virtual_modbus_server registers: - address: 0x01 value_type: U_WORD read_lambda: return id(stored_u_word); write_lambda: id(stored_u_word) = x; return true; - address: 0x02 value_type: U_WORD_S read_lambda: return id(stored_u_word_s); write_lambda: id(stored_u_word_s) = x; return true; - address: 0x03 value_type: S_WORD read_lambda: return id(stored_s_word); write_lambda: id(stored_s_word) = x; return true; - address: 0x04 value_type: S_WORD_S read_lambda: return id(stored_s_word_s); write_lambda: id(stored_s_word_s) = x; return true; - address: 0x05 value_type: U_DWORD read_lambda: return id(stored_u_dword); write_lambda: id(stored_u_dword) = x; return true; - address: 0x08 value_type: S_DWORD read_lambda: return id(stored_s_dword); write_lambda: id(stored_s_dword) = x; return true; - address: 0x0B value_type: U_DWORD_R read_lambda: return id(stored_u_dword_r); write_lambda: id(stored_u_dword_r) = x; return true; - address: 0x0E value_type: S_DWORD_R read_lambda: return id(stored_s_dword_r); write_lambda: id(stored_s_dword_r) = x; return true; - address: 0x11 value_type: U_QWORD read_lambda: return id(stored_u_qword); write_lambda: id(stored_u_qword) = x; return true; - address: 0x16 value_type: S_QWORD read_lambda: return id(stored_s_qword); write_lambda: id(stored_s_qword) = x; return true; - address: 0x1B value_type: U_QWORD_R read_lambda: return id(stored_u_qword_r); write_lambda: id(stored_u_qword_r) = x; return true; - address: 0x20 value_type: S_QWORD_R read_lambda: return id(stored_s_qword_r); write_lambda: id(stored_s_qword_r) = x; return true; - address: 0x25 value_type: FP32 read_lambda: return id(stored_fp32); write_lambda: id(stored_fp32) = x; return true; - address: 0x28 value_type: FP32_R read_lambda: return id(stored_fp32_r); write_lambda: id(stored_fp32_r) = x; return true; - address: 5 modbus_id: virtual_modbus_server registers: # Writable + readable register: srv_write_1 plus the client's read-back # confirm the write half of the 0x17 ran before the read half (Modbus 6.17). - address: 0x01 value_type: U_WORD read_lambda: return id(stored_1); write_lambda: |- id(stored_1) = x; id(srv_write_1).publish_state(x); return true; # Read-only register, returned together with 0x01 by the 2-register read half. - address: 0x02 value_type: U_WORD read_lambda: return 0x00AA; - address: 2 modbus_id: virtual_modbus_server_2 registers: - address: 0x01 value_type: U_WORD read_lambda: return 919; - address: 3 modbus_id: virtual_modbus_server_2 registers: - address: 0x01 value_type: U_WORD read_lambda: return 929; - address: 6 modbus_id: virtual_modbus_server_2 bits: - address: 0x00 read_lambda: return true; - address: 0x01 read_lambda: return false; - address: 0x02 read_lambda: return id(stored_bit_2); write_lambda: id(stored_bit_2) = x; return true; - address: 0x03 read_lambda: return id(stored_bit_3); write_lambda: id(stored_bit_3) = x; return true; sensor: - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_word" address: 0x01 register_type: holding value_type: U_WORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_word_s" address: 0x02 register_type: holding value_type: U_WORD_S - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_word_s_raw" address: 0x02 register_type: holding value_type: U_WORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_word" address: 0x03 register_type: holding value_type: S_WORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_word_s" address: 0x04 register_type: holding value_type: S_WORD_S - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_dword" address: 0x05 register_type: holding value_type: U_DWORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_dword" address: 0x08 register_type: holding value_type: S_DWORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_dword_r" address: 0x0B register_type: holding value_type: U_DWORD_R - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_dword_r" address: 0x0E register_type: holding value_type: S_DWORD_R - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_qword" address: 0x11 register_type: holding value_type: U_QWORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_qword" address: 0x16 register_type: holding value_type: S_QWORD - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_u_qword_r" address: 0x1B register_type: holding value_type: U_QWORD_R - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_s_qword_r" address: 0x20 register_type: holding value_type: S_QWORD_R - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_fp32" address: 0x25 register_type: holding value_type: FP32 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "reg_fp32_r" address: 0x28 register_type: holding value_type: FP32_R - platform: modbus_controller modbus_controller_id: modbus_controller_2 name: "multi_reg_a" address: 0x01 register_type: holding value_type: U_WORD - platform: modbus_controller modbus_controller_id: modbus_controller_3 name: "multi_reg_b" address: 0x01 register_type: holding value_type: U_WORD # client_read_write observations, server- and client-side. - platform: template name: "srv_write_1" id: srv_write_1 - platform: template name: "client_read_0" id: client_read_0 - platform: template name: "client_read_1" id: client_read_1 # The number schema caps min/max at 16777215 (float32 integer precision), so # the large dword/qword baselines cannot be written back through these numbers. number: - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_word" address: 0x01 register_type: holding value_type: U_WORD min_value: 0 max_value: 65535 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_word_s" address: 0x02 register_type: holding value_type: U_WORD_S min_value: 0 max_value: 65535 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_word" address: 0x03 register_type: holding value_type: S_WORD min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_word_s" address: 0x04 register_type: holding value_type: S_WORD_S min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_dword" address: 0x05 register_type: holding value_type: U_DWORD min_value: 0 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_dword" address: 0x08 register_type: holding value_type: S_DWORD min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_dword_r" address: 0x0B register_type: holding value_type: U_DWORD_R min_value: 0 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_dword_r" address: 0x0E register_type: holding value_type: S_DWORD_R min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_qword" address: 0x11 register_type: holding value_type: U_QWORD min_value: 0 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_qword" address: 0x16 register_type: holding value_type: S_QWORD min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_u_qword_r" address: 0x1B register_type: holding value_type: U_QWORD_R min_value: 0 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_s_qword_r" address: 0x20 register_type: holding value_type: S_QWORD_R min_value: -16777215 max_value: 16777215 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_fp32" address: 0x25 register_type: holding value_type: FP32 min_value: -16777215 max_value: 16777215 step: 0.01 - platform: modbus_controller modbus_controller_id: modbus_controller_1 name: "write_fp32_r" address: 0x28 register_type: holding value_type: FP32_R min_value: -16777215 max_value: 16777215 step: 0.01 # The four bits are read both as coils (FC 0x01) and discrete inputs (FC 0x02); # the server serves both from one shared table, so the two views must agree. binary_sensor: - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_coil_0" address: 0x00 register_type: coil - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_coil_1" address: 0x01 register_type: coil - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_coil_2" address: 0x02 register_type: coil - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_coil_3" address: 0x03 register_type: coil - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_di_0" address: 0x00 register_type: discrete_input - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_di_1" address: 0x01 register_type: discrete_input - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_di_2" address: 0x02 register_type: discrete_input - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "bit_di_3" address: 0x03 register_type: discrete_input # write_bit_2 uses the single-coil write (FC 0x05); write_bit_3 opts into the # multiple-coils write (FC 0x0F) so both server write paths are exercised. switch: - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "write_bit_2" address: 0x02 register_type: coil - platform: modbus_controller modbus_controller_id: modbus_controller_6 name: "write_bit_3" address: 0x03 register_type: coil use_write_multiple: true button: - platform: template name: "Start Scenario" id: start_scenario_btn on_press: # FC 0x17: write reg 0x0001 = 0x1234, then read regs 0x0001..0x0002 back in the same transaction. - modbus_client.read_write_multiple_registers: address: 5 read_address: 0x0001 read_count: 2 write_address: 0x0001 values: [0x1234] on_response: then: - lambda: |- // values is the read-back block: reg 0x0001 (must be the just-written 0x1234) and reg 0x0002. if (values.size() >= 2) { id(client_read_0).publish_state(values[0]); id(client_read_1).publish_state(values[1]); }