Датчики температуры в теплице на Modbus RTU

Беспроводные датчики соблазнительны, пока не выясняется, что в теплице из стекла и мокрого металла они теряют связь, а батарейки садятся в мороз. Проводная шина RS-485 с протоколом Modbus RTU скучнее, зато работает годами. Рассказываем, как мы её строим.

Почему RS-485 и Modbus

  • Расстояние. На скорости 9600 бит/с шина надёжно работает на длине до 1200 м — хватает на ряд теплиц.
  • Помехоустойчивость. Дифференциальная пара «A/B» мало чувствительна к наводкам от насосов и вентиляторов.
  • Простота. Одна витая пара плюс общий провод, до 32 стандартных устройств на сегмент (до 256 с приёмопередатчиками на 1/8 нагрузки).
  • Выбор датчиков. Датчики температуры и влажности воздуха, почвы, CO₂, освещённости с Modbus RTU выпускают десятки производителей, они взаимозаменяемы.

Modbus RTU — протокол «вопрос — ответ»: ведущее устройство (у нас это маленький Linux-сервер или контроллер) по очереди спрашивает каждый датчик по адресу, датчик отвечает значениями регистров. Сами датчики никогда не говорят первыми, поэтому коллизий на шине нет.

Топология: линия, а не звезда

Главное правило, которое нарушают чаще всего: RS-485 — это линия. Кабель идёт от ведущего устройства к первому датчику, от него ко второму и так далее. Отводы от магистрали к датчику — как можно короче, в пределах полуметра. «Звезда» из щита, где к каждому датчику идёт свой кабель, на 9600 бит/с иногда работает, но начинает сбоить при добавлении устройств и в сырую погоду.

Linux-сервер USB–RS-485 с подтяжкой 120 Ом A B GND (общий) адрес 1 адрес 2 адрес 3 120 Ом отводы к датчикам — не длиннее 0,5 м
Правильная шина: линия, терминаторы только на двух крайних точках, общий провод протянут до каждого датчика
  • Терминаторы — резисторы 120 Ом между A и B, ровно два: на самом первом и самом последнем устройстве линии. У многих датчиков и адаптеров они включаются перемычкой; на промежуточных устройствах перемычки должны быть сняты.
  • Подтяжка (bias) — резисторы, которые держат линию в определённом состоянии, когда никто не передаёт. Обычно они уже есть в адаптере ведущего устройства; если нет, без них шина «ловит» мусор между запросами.
  • Общий провод. «RS-485 — это два провода» — миф. Без общего провода разница потенциалов между землёй щита и землёй дальнего датчика легко выходит за допустимый диапазон, особенно рядом с насосами.
  • Кабель — экранированная витая пара для интерфейсов (например, 2×2×0,5: одна пара на A/B, вторая на питание и общий). Экран заземляется в одной точке, у щита.

Монтаж в теплице

Теплица — агрессивная среда: влажность под 100 %, конденсат, удобрения, перепады от −25 °C зимней ночью до +45 °C летним днём. Что мы делаем всегда:

  • Датчик воздуха — в радиационном экране (жалюзийный колпак), иначе солнце покажет вам +38 °C при реальных +27.
  • Высота — на уровне растений, а не у конька крыши; не ближе метра от труб отопления и форсунок туманообразования.
  • Соединения — только в распаечных коробках IP65 с кабельными вводами; кабель заводится снизу с петлёй, чтобы вода стекала мимо ввода.
  • Каждая теплица — отдельный сегмент шины через изолированный повторитель. Наводка или короткое замыкание в одной теплице не кладёт остальные.
  • На кабелях между зданиями — защита от импульсных перенапряжений с обеих сторон.
  • Раз в сезон — сверка с поверенным эталонным термометром. Ёмкостные датчики влажности в теплицах «плывут» на 2–5 % в год.

Адреса и регистры

У каждого устройства на шине — уникальный адрес от 1 до 247. Новые датчики почти всегда приходят с адресом 1, поэтому адреса назначаем на столе, подключая датчики по одному. Как именно меняется адрес, описано в документации датчика: обычно это запись в специальный регистр. Наклейку с адресом — на корпус и в схему.

Данные датчика лежат в регистрах — 16-битных ячейках. Типичная карта регистров недорогого датчика температуры и влажности выглядит так:

РегистрФункцияЗначениеПример
0x000003, чтениеВлажность × 10, %712 → 71,2 %
0x000103, чтениеТемпература × 10, °C, со знаком65 486 → −5,0 °C
0x07D006, записьАдрес устройства1…247
0x07D106, записьСкорость: 0 — 2400, 1 — 4800, 2 — 96002

Обратите внимание на температуру: отрицательные значения передаются в дополнительном коде, и «сырое» 65 486 означает 65 486 − 65 536 = −50, то есть −5,0 °C. Если опрашивающая программа читает регистр как беззнаковый, первым морозом вы получите +6548,6 °C и ложную тревогу о пожаре.

Адреса регистров и коды функций у разных производителей разные; одни используют функцию 03 (holding registers), другие — 04 (input registers). Таблица выше — пример, а не стандарт. Всегда сверяйтесь с паспортом датчика.

Опрос с Linux

Для проверки на месте удобна консольная утилита mbpoll. Прочитать два регистра начиная с первого у датчика с адресом 3:

mbpoll -m rtu -b 9600 -P none -d 8 -s 1 \
       -a 3 -t 4 -r 1 -c 2 -1 /dev/ttyUSB0
# -t 4 — holding registers (функция 03), -r 1 — нумерация с единицы,
# -c 2 — два регистра, -1 — один опрос и выход

-- Polling slave 3...
[1]:    712
[2]:    246

Для постоянного опроса мы пишем небольшую службу на Python с библиотекой minimalmodbus. Она опрашивает все датчики раз в 15 секунд и складывает результат в файл для textfile-коллектора node_exporter — дальше значения попадают в Prometheus, графики и тревоги.

#!/usr/bin/env python3
# /opt/greenhouse/poll.py
import os, time, minimalmodbus

PORT = "/dev/ttyUSB0"
OUT = "/var/lib/node_exporter/textfile/greenhouse.prom"
SENSORS = {1: "t1_west", 2: "t1_center", 3: "t1_east", 4: "t2_west"}

def make(addr):
    s = minimalmodbus.Instrument(PORT, addr)
    s.serial.baudrate = 9600
    s.serial.timeout = 0.3
    s.clear_buffers_before_each_transaction = True
    return s

devices = {addr: make(addr) for addr in SENSORS}

while True:
    lines = []
    for addr, name in SENSORS.items():
        dev = devices[addr]
        for attempt in range(3):
            try:
                hum = dev.read_register(0, 1, functioncode=3)
                temp = dev.read_register(1, 1, functioncode=3, signed=True)
                lines.append(f'greenhouse_temp_c{{sensor="{name}"}} {temp}')
                lines.append(f'greenhouse_humidity{{sensor="{name}"}} {hum}')
                lines.append(f'greenhouse_up{{sensor="{name}"}} 1')
                break
            except (IOError, ValueError):
                time.sleep(0.1)
        else:
            lines.append(f'greenhouse_up{{sensor="{name}"}} 0')
    tmp = OUT + ".tmp"
    with open(tmp, "w") as f:
        f.write("\n".join(lines) + "\n")
    os.replace(tmp, OUT)  # атомарная замена: коллектор не прочитает половину файла
    time.sleep(15)

Обратите внимание на метрику greenhouse_up. Самая опасная ситуация — не «температура упала», а «датчик молчит, а на графике застыло последнее нормальное значение». Тревога на отсутствие данных так же важна, как тревога на мороз.

Сколько датчиков успеет опросить одна шина? На 9600 бит/с один байт передаётся около 1,15 мс. Запрос двух регистров — 8 байт, ответ — 9, плюс паузы и время реакции датчика: на практике 40–60 мс на устройство. Значит, 40 датчиков опрашиваются за 2–3 секунды — с огромным запасом для теплицы, где температура меняется за минуты.

Типичные неисправности

СимптомЧастая причинаЧто проверить
Датчик не отвечает совсемНе тот адрес или скорость; перепутаны A и BПодключить один датчик коротким кабелем; поменять A/B местами — обозначения у производителей не совпадают
Часть ответов с ошибкой CRCНет терминаторов или их больше двух; нет общего проводаСопротивление между A и B на обесточенной шине: должно быть около 60 Ом
Сбои ночью или в дождьКонденсат в распаечной коробке; кабель идёт рядом с силовымВскрыть коробки на линии, проверить вводы; развести кабели минимум на 20 см
Сбои при включении насосовНаводки от частотного преобразователяЗаземление экрана в одной точке; изолированный повторитель на сегмент
Скачущие, «чужие» значенияДва устройства с одним адресомОтключать датчики по одному и смотреть, какой пропадает

Если хочется посмотреть, как это работает в реальном хозяйстве, — в кейсе про тепличное хозяйство в Зеленодольском районе описано, как такая шина трижды за зиму предупредила об остановке котла.