Arduino и протокол Modbus
Подробное руководство по протоколу Modbus: принципы работы, структура фреймов, совместимые платы Arduino и пример чтения данных со счётчика энергии через RS-485.
Введение
Эта статья посвящена протоколу последовательной связи Modbus и его использованию с платами Arduino. Мы разберём ключевые элементы протокола, покажем совместимые библиотеки и платы, а также рассмотрим практический пример работы с реальным устройством. Поскольку набор библиотек зависит от конкретного железа, всегда сверяйтесь со спецификацией вашей платы.
Совместимое оборудование
- Все платы, совместимые с MKR 485 Shield и MKR ETH Shield
- Все платы, совместимые с Ethernet Shield Rev2
- Portenta Machine Control
- Portenta Max Carrier
- Opta™
Библиотеки
Терминология: понятия «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 COIL02— READ DISCRETE INPUT03— READ HOLDING REGISTERS04— READ INPUT REGISTERS05— WRITE SINGLE COIL06— WRITE SINGLE REGISTER15— WRITE MULTIPLE COILS16— 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 Address | Function Code | Starting Register | Register Count |
|---|---|---|---|
| 0x21 | INPUT REGISTERS | 30107 | 2 |
Важно: этот формат запроса специфичен для 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