Skip to content

Instantly share code, notes, and snippets.

@smirnowegor
Created February 6, 2025 11:55
Show Gist options
  • Save smirnowegor/b48a4d4c6398f44bc95854ca1a503952 to your computer and use it in GitHub Desktop.
Save smirnowegor/b48a4d4c6398f44bc95854ca1a503952 to your computer and use it in GitHub Desktop.
Modbus RTU - Esphome- Home Assistant test code
### Серверная часть на esp32 и тест через Modbus Poll и QModMaster
captive_portal:
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master
refresh: 60s
components:
- modbus_server
uart:
- id: intmodbus
tx_pin: GPIO17 # TX пин на ESP32
rx_pin: GPIO16 # RX пин на ESP32
baud_rate: 9600
stop_bits: 1
data_bits: 8
parity: NONE
debug:
direction: BOTH # Включаем отладку для TX и RX
modbus_server:
- id: modbuserver
uart_id: intmodbus
address: 1 # slave address Адрес устройства, фактически адрес нашего esp
holding_registers:
- start_address: 200 # Регистр для управления реле
number: 1 # Тут больше бы информации
on_write: |
if(value)
id(relay)->turn_on();
else
id(relay)->turn_off();
return value;
on_read: |
return id(relay)->state ? 1 : 0;
switch:
- platform: gpio
pin: GPIO4
id: relay
name: "Relay" #Наше реле для управления
#### Можно добавить на другой пин еще реле и добавить к текущему.
esphome:
name: power-meter
friendly_name: Power meter
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "nl7CGnDDjFnOHbaiKJAq/CQ7Xa+G+/R/DcWPD0k+Ut4="
ota:
- platform: esphome
password: "febdc9041f65ed06006278584c81090e"
wifi:
networks:
- ssid: !secret wifi_ssid
password: !secret wifi_password
- ssid: Esp
password: ""
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Power-Meter Fallback Hotspot"
password: "Y8OgcVDRqqsE"
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master
refresh: 60s
components:
- modbus_server
captive_portal:
uart:
- id: intmodbus
tx_pin: GPIO17 # TX пин на ESP32
rx_pin: GPIO16 # RX пин на ESP32
baud_rate: 9600
stop_bits: 1
data_bits: 8
parity: NONE
debug:
direction: BOTH # Включаем отладку для TX и RX
modbus_server:
- id: modbuserver
uart_id: intmodbus
address: 1 # slave address (адрес устройства)
holding_registers:
- start_address: 200 # Регистр для управления первым реле (GPIO4)
number: 1
on_write: |
if(value)
id(relay_1)->turn_on();
else
id(relay_1)->turn_off();
return value;
on_read: |
return id(relay_1)->state ? 1 : 0;
- start_address: 201 # Регистр для управления вторым реле (GPIO14)
number: 1
on_write: |
if(value)
id(relay_2)->turn_on();
else
id(relay_2)->turn_off();
return value;
on_read: |
return id(relay_2)->state ? 1 : 0;
switch:
- platform: gpio
pin: GPIO4
id: relay_1
name: "Relay 1" # Первое реле
- platform: gpio
pin: GPIO14
id: relay_2
name: "Relay 2" # Второе реле
### Подключаем в качестве теста передачу температуры от рандомного датчика. Сам датчик находится в разделе sensor, а в modbus_server: добавим - start_address: 202 # Register for reading temperature. Смотри в коде. На выходе можем принимать значение.
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master
refresh: 60s
components:
- modbus_server
captive_portal:
uart:
- id: intmodbus
tx_pin: GPIO17 # TX pin on ESP32
rx_pin: GPIO16 # RX pin on ESP32
baud_rate: 9600
stop_bits: 1
data_bits: 8
parity: NONE
debug:
direction: BOTH # Enable debug for TX and RX
modbus_server:
- id: modbuserver
uart_id: intmodbus
address: 1 # slave address
holding_registers:
- start_address: 200 # Register for controlling relay 1 (GPIO4)
number: 1
on_write: |
if(value)
id(relay_1)->turn_on();
else
id(relay_1)->turn_off();
return value;
on_read: |
return id(relay_1)->state ? 0x0001 : 0x0000;
- start_address: 201 # Register for controlling relay 2 (GPIO14)
number: 1
on_write: |
if(value)
id(relay_2)->turn_on();
else
id(relay_2)->turn_off();
return value;
on_read: |
return id(relay_2)->state ? 1 : 0;
- start_address: 202 # Register for reading temperature
number: 1
on_read: |
return id(virtual_temp).state * 10;
switch:
- platform: gpio
pin: GPIO4
id: relay_1
name: "Relay 1" # Первое реле
- platform: gpio
pin: GPIO14
id: relay_2
name: "Relay 2" # Второе реле
sensor:
- platform: template
id: virtual_temp
name: "Virtual Temperature"
unit_of_measurement: "C"
accuracy_decimals: 1
update_interval: 5s
lambda: |-
static float temp = 25.0;
temp += random(-5, 5) * 0.1;
return temp;
### Клиент (Мастер) на esp32
captive_portal:
uart:
- id: modbus_uart
tx_pin: GPIO17
rx_pin: GPIO16
baud_rate: 9600
stop_bits: 1
data_bits: 8
parity: NONE
debug:
direction: BOTH
dummy_receiver: false
modbus:
- id: modbus_client
uart_id: modbus_uart
# flow_control_pin:
# number: GPIO4
# inverted: false #не нужен, так как поток автоматически меняется
modbus_controller:
- id: modbus_device
address: 0x1
modbus_id: modbus_client
setup_priority: -10
update_interval: 10s # Опрашивать устройство каждые 10 секунд
switch:
- platform: modbus_controller
modbus_controller_id: modbus_device
name: "Relay 1"
register_type: holding
address: 200
bitmask: 1
use_write_multiple: true
lambda: |-
if (x) {
return 1;
} else {
return 0;
}
- platform: modbus_controller
modbus_controller_id: modbus_device
name: "Relay 2"
register_type: holding
address: 201
bitmask: 1
use_write_multiple: true
lambda: |-
if (x) {
return 1;
} else {
return 0;
}
sensor:
- platform: modbus_controller
modbus_controller_id: modbus_device
name: "Temperature Sensor"
register_type: holding
address: 202
value_type: U_WORD
unit_of_measurement: "°C"
accuracy_decimals: 1
filters:
- multiply: 0.1
### Клиент Home Assistnat / Сервер kincony_kc868_a16s ( Esp32) - прямая связь Modbus
#### В Home Assistant в конфигурации добавил файл с модбасом и сделал ссылку на файл
modbus: !include modbus.yaml
#### Вот код для Home Assistant
- name: kincony_kc868_a16s # Имя устройства в Home Assistant
type: serial # Тип подключения (последовательный порт)
method: rtu # Метод Modbus (RTU)
port: /dev/ttyUSB0 # Путь к устройству USB-RS485
baudrate: 9600 # Скорость передачи данных
stopbits: 1 # Количество стоп-битов
bytesize: 8 # Размер байта
parity: N # Отсутствие контроля четности
timeout: 5 # Таймаут ожидания данных
delay: 1 # Задержка между запросами
switches: # Конфигурация реле
- name: "Relay 1" # Имя реле 1
slave: 1 # Адрес устройства (slave)
address: 200 # Адрес регистра для управления реле 1
- name: "Relay 2" # Имя реле 2
slave: 1 # Адрес устройства (slave)
address: 201 # Адрес регистра для управления реле 2
sensors:
- address: 202 # Адрес регистра для температуры
input_type: holding # Тип регистра (holding register)
name: "Virtual Temperature" # Имя датчика температуры
device_class: temperature # Класс устройства (температура)
data_type: uint16 # Тип данных (16-битное целое число)
scale: 0.1 # Масштабирование данных (например, значение 250 будет интерпретировано как 25.0°C)
offset: 0 # Смещение данных (дополнительная коррекция значения)
precision: 1 # Точность (количество знаков после запятой)
slave: 1 # Адрес устройства (slave) на шине Modbus
#### Вот “ответный код” от сервера контроллера.
# Внешние компоненты, которые подключаются из репозитория GitHub
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master # Источник компонента
refresh: 60s # Частота обновления компонента
components:
- modbus_server # Используемый компонент (Modbus-сервер)
# Пример конфигурации для ESP32 с использованием I2C
i2c:
sda: 4 # Пин для данных (SDA)
scl: 5 # Пин для тактового сигнала (SCL)
scan: true # Автоматическое сканирование устройств на шине I2C
id: bus_a # Идентификатор шины I2C
# Включение Captive Portal (портала для настройки устройства через Wi-Fi)
captive_portal:
# Конфигурация для PCF8574 (расширитель портов)
pcf8574:
- id: 'pcf8574_hub_out_1' # Идентификатор для управления выходами 1-8
address: 0x24 # Адрес устройства на шине I2C
# Конфигурация реле (переключателей)
switch:
- platform: gpio # Использование GPIO для управления реле
name: "a16s-output1" # Имя реле 1
pin:
pcf8574: pcf8574_hub_out_1 # Использование PCF8574 для управления
number: 0 # Номер выхода на PCF8574
mode: OUTPUT # Режим работы (выход)
inverted: true # Инверсия сигнала (активный низкий уровень)
id: relay_1 # Идентификатор реле 1
- platform: gpio # Второе реле
name: "a16s-output2" # Имя реле 2
pin:
pcf8574: pcf8574_hub_out_1 # Использование PCF8574
number: 1 # Номер выхода на PCF8574
mode: OUTPUT # Режим работы
inverted: true # Инверсия сигнала
id: relay_2 # Идентификатор реле 2
# Конфигурация UART для Modbus
uart:
id: modbus_uart # Идентификатор UART
tx_pin: GPIO33 # Пин для передачи данных (TX) на RS485
rx_pin: GPIO32 # Пин для приема данных (RX) на RS485
baud_rate: 9600 # Скорость передачи данных
parity: NONE # Отсутствие контроля четности
stop_bits: 1 # Количество стоп-битов
debug:
direction: BOTH # Отладка в обоих направлениях (TX и RX)
dummy_receiver: false # Отключение фиктивного приемника
# Конфигурация Modbus-сервера
modbus_server:
id: modbuserver # Идентификатор Modbus-сервера
uart_id: modbus_uart # Использование настроенного UART
address: 1 # Адрес устройства (slave) на шине Modbus
holding_registers: # Регистры для хранения данных
- start_address: 200 # Регистр для управления реле 1 (a16s-output1)
number: 1 # Количество регистров
on_write: |- # Действие при записи в регистр
if (value) {
id(relay_1).turn_on(); # Включение реле 1
} else {
id(relay_1).turn_off(); # Выключение реле 1
}
return value;
on_read: |- # Действие при чтении регистра
return id(relay_1).state ? 0x0001 : 0x0000; # Возврат состояния реле 1
- start_address: 201 # Регистр для управления реле 2 (a16s-output2)
number: 1
on_write: |-
if (value) {
id(relay_2).turn_on(); # Включение реле 2
} else {
id(relay_2).turn_off(); # Выключение реле 2
}
return value;
on_read: |-
return id(relay_2).state ? 1 : 0; # Возврат состояния реле 2
- start_address: 202 # Регистр для чтения виртуальной температуры
number: 1
on_read: |-
return id(virtual_temp).state * 10; # Возврат значения температуры
# Конфигурация виртуального датчика температуры
sensor:
- platform: template # Использование шаблонного датчика
id: virtual_temp # Идентификатор датчика
name: "Virtual Temperature" # Имя датчика
unit_of_measurement: "C" # Единица измерения (градусы Цельсия)
accuracy_decimals: 1 # Количество знаков после запятой
update_interval: 5s # Интервал обновления данных
lambda: |- # Лямбда-функция для генерации данных
static float temp = 25.0; # Начальное значение температуры
temp += random(-5, 5
#### Если нужно подключить кнопку, вот код для контроллера.
# Бинарные сенсоры (входы)
binary_sensor:
- platform: gpio
name: "a16s-input1" # Имя первого входа
pin:
pcf8574: pcf8574_hub_in_1 # Использование PCF8574 для входа
number: 0 # Номер входа на PCF8574
mode: INPUT # Режим работы (вход)
inverted: true # Инверсия сигнала (активный низкий уровень)
id: input_1 # Идентификатор для использования в Modbus
- platform: gpio
name: "a16s-input2" # Имя второго входа
pin:
pcf8574: pcf8574_hub_in_1 # Использование PCF8574 для входа
number: 1 # Номер входа на PCF8574
mode: INPUT # Режим работы (вход)
inverted: true # Инверсия сигнала
id: input_2 # Идентификатор для использования в Modbus
#### Модбас настройки для кнопок
modbus_server:
id: modbuserver
uart_id: modbus_uart
address: 1 # адрес slave
holding_registers:
# Регистры для бинарных сенсоров
- start_address: 300 # Регистр для состояния входа 1 (a16s-input1)
number: 1
on_read: |-
return id(input_1).state ? 1 : 0;
- start_address: 301 # Регистр для состояния входа 2 (a16s-input2)
number: 1
on_read: |-
return id(input_2).state ? 1 : 0;
#### Для Home Assistant.
binary_sensors: # Конфигурация бинарных сенсоров
- name: "Input 1"
slave: 5 # Адрес slave
address: 300 # Адрес регистра для состояния входа 1
input_type: holding # Тип регистра (holding register)
device_class: door # Класс устройства (например, door, window, motion)
- name: "Input 2"
slave: 5
address: 301 # Адрес регистра для состояния входа 2
input_type: holding
device_class: window # Пример класса устройства
## Цепь Modbus RTU из 4х устройств.
#### Мой код для клиента Мастер Home Assistant
# Конфигурация для Home Assistant
- name: kincony_kc868_a16s # Имя устройства в Home Assistant
type: serial # Тип подключения (последовательный порт)
method: rtu # Метод Modbus (RTU)
port: /dev/ttyUSB0 # Путь к устройству USB-RS485
baudrate: 9600 # Скорость передачи данных
stopbits: 1 # Количество стоп-битов
bytesize: 8 # Размер байта
parity: N # Отсутствие контроля четности
timeout: 5 # Таймаут ожидания данных
delay: 1 # Задержка между запросами
switches: # Конфигурация реле
- name: "Relay 1" # Имя реле 1
slave: 5 # Адрес устройства (slave)
address: 200 # Адрес регистра для управления реле 1
- name: "Relay 2" # Имя реле 2
slave: 5 # Адрес устройства (slave)
address: 201 # Адрес регистра для управления реле 2
- name: "Relay 3" # Имя реле 2
slave: 2 # Адрес устройства (slave)
address: 0x0000 # Адрес регистра для управления реле Стандартное модбас-устройство (slave 1)
write_type: coil
- name: "Relay 4" # Имя реле 2
slave: 3 # Адрес устройства (slave)
address: 210 # Адрес регистра для управления реле На ESP32
sensors: # Конфигурация датчиков
- address: 202
input_type: holding
name: "Kincony Virtual Temperature"
data_type: int16 # Исправлено с uint16
scale: 0.1
slave: 5
- address: 220 # Новый регистр для ustroistvo-3
input_type: holding
name: "Ustroistvo-3 Virtual Temperature"
data_type: int16
scale: 0.1
slave: 3 # Адрес Slave 3
binary_sensors: # Конфигурация бинарных сенсоров
- name: "Input 1"
slave: 5 # Адрес slave
address: 300 # Адрес регистра для состояния входа 1
input_type: holding # Тип регистра (holding register)
device_class: door # Класс устройства (например, door, window, motion)
- name: "Input 2"
slave: 5
address: 301 # Адрес регистра для состояния входа 2
input_type: holding
device_class: window # Пример класса устройства
#### мой код для Сервер 1 это ESP32 с реле и виртуальным датчиком
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master
refresh: 60s
uart:
id: modbus_uart2
tx_pin: GPIO17
rx_pin: GPIO16
baud_rate: 9600
parity: NONE
stop_bits: 1
debug:
direction: BOTH
dummy_receiver: false
modbus_server:
id: modbuserver2
uart_id: modbus_uart2
address: 3 # Новый адрес для данного устройства (slave 2)
holding_registers:
- start_address: 210 # Регистр управления реле (можно выбрать любой свободный адрес)
number: 1
on_write: |-
if (value) {
id(relay_gpio4).turn_on();
} else {
id(relay_gpio4).turn_off();
}
return value;
on_read: |-
return id(relay_gpio4).state ? 1 : 0;
# В раздел modbus_server добавляем новый регистр:
- start_address: 220 # Новый регистр для температуры
number: 1
on_read: |-
return id(virtual_temp_ustroistvo3).state * 10;
# Управление реле напрямую через GPIO
switch:
- platform: gpio
name: "ESP32 Relay on GPIO4"
pin: GPIO4
id: relay_gpio4
# В раздел sensor добавляем:
sensor:
- platform: template
id: virtual_temp_ustroistvo3
name: "Virtual Temperature Ustroistvo-3"
unit_of_measurement: "°C"
accuracy_decimals: 1
update_interval: 5s
lambda: |-
return 25.0 + random(-5, 5) * 0.1;
#### Мой код Сервер 3 это kincony kc868 a16s со встроенным rs485 на котором настроено 2 реле и виртуальный датчик.
captive_portal:
external_components:
- source: github://epiclabs-io/esphome-modbus-server@master
refresh: 60s
components:
- modbus_server
i2c:
sda: 4
scl: 5
scan: true
id: bus_a
pcf8574:
- id: 'pcf8574_hub_out_1' # for output channel 1-8
address: 0x24
- id: 'pcf8574_hub_out_2' # для выходов 9-16
address: 0x25
- id: 'pcf8574_hub_in_1' # для входов 1-8
address: 0x22
- id: 'pcf8574_hub_in_2' # для входов 9-16
address: 0x23
switch:
- platform: gpio
name: "a16s-output1"
pin:
pcf8574: pcf8574_hub_out_1
number: 0
mode: OUTPUT
inverted: true
id: relay_1
- platform: gpio
name: "a16s-output2"
pin:
pcf8574: pcf8574_hub_out_1
number: 1
mode: OUTPUT
inverted: true
id: relay_2
binary_sensor:
- platform: gpio
name: "a16s-input1"
id: input_1
pin:
pcf8574: pcf8574_hub_in_1
number: 0
mode: INPUT
inverted: true
on_press:
- switch.toggle: relay_1
on_state:
then:
- lambda: |-
id(modbuserver).add_holding_register(300, x ? 1 : 0);
- platform: gpio
name: "a16s-input2"
id: input_2
pin:
pcf8574: pcf8574_hub_in_1
number: 1
mode: INPUT
inverted: true
on_press:
- switch.toggle: relay_2
on_state:
then:
- lambda: |-
id(modbuserver).add_holding_register(301, x ? 1 : 0);
uart:
id: modbus_uart
tx_pin: GPIO33 # RS485-TX на KinCony KC868-A16S
rx_pin: GPIO32 # RS485-RX на KinCony KC868-A16S
baud_rate: 9600
parity: NONE
stop_bits: 1
debug:
direction: BOTH
dummy_receiver: false
modbus_server:
id: modbuserver
uart_id: modbus_uart
address: 5 # адрес slave
holding_registers:
- start_address: 200 # Регистр для управления реле 1 (a16s-output1)
number: 1
on_write: |-
if (value) {
id(relay_1).turn_on();
} else {
id(relay_1).turn_off();
}
return value;
on_read: |-
return id(relay_1).state ? 0x0001 : 0x0000;
- start_address: 201 # Регистр для управления реле 2 (a16s-output2)
number: 1
on_write: |-
if (value) {
id(relay_2).turn_on();
} else {
id(relay_2).turn_off();
}
return value;
on_read: |-
return id(relay_2).state ? 1 : 0;
- start_address: 202 # Регистр для чтения температуры
number: 1
on_read: |-
return id(virtual_temp).state * 10;
- start_address: 300 # Регистр для состояния входа 1 (a16s-input1)
number: 1
on_read: |-
return id(input_1).state ? 1 : 0;
- start_address: 301 # Регистр для состояния входа 2 (a16s-input2)
number: 1
on_read: |-
return id(input_2).state ? 1 : 0;
sensor:
- platform: template
id: virtual_temp
name: "Virtual Temperature"
unit_of_measurement: "°C"
accuracy_decimals: 1
update_interval: 5s
lambda: |-
return 25.0 + random(-5, 5) * 0.1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment