Отчёт о технической проблеме: Рассинхронизация ответов Modbus RTU over TCP (Serial-over-TCP) на шлюзах WB-MGE v.3 и драйвере wb-mqtt-serial

**Дата составления:** 1 октября 2026 г.

* **Контроллер:** Wiren Board 8 (SN: `AIKH5OZJ`, Debian 11 Bullseye, ПО `wb-2606`, драйвер `wb-mqtt-serial 2.248.1-wb107`)

* **Шлюзы интерфейсов:** WB-MGE v.3 (ESP32-based, прошивка `1.1.2`, build `9c10fd2_main`)

* **Опрашиваемые модули:** Модули аналоговых входов `WB-MAI6` (FW `2.3.3`, Slave ID `223` и `179`)

* **Подключение шлюзов:** Wi-Fi 2.4 GHz (WLAN `Home665hid`, UniFi AP, уровень сигнала -51 dBm, RSSI 61)

* **Режим порта в `wb-mqtt-serial.conf`:** `“port_type”: “tcp”`, порт 502 (Serial over TCP / сырой Modbus RTU в TCP-потоке)

—

## 1. Суть проблемы и симптомы

При опросе аналоговых модулей ввода `WB-MAI6`, подключенных через шлюзы `WB-MGE v.3` в режиме `Serial over TCP` (`“port_type”: “tcp”`), в каналах измерения сопротивления 2-проводных термодатчиков теплого пола (Eberle PTC 1000 и NTC) спорадически возникают **катастрофические однократные аномальные выбросы значений**.

### Примеры зафиксированных аномалий:

1. **Канал сопротивления датчика теплого пола `IN 1 N Resistance` (норма ~994…1030 Ом, стяжка 22…31 °C):**

* Внезапное чтение: **`4411.93 Ом`**

* Внезапное чтение: **`3 270 932.42 Ом`** (3.27 МОм)

* Внезапные чтения: **`1 635 680 Ом`**, **`1 090 755 Ом`**

* Внезапные чтения: **`509.54 Ом`**, **`555.14 Ом`**, **`760.33 Ом`**, **`850.33 Ом`**

* Внезапные чтения: **`1049.18 Ом`**, **`1057.32 Ом`** (всплеск сопротивления на 50 Ом выше реального)

2. **Влияние на систему автоматизации и графики:**

* В Home Assistant температура этого датчика кратковременно падала до **$-55.5^\circ\text{C}$**, **$-46.6^\circ\text{C}$**, **$-11.5^\circ\text{C}$**, **$+1.9^\circ\text{C}$**, либо подскакивала до $+31.4^\circ\text{C}$.

* На графиках истории формируются гигантские «иглы», ложно срабатывала защита от перегрева, отключая нагрев зон.

3. **Характер сбоя:**

* Выброс длится **ровно 1 цикл опроса** (60 секунд), после чего следующее же чтение возвращается в абсолютно нормальное значение.

* На модуле `wb-mai6_166`, подключенном к WB8 **напрямую по кабелю RS-485** (`/dev/ttyRS485-2`), за всё время эксплуатации зафиксировано **0 аномалий**. Проблема возникает **исключительно на беспроводных шлюзах WB-MGE v.3**.

—

## 2. Диагностика и математическое доказательство (Root Cause Analysis)

Мы сопоставили тайминги системных логов `journalctl`, сырые записи базы SQLite `wb-mqtt-db` (`/var/lib/wirenboard/db/data.db`) и структуру карты регистров шаблона `config-wb-mai6-fw-2.2.json`.

В журнале `wb-mqtt-serial` ровно в секунды появления аномалий фиксируются сообщения:

```text

Sep 29 04:57:33 wb-mqtt-serial: WARNING: [modbus] failed to read 2 input(s) @ 1536 of device modbus:223: Serial protocol error: request timed out

Sep 29 04:57:55 wb-mqtt-serial: WARNING: [modbus] failed to read 2 input(s) @ 270 of device modbus:223: Serial protocol error: request timed out

```

### Побайтовая расшифровка аномальных чисел:

#### Факт №1: Аномалия `4411.93 Ом` — это чтение серийного номера модуля

* Значение в канале с масштабом 0.01: $4411.93 \times 100 = \mathbf{441193}$ (`0x0006BB69`).

* Серийный номер установленного модуля `WB-MAI6` (Slave ID 223) равен ровно **441193** (U32, регистр 270 `Serial`).

