Главная chevron_right Уроки chevron_right Связь chevron_right Arduino и протокол Modbus

Arduino и протокол Modbus

Подробное руководство по протоколу Modbus: принципы работы, структура фреймов, совместимые платы Arduino и пример чтения данных со счётчика энергии через RS-485.

Введение

Эта статья посвящена протоколу последовательной связи Modbus и его использованию с платами Arduino. Мы разберём ключевые элементы протокола, покажем совместимые библиотеки и платы, а также рассмотрим практический пример работы с реальным устройством. Поскольку набор библиотек зависит от конкретного железа, всегда сверяйтесь со спецификацией вашей платы.

Совместимое оборудование

Библиотеки

Терминология: понятия «master» и «slave» в документации Arduino заменены на «controller» (контроллер) и «peripheral» (периферийное устройство). Именно эти термины используются в статье.

Что такое Modbus?

Modbus — открытый протокол последовательной передачи данных, разработанный компанией Modicon (сейчас Schneider Electric) в 1979 году. Сегодня это старейший и самый распространённый протокол в промышленной автоматизации. Он позволяет счётчикам энергии, датчикам влажности и другим устройствам в одной сети передавать данные на управляющий компьютер или ПЛК.

Существует несколько версий протокола: Modbus RTU, Modbus ASCII, Modbus TCP и Modbus Plus. В основе лежит архитектура контроллер–периферия: только контроллер инициирует обмен, отправляя запросы, а периферийные устройства отвечают на них.

Физически Modbus работает поверх RS-232, RS-485 или Ethernet. Исторически первой была реализация на RS-232, однако большинство современных систем используют RS-485: он обеспечивает большие расстояния, более высокие скорости и возможность подключения нескольких устройств к одной шине. Связь по RS-485 организована по двухпроводной схеме (передача и приём).

Если в сети нужно совместить Modbus с другими протоколами, выбирайте Modbus TCP поверх Ethernet — тогда разные протоколы смогут сосуществовать на одном физическом интерфейсе.

Схема протокола Modbus RTU: контроллер отправляет запросы на чтение/запись периферийным устройствам и получает ответы.

Как работает Modbus?

Каждое сообщение Modbus состоит из четырёх элементов, расположенных в строго определённом порядке. Именно фиксированная структура делает разбор сообщений простым и надёжным. Разговор всегда начинает контроллер: он отправляет запрос, периферийное устройство его интерпретирует и отвечает.

Modbus оперирует функциями чтения и записи, которые работают с внутренними регистрами памяти периферийного устройства. Регистровая карта конкретного устройства — главный документ, с которого нужно начинать работу: она описывает, где хранятся входные и выходные данные.

Структура фрейма запроса:

Структура фрейма запроса Modbus: адрес устройства, код функции, данные, проверка ошибок CRC.

Адрес устройства (Device Address)

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

Код функции (Function Code)

Код функции говорит периферийному устройству, что именно нужно сделать: прочитать или записать данные. Многие типы данных получили названия от управления реле — например, одиночный битовый выход называется «coil» (катушка), а одиночный битовый вход — «discrete input» (дискретный вход). Библиотека Arduino Modbus поддерживает следующие функции:

  • 01 — READ COIL
  • 02 — READ DISCRETE INPUT
  • 03 — READ HOLDING REGISTERS
  • 04 — READ INPUT REGISTERS
  • 05 — WRITE SINGLE COIL
  • 06 — WRITE SINGLE REGISTER
  • 15 — WRITE MULTIPLE COILS
  • 16 — WRITE MULTIPLE REGISTERS

Данные (Data)

Поле данных содержит запрашиваемую или передаваемую информацию. При использовании библиотеки Arduino Modbus здесь указываются начальный регистр и количество регистров для чтения или записи.

Проверка ошибок CRC (CRC Error Check)

CRC (Cyclic Redundancy Check) — алгоритм обнаружения ошибок, основанный на cyclic codes. Он прост в аппаратной реализации и хорошо выявляет ошибки, возникающие из-за помех в канале передачи.

В конец каждого сообщения Modbus добавляются два байта CRC, вычисленные по всему содержимому сообщения. Принимающее устройство пересчитывает CRC самостоятельно и сравнивает с полученным значением. Если хотя бы один бит искажён — значения не совпадут, и ошибка будет обнаружена.

Хорошая новость: при работе с библиотекой Arduino Modbus вычисление и проверка CRC выполняются автоматически — вам не нужно заниматься этим вручную.

Порядок байт и слов (Byte and word ordering)

Спецификация Modbus не регламентирует порядок хранения данных в регистрах. Одни производители передают старший байт первым (big-endian), другие — младший (little-endian). Аналогично для 32-битных значений: одни устройства помещают старшее слово в первый регистр, другие — в последний. Главное — знать, какой порядок использует конкретное устройство, и настроить приём соответственно. (Schneider Electric, 2022)

Как использовать Modbus с Arduino

Теперь, разобравшись с теорией, перейдём к практике. Arduino может выступать как контроллером, так и периферийным устройством — в зависимости от задачи.

Для работы с Modbus используйте Arduino Modbus library: она поддерживает два транспорта — последовательный обмен по RS485 в режиме RTU и сетевой обмен по TCP через Ethernet или Wi-Fi. Поскольку библиотека Modbus зависит от RS-485 library, обе библиотеки нужно подключить в коде.

Большинство плат Arduino совместимы с Modbus, особенно если речь идёт о TCP-варианте. Для работы через RS-485 используйте MKR 485 Shields — он превращает любую плату серии MKR в полноценный узел Modbus RTU. Подробнее об обмене данными между двумя MKR 485 Shields читайте в this tutorial.

При использовании Modbus library отправка запроса выглядит просто. Вот типичный формат функции запроса:

Device AddressFunction CodeStarting RegisterRegister Count
0x21INPUT REGISTERS301072

Важно: этот формат запроса специфичен для Modbus library и работает только с совместимыми платами. Проверяйте спецификацию вашей платы перед использованием!

Адрес устройства, код функции, начальный регистр и количество регистров берутся из документации на конкретное устройство. CRC библиотека рассчитывает самостоятельно.

Практический пример

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

Откроем документацию на устройство и найдём описание фрейма запроса:

Пример фрейма запроса Modbus для устройства Finder: адрес ведомого, код функции, начальный регистр, количество регистров, CRC.

Разберём каждое поле:

  • Адрес устройства0x21, то есть 33 в десятичной системе.
  • Код функции04, что соответствует INPUT REGISTERS (see Function Code).
  • Начальный регистр0x6B в шестнадцатеричном виде. Для библиотеки Modbus нужно перевести в десятичное число.

Если пролистать документацию дальше, регистры указаны в десятичном формате:

Строка таблицы регистров Modbus устройства Finder: тип 4, регистры 30107 и 30108, параметр U1.

Видно, что напряжение U1 занимает регистры 30107 и 30108 — значит, количество регистров равно 2.

Функция readVoltage(), вызванная с найденными параметрами, считает значение напряжения со счётчика Finder. Внутри функции можно увидеть вызов requestFrom() с четырьмя аргументами: Device Address, Function Code, Starting Register и Register Count.

/**
  Предназначена для чтения информации о напряжении со счётчика электроэнергии Finder 7M.24, совместимого с протоколом Modbus.

  @param none
  @return Возвращает полученное значение напряжения.
*/
float readVoltage() {
  float volt = 0.;
  // Отправка запроса на чтение по RS485
  if (!ModbusRTUClient.requestFrom(0x21, INPUT_REGISTERS, 30107, 2)) {
    // Обработка ошибок
    Serial.print("- Failed to read the voltage! ");
    Serial.println(ModbusRTUClient.lastError());
  } else {
    // Обработчик ответа
    uint16_t word1 = ModbusRTUClient.read();  // Чтение word1 из буфера
    uint16_t word2 = ModbusRTUClient.read();  // Чтение word2 из буфера
    uint32_t millivolt = word1 << 16 | word2; // Объединение word1 и word2 для получения значения напряжения в милливольтах
    volt = millivolt/1000.0;                  // Перевод в вольты
  }

  return volt;
}

Типичные ошибки и советы по отладке

  • Неверный адрес устройства. Убедитесь, что адрес в коде совпадает с адресом, установленным на самом устройстве (часто настраивается через DIP-переключатели или меню).
  • Путаница с системой счисления. Регистры в документации могут быть указаны в шестнадцатеричном (0x6B) или десятичном (107) формате — всегда переводите к единому виду перед использованием в коде.
  • Неправильный порядок байт. Если полученное значение явно некорректно, попробуйте поменять порядок байт при сборке 32-битного значения из двух регистров.
  • Отсутствие подтягивающих резисторов на шине RS-485. При нестабильной связи проверьте наличие терминирующих резисторов 120 Ом на концах шины.
  • Скорость и параметры порта. Скорость (baud rate), количество бит данных, чётность и стоп-биты должны совпадать у всех устройств на шине.

Как проверить результат

Если вы не уверены в корректности запроса, воспользуйтесь программой-анализатором Modbus (например, Modbus Poll) и USB–RS-485 адаптером, чтобы отправить тот же запрос с компьютера и сравнить ответы. Это поможет быстро определить, проблема в коде Arduino или в настройках устройства.

---

Эта статья охватывает основы Modbus RTU. Для изучения других вариантов реализации протокола воспользуйтесь ссылками ниже.

Читать далее

  • https://www.se.com/us/en/faqs/FA168406/
  • https://modbus.org/
  • https://www.csimn.com/CSI_pages/Modbus101.html
  • https://en.wikipedia.org/wiki/Modbus
  • https://docs.arduino.cc/tutorials/mkr-485-shield/mkr-485-communication