* Драйвер запросил сопротивление канала `IN 1 N Resistance`, но в сокет пришел задержанный ответ на предыдущий запрос серийного номера!

#### Факт №2: Аномалия `3 270 932.42 Ом` — это склейка напряжения 5V и температуры MCU

* Значение в сыром 32-битном регистре: $3\,270\,932.42 \times 100 = 327\,093\,242$ = **`0x137F0BFA`**.

* Разложим на два 16-битных слова:

* Старшее слово: `0x137F` = **4991** (десятичное). Ровно **4.991 В (4991 мВ)** из регистра `0x0600` (1536) — канал `5V Output`!

* Младшее слово: `0x0BFA` = **3066** (десятичное). Ровно **30.66 °C** из регистра `0x0601` (1537) — канал `Internal Temperature`!

#### Факт №3: Значения `1 635 680 Ом` и `1 090 755 Ом` — усреднение АЦП

* В конфигурации включен аппаратный оверсэмплинг АЦП (`in1_n_measurements: 5`). При наложении смещенных регистров усреднение части нормальных выборок (~1000 Ом) и склеенного регистра (3.27 МОм) давало числа $\approx 3.27\text{ МОм} / 2 = 1.635\text{ МОм}$ и $\approx 3.27\text{ МОм} / 3 = 1.09\text{ МОм}$.

#### Факт №4: Отрицательные пики $-55.5^\circ\text{C}$

* При сдвиге пакетов читалось паразитное сопротивление $509.54$ Ом (младшие слова регистров).

* Характеристика Eberle PTC 1000 пересчитывает $509.54$ Ом в $-53.0^\circ\text{C}$.

* С учётом офсета зоны $32.4$ (`offset: -2.5`): $-53.0 + (-2.5) = \mathbf{-55.5^\circ\text{C}}$.

—

## 3. Архитектурная причина в драйвере `wb-mqtt-serial`

1. **Отсутствие Transaction ID в Serial-over-TCP:**

В режиме `“port_type”: “tcp”` передаются «сырые» кадры Modbus RTU. В Modbus RTU нет заголовка MBAP и поля Transaction ID (в отличие от полноценного Modbus TCP).

2. **Преждевременный сброс ожидания:**

По умолчанию таймаут ответа в `wb-mqtt-serial` составляет всего **500 мс** (`response_timeout_ms`). Для Wi-Fi радиоканала (особенно 2.4 ГГц при наличии других клиентов и фонового сканирования) задержка пакета в 600–800 мс является штатной ситуацией.

3. **Отсутствие очистки буфера сокета (Socket Drain/Flush):**

Когда наступает таймаут 500 мс, драйвер:

* Логирует `request timed out`;

* **НЕ сбрасывает** буфер входящего TCP-сокета и не переподключает его;

* Сразу отправляет в этот же TCP-сокет следующий Modbus-запрос (например, для канала сопротивления).

4. **Смещение ответов на 1 шаг:**

Задержанный ответ на *предыдущий* запрос наконец приходит по TCP. Драйвер считывает его, видит корректный Slave ID (223), корректный Function Code (0x04) и валидный CRC. Драйвер считает этот пакет ответом на *текущий* запрос и помещает данные не в тот MQTT-топик.

—

## 4. Как проблема была временно купирована на объекте

Мы применили комплексное решение на уровне контроллера WB8:

1. **На уровне `wb-mqtt-serial.conf`:**

* Для всех TCP-портов шлюзов WB-MGE установлен увеличенный таймаут:

 \`\`\`json

 "response_timeout_ms": 2000

 \`\`\`

* В шаблонах опрашиваемых устройств отключен опрос неиспользуемых сервисных каналов (`5V Output`, `Internal Temperature`, `Serial`, `Uptime`), что сократило сетевой трафик Modbus RTU по Wi-Fi более чем на 70%.

2. **На уровне движка правил `wb-rules` (`floor_heating.js`):**

* **Resistance Gate:** допустимый диапазон Eberle PTC 1000 сужен с `300..3000` до **`850..1250 Ом`** (+2…+54 °C). Все паразитные мегаомы, серийные номера и низкие сопротивления мгновенно отбрасываются.

* **Plausible Temp Gate:** температура пола программно ограничена диапазоном **`10.0°C .. 42.0°C`**.

* **De-glitch filter:** скачки температуры более **`2.5°C`** за 1 цикл опроса блокируются (стяжка физически не может нагреться/остыть на 3 °C за 60 секунд). Для принятия скачка требуется подтверждение 3 цикла подряд.

—

## 5. Предложения для команды разработки Wiren Board

Для устранения проблемы в кодовой базе `wb-mqtt-serial` и прошивке `WB-MGE v.3` предлагаем рассмотреть следующие доработки:

1. **Очистка TCP-буфера при таймауте (Drain socket on timeout):**

Если при опросе по TCP произошел таймаут (`Serial protocol error: request timed out`), драйвер перед отправкой следующего PDU обязан полностью вычитать и сбросить весь хвост входящего буфера сокета с небольшой задержкой (guard interval), либо выполнить переподключение TCP-сессии (`TCP reset / reconnect`). Иначе задержанный ответ гарантированно «отравит» следующий регистр.

2. **Полноценный Modbus TCP для WB-MGE v.3:**

Реализовать и рекомендовать на шлюзах WB-MGE режим **Modbus TCP** (порт 502) с инкапсуляцией в заголовок MBAP (с инкрементом `Transaction ID`). В этом случае драйвер сопоставляет Transaction ID ответа с запросом и в принципе не может присвоить ответ другому каналу при любых сетевых задержках.

3. **Увеличение таймаута по умолчанию для TCP-портов:**

В схеме `wb-mqtt-serial-ports.schema.json` дефолтный `response_timeout_ms` для `tcpPort` и `modbusTcpPort` целесообразно поднять с 500 мс хотя бы до 1500–2000 мс.

4. **Оптимизация стандартных шаблонов:**

Регистры `Serial`, `Uptime`, `5V Output` в шаблонах аналоговых модулей не должны опрашиваться на высокой частоте наравне с быстрыми измерительными каналами — для них предпочтителен опрос раз в 10–60 минут либо по требованию.

WirenBoard_Support_WB_MGE_Modbus_TCP_Desync_Bug.md (12,5 КБ)

Здравствуйте! Я — автоматический помощник техподдержки Wiren Board.
Сейчас нерабочее время — я постараюсь помочь сразу, а в ближайший рабочий день
инженеры посмотрят тему и ответят.

Александр, спасибо за подробный отчёт. Совпадение ошибочного сопротивления с серийным номером и данными сервисных каналов — сильный аргумент в пользу принятия задержанного ответа за ответ на другой запрос. Однако без захвата обмена пока нельзя считать всю предложенную цепочку подтверждённой.

Есть три существенных уточнения:

  1. Полноценный Modbus TCP уже предусмотрен в WB-MGE v.3. В документации шлюза описаны отдельно режимы «Modbus TCP» и «Прозрачный мост». Стоит проверить наличие первого в интерфейсе вашей прошивки и испытать его на одном шлюзе, согласованно изменив режим на стороне контроллера. Сам номер порта 502 не определяет протокол. Transaction ID помогает сопоставлять сетевые запросы и ответы, но не даёт абсолютной гарантии против ошибок на участке RTU или внутри шлюза.

  2. Утверждение об отсутствии очистки требует проверки именно установленной версии. В текущем исходном коде есть SkipNoise() и его вызов при некорректном Modbus-ответе: обработка ошибок, реализация очистки. Это ещё не подтверждает корректную обработку таймаута в 2.248.1-wb107: очистка уже поступивших данных также не исключает прихода запоздалого ответа после неё. Указанные в README 500 мс — значение при отсутствии настройки, а не доказательство фактического таймаута ваших устройств.

  3. Объяснение промежуточных выбросов аппаратным усреднением АЦП пока не доказано. Если подмена происходит при разборе ответа в драйвере, аппаратное усреднение на MAI6 не усредняет ошибочно присвоенное сетевое значение. Фильтры полезны как временная мера, но отбрасывание скачков не должно скрывать настоящий перегрев: при длительной недостоверности измерения нужна безопасная остановка нагрева, а не бессрочное использование последней температуры.

Для следующего шага приложите, пожалуйста:

  • конфигурацию одного проблемного TCP-порта с устройством и переопределениями каналов, без секретов;
  • отладочный журнал драйвера и, если есть, захват TCP-обмена вокруг одного выброса;
  • результат наблюдения после увеличения таймаута: сколько времени прошло и остались ли выбросы либо таймауты.

Здесь может понадобиться мнение инженера Wiren Board. Для уверенного
ответа мне может не хватить контекста — если можете, приложите логи или
результаты тестов, я уточню гипотезы точнее.

Примечание: я — ИИ-ассистент, а не сотрудник-человек; в ответе возможны неточности. Если пришлёте уточняющие данные/логи, я смогу проверить гипотезы точнее.

Анализ замечаний техподдержки

1. О режиме «Modbus TCP» в WB-MGE v.3

• Замечание ТП: В шлюзе WB-MGE v.3 есть полноценный режим Modbus TCP, отличный от «Прозрачного моста». Рекомендуют проверить и испытать его.
• Результат проверки:
• Мы обратились к API шлюза 10.70.66.8 (3-MGE) и проверили конфигурацию:
“rs485_1”: {
“port_mode”: “tcp_bridge”,
“bridge”: {
“mode”: “server”,
“port”: 502,
“modbus”: false
}
}
Параметр “modbus” действительно стоял в значении false (Прозрачный мост / Serial-over-TCP).
• Если выставить “modbus”: true, шлюз переходит в режим конвертера Modbus TCP ↔ Modbus RTU.
• На стороне контроллера WB8 в wb-mqtt-serial.conf этому соответствует:
“port_type”: “modbus tcp”,
“connected_to_mge”: true

  • В этом режиме каждый сетевой пакет упаковывается в заголовок MBAP с уникальным Transaction ID, генерируемым контроллером. Шлюз транслирует его в ответе. При задержке пакета в Wi-Fi драйвер wb-mqtt-serial сопоставляет Transaction ID и гарантированно отбрасывает запоздавший ответ, физически исключая подмену регистров.
  • Готовность к тесту: Мы можем согласованно переключить порт 1 шлюза 10.**.**.** в "modbus": true, а в wb-mqtt-serial.conf задать "port_type": "modbus tcp".

──────

2. Об очистке буфера и функции SkipNoise()

• Замечание ТП: В коде драйвера есть SkipNoise(), но очистка данных не исключает прихода запоздалого ответа после неё.
• Результат проверки:
• SkipNoise() вызывается драйвером, когда в сокет поступили некорректные данные (мусор, битый CRC, не Modbus-пакет).
• Но в случае таймаута на момент истечения response_timeout_ms в сокете вообще нет байтов — пакет ещё физически задерживается в радиоканале Wi-Fi. Драйвер считает запрос просроченным и посылает следующий запрос.
• Задержанный ответ прилетает в системный сокет TCP уже после того, как драйвер сдался по таймауту.
• Когда драйвер выполняет read() для ожидания ответа на новый запрос, он немедленно считывает из сокета этот старый ответ.
• А так как старый ответ — это абсолютно валидный Modbus RTU кадр с совпадающим Slave ID (223), функцией (0x04) и корректным CRC, драйвер не считает его шумом, SkipNoise() не вызывается, и драйвер добросовестно записывает эти байты не в тот канал. Сам бот WB подтвердил эту уязвимость прозрачного моста.

──────

3. Происхождение промежуточных значений (1.63M, 1.09M) — подтверждение ошибки гипотезы АЦП

• Замечание ТП: Аппаратное усреднение АЦП модуля MAI6 не может усреднять сетевое значение, подмененное в драйвере.
• Результат проверки: Бот абсолютно прав — АЦП модуля здесь ни при чем!
Мы нашли точный источник этих промежуточных чисел в SQLite-базе /var/lib/wirenboard/db/data.db сервиса истории wb-mqtt-db:
(uid=738119, channel=309, min=‘1029.81’, max=‘3270932.42’, value=‘1635981.115’)

  • Драйвер wb-mqtt-serial опубликовал в MQTT ровно чистое значение 3 270 932.42 (зафиксировано в колонке max).
  • А сервис истории wb-mqtt-db при сохранении за интервал времени посчитал среднее арифметическое между нормальным значением (1029.81 Ом) и единичным всплеском (3.27 МОм):


1029.81 + 3 270 932.42
────────────────────── = 𝟏 𝟔𝟑𝟓 𝟗𝟖𝟏.𝟏𝟏𝟓 Ом!
          2

• Таким образом, 1.635 МОм — это исключительно артефакт агрегации базы данных wb-mqtt-db.
──────

4. Безопасность и защита от скрытого перегрева (Fail-Safe)

• Замечание ТП: Фильтры не должны скрывать настоящий перегрев: при длительной недостоверности измерения нужна безопасная остановка нагрева, а не бессрочное использование последней температуры.
• Что мы сразу исправили в /etc/wb-rules/floor_heating.js:

  1. Мгновенный аварийный сброс нагрева (Instant Overheat Cutoff):
    Если замер ≥ 36.0°C (MAX_SAFE_TEMP), физическое реле нагрева на шине KNX (0/3/150 и др.) выключается мгновенно на 1-м же цикле, без каких-либо задержек и ожидания деглитчера! Безопасность имеет абсолютный приоритет над фильтрацией графиков.
  2. Сокращение таймаута сторожевого таймера (Watchdog):
    Таймаут SENSOR_TIMEOUT_SEC сокращен с 1 часа до 10 минут (600 секунд).

Александр, спасибо за уточнения. Следующий обоснованный шаг — сравнительный тест одного шлюза в режиме Modbus TCP. При этом несколько выводов стоит сформулировать точнее.

  1. Transaction ID действительно проверяется драйвером. В текущей реализации Modbus TCP ответ с несовпадающим Transaction ID пропускается, после чего драйвер продолжает ожидать нужный ответ в пределах таймаута. Это защищает от принятия запоздавшего TCP-ответа с другим ID, но не исключает подмену на участке RTU, если шлюз сам присвоит задержанным данным ID нового запроса. Проверка текущего кода также не подтверждает автоматически поведение установленной 2.248.1-wb107.

  2. Объяснение промежуточного значения агрегацией теперь хорошо обосновано. Приведённая запись соответствует среднему между нормальным значением и выбросом; в документации wb-mqtt-db value описано как среднее за интервал. Однако запись max сама по себе не устанавливает количество исходных MQTT-публикаций. И поправлю формулировку «бот подтвердил уязвимость»: ранее я подтвердил правдоподобность механизма, не установленную причину конкретного сбоя.

  3. В защите важен источник обновления watchdog. Его следует обновлять только по принятому достоверному измерению, а не по любому входящему сообщению: иначе постоянный поток отброшенных значений не даст сработать остановке. Проверка перегрева должна выполняться до деглитчера и удержания последнего значения. Достаточность 600 секунд без оценки системы отопления подтвердить нельзя.

Для теста переключите один шлюз и соответствующий порт драйвера согласованно, сохранив остальные настройки, включая таймаут 2000 мс. Зафиксируйте длительность наблюдения, число таймаутов и наличие выбросов; при повторении приложите журнал и захват обмена вокруг события. Исчезновение выбросов будет полезным результатом, но само по себе ещё не докажет всю цепочку причины.

Примечание: я — ИИ-ассистент, а не сотрудник-человек; в ответе возможны неточности. Если пришлёте уточняющие данные/логи, я смогу проверить гипотезы точнее.

Добрый день.

А какие “задержки ответа” канала связи предусмотрены проектом?
И в какое значение выставлен “response_timeout_ms”?

Добрый день, Александр.

Да, настройки правильные, можно переключать. Галочка «К этому порту подключен шлюз WB-MGE v.3» нужна, чтобы продолжил работать Быстрый Modbus. Устройства на порту перенастраивать не нужно.

Вы правы, мы проверили это по коду драйвера. Если ответ приходит после истечения ожидания, драйвер его не выбрасывает и может принять за ответ на следующий запрос. В режиме прозрачного моста такой ответ выглядит так же, как нужный. В режиме Modbus TCP у каждого ответа есть номер запроса, и опоздавший в сети ответ драйвер отбросит. Поэтому для WB-MGE v.3 на Wi-Fi этот режим надёжнее.

Увеличенный таймаут снижает вероятность сбоя, но полностью его не убирает. Ваше предложение очищать буфер после таймаута передали разработчикам.

Напишите, пожалуйста, как пройдёт тест.

Это никак не поможет.
Так как:

  • запрос отправлен - ответа в период до таймаута нет.
  • таймаут, (тут предлагаемая очистка чистого буфера)
  • отправка нового запроса
  • получение ответа на предыдущий запрос.

То есть ключевое: если среда используемая для связи допускает большие задержки то таймаут должен выставляться в значение гарантированно большее максимальной расчетно задержки.
Ну или, как верно пишет коллега - нужно использовать TCP-based протоколы, тот же Modbus TCP.

Добрый день!

Большое спасибо команде Wiren Board (@Understarfall, @BrainRoot) за проверку исходного кода драйвера и конструктивный диалог! Очень ценно, что ситуация с десинхронизацией подтверждена и предложение по очистке буфера передано разработчикам.

Делюсь ответами на вопросы и результатами проведенного тестирования.

  1. Задержки каналов связи в проекте:
    • В системе на объекте МФ70 работают два шлюза WB-MGE v.3:
  • 2-MGE (10.0.0.7, 2-й этаж): подключен по кабелю Ethernet (LAN). RTT в сети стабильный: 20–40 мс.
  • 3-MGE (10.0.0.8, 3-й этаж): подключен по радиоканалу Wi-Fi 2.4 ГГц к ближайшей точке доступа UniFi. Базовый пинг составляет 50–140 мс, но при эфирном джиттере и повторных передачах кадров 802.11 RTT в пиках доходит до 400–600 мс.
    • На стороне шины RS-485 опрос модуля WB-MAI6 идет на скорости 9600 бод. Время транзакции на шине составляет порядка 60–80 мс.
    • В результате суммарное время кругового опроса (контроллер → Wi-Fi → WB-MGE → опрос MAI6 по RS-485 → ответ шлюза → Wi-Fi → контроллер) на 3-м этаже в моменты радиовсплесков периодически превышало 500 мс.
  1. Значение response_timeout_ms:
    • До начала диагностики данный параметр в конфигурации /mnt/data/etc/wb-mqtt-serial.conf не был задан вовсе, то есть драйвер использовал дефолтное значение 500 мс.
    • При превышении 500 мс драйвер закрывал транзакцию по таймауту и отправлял следующий запрос, а задержанный ответ из Wi-Fi сокета попадал в буфер уже во время ожидания нового ответа.
    • В рамках тестов на обоих сетевых портах таймаут увеличен до 2000 мс.

Результаты тестирования

Мы переключили режимы и запустили сравнительный тест:

  1. Шлюз 3-MGE (10.0.0.8, 3-й этаж, Wi-Fi 2.4 ГГц):
    • В веб-интерфейсе шлюза включили режим Modbus TCP: rs485_1.bridge.modbus = true.
    • В wb-mqtt-serial.conf на контроллере WB8 настроили порт:
"port_type": "modbus tcp",
"connected_to_mge": true,
"response_timeout_ms": 2000

• Отключили высокочастотный опрос вспомогательных регистров модуля (серийный номер, uptime, напряжение), оставив только рабочие каналы сопротивления датчиков.
• Результат: Драйвер мгновенно подключился (device modbus-tcp:223 is connected). Опрос идет непрерывно, Быстрый Modbus работает штатно, 0 сбоев, 0 таймаутов, 0 сдвигов регистров. Температура отображается идеально ровно, никаких скачков до -55 °C или мегаомов больше нет!

  1. Шлюз 2-MGE (10.0.0.7, 2-й этаж, проводной Ethernet):
    • Оставлен в режиме Serial over TCP (“port_type”: “tcp”) с response_timeout_ms: 2000 в качестве контрольной группы.
    • Результат: На стабильном проводном канале при таймауте 2000 мс задержки в лимит укладываются с запасом, сбоев также не зафиксировано.

Уточнения по замечаниям @Weekend_Engineer_bot

  1. Промежуточные значения сопротивления (1.63 МОм):
    • Проверка SQLite базы /var/lib/wirenboard/db/data.db полностью подтвердила замечание бота: 1.63 МОм возникло из-за усреднения в сервисе wb-mqtt-db (среднее между нормальным 1029 Ом и пиковым 3 270 932 Ом за интервал записи). Драйвер отправлял в MQTT «чистый» серийный номер 3 270 932, АЦП модуля здесь ни при чем.

  2. Логика безопасности в правилах wb-rules:
    • Реализовали рекомендации бота:

  • Мгновенная аварийная отсечка при T >= 36 °C: отключает нагрев немедленно, в обход любых сглаживающих фильтров и деглитчеров.
  • Watchdog датчиков (600 с): таймер обновляется только по принятым валидным измерениям. При отсутствии достоверных данных 10 минут нагрев зоны принудительно гасится.

Итог: Перевод Wi-Fi шлюза в нативный режим Modbus TCP с флагом connected_to_mge: true и увеличение таймаута до 2000 мс полностью устранили проблему рассинхронизации опроса!

Александр, спасибо за результаты теста.

Для шлюза на Wi-Fi рекомендуем оставить режим Modbus TCP. Выбросы раньше появлялись эпизодически, поэтому понаблюдайте ещё несколько суток. Если таймаутов и выбросов не будет, проблему можно считать решённой.

Уточню по очистке буфера. Как верно заметил коллега, сама по себе очистка после таймаута не помогла бы, потому что опоздавший ответ приходит уже после неё. От подмены защищают таймаут с запасом над задержкой канала или режим Modbus TCP, что и показал ваш тест.

Если возникнут вопросы, обращайтесь.