Из данной статьи вы узнаете о протоколе Modbus RTU, который широко применяется в АСУ ТП. Англоязычная версия статьи доступна на сайте ipc2u.com. Описание протокола Modbus TCP можно найти в статье.
Оглавление:
- Описание протокола Modbus RTU
- Какие бывают команды Modbus RTU?
- Как послать команду Modbus RTU на чтение дискретного вывода? Команда 0x01
- Как послать команду Modbus RTU на чтение дискретного ввода? Команда 0x02
- Как послать команду Modbus RTU на чтение аналогового вывода? Команда 0x03
- Как послать команду Modbus RTU на чтение аналогового ввода? Команда 0x04
- Как послать команду Modbus RTU на запись дискретного вывода? Команда 0x05
- Как послать команду Modbus RTU на запись аналогового вывода? Команда 0x06
- Как послать команду Modbus RTU на запись нескольких дискретных выводов? Команда 0x0F
- Как послать команду Modbus RTU на запись нескольких аналоговых выводов? Команда 0x10
- Какие бывают ошибки запроса Modbus?
- Программы для работы с протоколом Modbus RTU
Описание протокола Modbus RTU
Modbus — коммуникационный протокол, основан на архитектуре ведущий-ведомый (master-slave). Использует для передачи данных интерфейсы RS-485, RS-422, RS-232, а также Ethernet сети TCP/IP (протокол Modbus TCP).
Сообщение Modbus RTU состоит из адреса устройства SlaveID, кода функции, специальных данных в зависимости от кода функции и CRC контрольной суммы.
SlaveID | Код функции | Специальные данные | CRC |
Если отбросить SlaveID адрес и CRC контрольную сумму, то получится PDU, Protocol Data Unit.
SlaveID – это адрес устройства, может принимать значение от 0 до 247, адреса с 248 до 255 зарезервированы.
Данные в модуле хранятся в 4 таблицах.
Две таблицы доступны только для чтения и две для чтения-записи.
В каждой таблице помещается 9999 значений.
Номер регистра | Адрес регистра HEX | Тип | Название | Тип |
---|---|---|---|---|
1-9999 | 0000 до 270E | Чтение-запись | Discrete Output Coils | DO |
10001-19999 | 0000 до 270E | Чтение | Discrete Input Contacts | DI |
30001-39999 | 0000 до 270E | Чтение | Analog Input Registers | AI |
40001-49999 | 0000 до 270E | Чтение-запись | Analog Output Holding Registers | AO |
В сообщении Modbus используется адрес регистра.
Например, первый регистр AO Holding Register, имеет номер 40001, но его адрес равен 0000.
Разница между этими двумя величинами есть смещение offset.
Каждая таблица имеет свое смещение, соответственно: 1, 10001, 30001 и 40001.
Ниже приведен пример запроса Modbus RTU для получения значения AO аналогового выхода (holding registers) из регистров от #40108 до 40110 с адресом устройства 17.
11 03 006B 0003 7687
11 | Адрес устройства SlaveID (17 = 11 hex) |
03 | Функциональный код Function Code (читаем Analog Output Holding Registers) |
006B | Адрес первого регистра (40108-40001 = 107 =6B hex) |
0003 | Количество требуемых регистров (чтение 3-х регистров с 40108 по 40110) |
7687 | Контрольная сумма CRC |
В ответе от Modbus RTU Slave устройства мы получим:
11 03 06 AE41 5652 4340 49AD
Где:
11 | Адрес устройства (17 = 11 hex) | SlaveID |
03 | Функциональный код | Function Code |
06 | Количество байт далее (6 байтов идут следом) | Byte Count |
AE | Значение старшего разряда регистра (AE hex) | Register value Hi (AO0) |
41 | Значение младшего разряда регистра (41 hex) | Register value Lo (AO0) |
56 | Значение старшего разряда регистра (56 hex) | Register value Hi (AO1) |
52 | Значение младшего разряда регистра (52 hex) | Register value Lo (AO1) |
43 | Значение старшего разряда регистра (43 hex) | Register value Hi (AO2) |
40 | Значение младшего разряда регистра (40 hex) | Register value Lo (AO2) |
49 | Контрольная сумма | CRC value Lo |
AD | Контрольная сумма | CRC value Hi |
Регистр аналогового выхода AO0 имеет значение AE 41 HEX или 44609 в десятичной системе.
Регистр аналогового выхода AO1 имеет значение 56 52 HEX или 22098 в десятичной системе.
Регистр аналогового выхода AO2 имеет значение 43 40 HEX или 17216 в десятичной системе.
Значение AE 41 HEX — это 16 бит 1010 1110 0100 0001, может принимать различное значение, в зависимости от типа представления.
Значение регистра 40108 при комбинации с регистром 40109 дает 32 бит значение.
Пример представления.
Тип представления | Диапазон значений | Пример в HEX | Будет в десятичной форме |
---|---|---|---|
16-bit unsigned integer | 0 до 65535 | AE41 | 44,609 |
16-bit signed integer | -32768 до 32767 | AE41 | -20,927 |
two character ASCII string | 2 знака | AE41 | ® A |
discrete on/off value | 0 и 1 | 0001 | 0001 |
32-bit unsigned integer | 0 до 4,294,967,295 | AE41 5652 | 2,923,517,522 |
32-bit signed integer | -2,147,483,648 до 2,147,483,647 | AE41 5652 | -1,371,449,774 |
32-bit single precision IEEE floating point number | 1,2·10−38 до 3,4×10+38 | AE41 5652 | -4.395978 E-11 |
four character ASCII string | 4 знака | AE41 5652 | ® A V R |
Наверх к оглавлению
Какие бывают команды Modbus RTU?
Приведем таблицу с кодами функций чтения и записи регистров Modbus RTU.
Код функции | Что делает функция | Тип значения | Тип доступа | |
---|---|---|---|---|
01 (0x01) | Чтение DO | Read Coil Status | Дискретное | Чтение |
02 (0x02) | Чтение DI | Read Input Status | Дискретное | Чтение |
03 (0x03) | Чтение AO | Read Holding Registers | 16 битное | Чтение |
04 (0x04) | Чтение AI | Read Input Registers | 16 битное | Чтение |
05 (0x05) | Запись одного DO | Force Single Coil | Дискретное | Запись |
06 (0x06) | Запись одного AO | Preset Single Register | 16 битное | Запись |
15 (0x0F) | Запись нескольких DO | Force Multiple Coils | Дискретное | Запись |
16 (0x10) | Запись нескольких AO | Preset Multiple Registers | 16 битное | Запись |
Наверх к оглавлению
Как послать команду Modbus RTU на чтение дискретного вывода? Команда 0x01
Эта команда используется для чтения значений дискретных выходов DO.
В запросе PDU задается начальный адрес первого регистра DO и последующее количество необходимых значений DO. В PDU значения DO адресуются, начиная с нуля.
Значения DO в ответе находятся в одном байте и соответствуют значению битов.
Значения битов определяются как 1 = ON и 0 = OFF.
Младший бит первого байта данных содержит значение DO адрес которого указывался в запросе. Остальные значения DO следуют по нарастающей к старшему значению байта. Т.е. справа на лево.
Если запрашивалось меньше восьми значений DO, то оставшиеся биты в ответе будут заполнены нулями (в направлении от младшего к старшему байту). Поле Byte Count Количество байт далее указывает количество полных байтов данных в ответе.
Пример запроса DO с 20 по 56 для SlaveID адреса устройства 17. Адрес первого регистра будет 0013 hex = 19, т.к. счет ведется с 0 адреса (0014 hex = 20, -1 смещение нуля = получаем 0013 hex = 19).
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
01 | Функциональный код | 01 | Функциональный код |
00 | Адрес первого регистра Hi байт | 05 | Количество байт далее |
13 | Адрес первого регистра Lo байт | CD | Значение регистра DO 27-20 (1100 1101) |
00 | Количество регистров Hi байт | 6B | Значение регистра DO 35-28 (0110 1011) |
25 | Количество регистров Lo байт | B2 | Значение регистра DO 43-36 (1011 0010) |
0E | Контрольная сумма CRC | 0E | Значение регистра DO 51-44 (0000 1110) |
84 | Контрольная сумма CRC | 1B | Значение регистра DO 56-52 (0001 1011) |
45 | Контрольная сумма CRC | ||
E6 | Контрольная сумма CRC |
Состояния выходов DO 27-20 показаны как значения байта CD hex, или в двоичной системе 1100 1101.
В регистре DO 56-52 5 битов справа были запрошены, а остальные биты заполнены нулями до полного байта (0001 1011).
Каналы | — | — | — | DO 56 | DO 55 | DO 54 | DO 53 | DO 52 |
---|---|---|---|---|---|---|---|---|
Биты | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
Hex | 1B |
Модули с дискретным выводом: M-7065, ioLogik R1214, ADAM-4056S
Наверх к оглавлению
Как послать команду Modbus RTU на чтение дискретного ввода? Команда 0x02
Эта команда используется для чтения значений дискретных входов DI.
Пример запроса DI с регистров от #10197 до 10218 для SlaveID адреса устройства 17. Адрес первого регистра будет 00C4 hex = 196, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
02 | Функциональный код | 02 | Функциональный код |
00 | Адрес первого регистра Hi байт | 03 | Количество байт далее |
C4 | Адрес первого регистра Lo байт | AC | Значение регистра DI 10204-10197 (1010 1100) |
00 | Количество регистров Hi байт | DB | Значение регистра DI 10212-10205 (1101 1011) |
16 | Количество регистров Lo байт | 35 | Значение регистра DI 10218-10213 (0011 0101) |
BA | Контрольная сумма CRC | 20 | Контрольная сумма CRC |
A9 | Контрольная сумма CRC | 18 | Контрольная сумма CRC |
Модули с дискретным вводом: M-7053, ioLogik R1210, ADAM-4051
Наверх к оглавлению
Как послать команду Modbus RTU на чтение аналогового вывода? Команда 0x03
Эта команда используется для чтения значений аналоговых выходов AO.
Пример запроса AO с регистров от #40108 до 40110 для SlaveID адреса устройства 17. Адрес первого регистра будет 006B hex = 107, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
03 | Функциональный код | 03 | Функциональный код |
00 | Адрес первого регистра Hi байт | 06 | Количество байт далее |
6B | Адрес первого регистра Lo байт | AE | Значение регистра Hi #40108 |
00 | Количество регистров Hi байт | 41 | Значение регистра Lo #40108 |
03 | Количество регистров Lo байт | 56 | Значение регистра Hi #40109 |
76 | Контрольная сумма CRC | 52 | Значение регистра Lo #40109 |
87 | Контрольная сумма CRC | 43 | Значение регистра Hi #40110 |
40 | Значение регистра Lo #40110 | ||
49 | Контрольная сумма CRC | ||
AD | Контрольная сумма CRC |
Модули с аналоговым выводом: M-7024, ioLogik R1241, ADAM-4024
Наверх к оглавлению
Как послать команду Modbus RTU на чтение аналогового ввода? Команда 0x04
Эта команда используется для чтения значений аналоговых входов AI.
Пример запроса AI с регистра #30009 для SlaveID адреса устройства 17. Адрес первого регистра будет 0008 hex = 8, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
04 | Функциональный код | 04 | Функциональный код |
00 | Адрес первого регистра Hi байт | 02 | Количество байт далее |
08 | Адрес первого регистра Lo байт | 00 | Значение регистра Hi #30009 |
00 | Количество регистров Hi байт | 0A | Значение регистра Lo #30009 |
01 | Количество регистров Lo байт | F8 | Контрольная сумма CRC |
B2 | Контрольная сумма CRC | F4 | Контрольная сумма CRC |
98 | Контрольная сумма CRC |
Модули с аналоговым вводом: M-7017, ioLogik R1240, ADAM-4017+
Наверх к оглавлению
Как послать команду Modbus RTU на запись дискретного вывода? Команда 0x05
Эта команда используется для записи одного значения дискретного выхода DO.
Значение FF 00 hex устанавливает выход в значение включен ON.
Значение 00 00 hex устанавливает выход в значение выключен OFF.
Все остальные значения недопустимы и не будут влиять значение на выходе.
Нормальный ответ на такой запрос — это эхо (повтор запроса в ответе), возвращается после того, как состояние DO было изменено.
Пример записи в DO с регистром #173 для SlaveID адреса устройства 17. Адрес регистра будет 00AC hex = 172, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
05 | Функциональный код | 05 | Функциональный код |
00 | Адрес первого регистра Hi байт | 00 | Адрес первого регистра Hi байт |
AC | Адрес первого регистра Lo байт | AC | Адрес первого регистра Lo байт |
FF | Значение Hi байт | FF | Значение Hi байт |
00 | Значение Lo байт | 00 | Значение Lo байт |
4E | Контрольная сумма CRC | 4E | Контрольная сумма CRC |
8B | Контрольная сумма CRC | 8B | Контрольная сумма CRC |
Состояние выхода DO173 поменялось с выключен OFF на включен ON.
Модули с дискретным выводом: M-7053, ioLogik R1210, ADAM-4051
Наверх к оглавлению
Как послать команду Modbus RTU на запись аналогового вывода? Команда 0x06
Эта команда используется для записи одного значения аналогового выхода AO.
Пример записи в AO с регистром #40002 для SlaveID адреса устройства 17. Адрес первого регистра будет 0001 hex = 1, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
06 | Функциональный код | 06 | Функциональный код |
00 | Адрес первого регистра Hi байт | 00 | Адрес первого регистра Hi байт |
01 | Адрес первого регистра Lo байт | 01 | Адрес первого регистра Lo байт |
00 | Значение Hi байт | 00 | Значение Hi байт |
03 | Значение Lo байт | 03 | Значение Lo байт |
9A | Контрольная сумма CRC | 9A | Контрольная сумма CRC |
9B | Контрольная сумма CRC | 9B | Контрольная сумма CRC |
Модули с аналоговым выводом: M-7024, ioLogik R1241, ADAM-4024
Наверх к оглавлению
Как послать команду Modbus RTU на запись нескольких дискретных выводов? Команда 0x0F
Эта команда используется для записи нескольких значений дискретного выхода DO.
Пример записи в несколько DO с регистрами от #20 до #29 для SlaveID адреса устройства 17. Адрес регистра будет 0013 hex = 19, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
0F | Функциональный код | 0F | Функциональный код |
00 | Адрес первого регистра Hi байт | 00 | Адрес первого регистра Hi байт |
13 | Адрес первого регистра Lo байт | 13 | Адрес первого регистра Lo байт |
00 | Количество регистров Hi байт | 00 | Кол-во записанных рег. Hi байт |
0A | Количество регистров Lo байт | 0A | Кол-во записанных рег. Lo байт |
02 | Количество байт далее | 26 | Контрольная сумма CRC |
CD | Значение байт DO 27-20 (1100 1101) | 99 | Контрольная сумма CRC |
01 | Значение байт DO 29-28 (0000 0001) | ||
BF | Контрольная сумма CRC | ||
0B | Контрольная сумма CRC |
В ответе возвращается количество записанных регистров.
Модули с дискретным выводом: M-7053, ioLogik R1210, ADAM-4051
Наверх к оглавлению
Как послать команду Modbus RTU на запись нескольких аналоговых выводов? Команда 0x10
Эта команда используется для записи нескольких значений аналогового выхода AO.
Пример записи в несколько AO с регистрами #40002 и #40003 для SlaveID адреса устройства 17. Адрес первого регистра будет 0001 hex = 1, т.к. счет ведется с 0 адреса.
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
11 | Адрес устройства | 11 | Адрес устройства |
10 | Функциональный код | 10 | Функциональный код |
00 | Адрес первого регистра Hi байт | 00 | Адрес первого регистра Hi байт |
01 | Адрес первого регистра Lo байт | 01 | Адрес первого регистра Lo байт |
00 | Количество регистров Hi байт | 00 | Кол-во записанных рег. Hi байт |
02 | Количество регистров Lo байт | 02 | Кол-во записанных рег. Lo байт |
04 | Количество байт далее | 12 | Контрольная сумма CRC |
00 | Значение Hi 40002 | 98 | Контрольная сумма CRC |
0A | Значение Lo 40002 | ||
01 | Значение Hi 40003 | ||
02 | Значение Lo 40003 | ||
C6 | Контрольная сумма CRC | ||
F0 | Контрольная сумма CRC |
Модули с аналоговым выводом: M-7024, ioLogik R1241, ADAM-4024
Наверх к оглавлению
Какие бывают ошибки запроса Modbus?
Если устройство получило запрос, но запрос не может быть обработан, то устройство ответит кодом ошибки.
Ответ будет содержать измененный Функциональный код, старший бит будет равен 1.
Пример:
Было | Стало |
---|---|
Функциональный код в запросе | Функциональный код ошибки в ответе |
01 (01 hex) 0000 0001 | 129 (81 hex) 1000 0001 |
02 (02 hex) 0000 0010 | 130 (82 hex) 1000 0010 |
03 (03 hex) 0000 0011 | 131 (83 hex) 1000 0011 |
04 (04 hex) 0000 0100 | 132 (84 hex) 1000 0100 |
05 (05 hex) 0000 0101 | 133 (85 hex) 1000 0101 |
06 (06 hex) 0000 0110 | 134 (86 hex) 1000 0110 |
15 (0F hex) 0000 1111 | 143 (8F hex) 1000 1111 |
16 (10 hex) 0001 0000 | 144 (90 hex) 1001 0000 |
Пример запроса и ответ с ошибкой:
Байт | Запрос | Байт | Ответ |
---|---|---|---|
(Hex) | Название поля | (Hex) | Название поля |
0A | Адрес устройства | 0A | Адрес устройства |
01 | Функциональный код | 81 | Функциональный код с измененным битом |
04 | Адрес первого регистра Hi байт | 02 | Код ошибки |
A1 | Адрес первого регистра Lo байт | B0 | Контрольная сумма CRC |
00 | Количество регистров Hi байт | 53 | Контрольная сумма CRC |
01 | Количество регистров Lo байт | ||
AC | Контрольная сумма CRC | ||
63 | Контрольная сумма CRC |
Расшифровка кодов ошибок
01 | Принятый код функции не может быть обработан. |
02 | Адрес данных, указанный в запросе, недоступен. |
03 | Значение, содержащееся в поле данных запроса, является недопустимой величиной. |
04 | Невосстанавливаемая ошибка имела место, пока ведомое устройство пыталось выполнить затребованное действие. |
05 | Ведомое устройство приняло запрос и обрабатывает его, но это требует много времени. Этот ответ предохраняет ведущее устройство от генерации ошибки тайм-аута. |
06 | Ведомое устройство занято обработкой команды. Ведущее устройство должно повторить сообщение позже, когда ведомое освободится. |
07 | Ведомое устройство не может выполнить программную функцию, заданную в запросе. Этот код возвращается для неуспешного программного запроса, использующего функции с номерами 13 или 14. Ведущее устройство должно запросить диагностическую информацию или информацию об ошибках от ведомого. |
08 | Ведомое устройство при чтении расширенной памяти обнаружило ошибку паритета. Ведущее устройство может повторить запрос, но обычно в таких случаях требуется ремонт. |
10 (0A hex) |
Шлюз неправильно настроен или перегружен запросами. |
11 (0B hex) |
Slave устройства нет в сети или от него нет ответа. |
Наверх к оглавлению
Программы для работы с протоколом Modbus RTU
Ниже перечислены программы, которые облегчают работу с Modbus.
DCON Utility Pro с поддержкой Modbus RTU, ASCII, DCON. Скачать
Modbus Master Tool с поддержкой Modbus RTU, ASCII, TCP. Скачать
Modbus TCP client с поддержкой Modbus TCP. Скачать
Наверх к оглавлению
Оставить заявку
Описание протокола Modbus:
Перед тем как приступить к описанию протокола, давайте разберёмся с терминами.
Шина – канал связи, служащий для передачи данных, используя предписанные электрические (физические) и логические (управляющие) уровни.
Шина это нечно физическое (дорожки на плате, провода, шлейфы и т.д.). Шина определяет граничные значения скорости, максимальную дальность, напряжения логических уровней, допустимые емкости между линиями, форму сигналов на линиях и т.д.
Протокол – набор правил и действий (очерёдности действий), позволяющий осуществлять соединение и обмен данными.
Протокол это правила по которым осуществляется передача данных. Данные между двумя устройствами передаются по шине в соответствии с протоколом.
Интерфейс – совокупность средств и правил, обеспечивающих логическое и физическое взаимодействие устройств и (или) программ системы.
Интерфейс это шина + протокол + устройство или программа отвечающая за передачу данных.
Протокол Modbus – коммуникационный протокол, основанный на архитектуре ведущий-ведомый.
Протокол Modbus разработан для шин: RS-485, RS-422, RS-232, и интерфейса Ethernet сети TCP/IP, но при желании его можно использовать для связи и по другим шинам или даже радиоканалам.
Протокол Modbus предусматривает наличие одного ведущего и до 247 ведомых устройств. Количество ведомых устройств может быть ограничено физическими возможностями шины.
Для ведущего устройства (master) в протоколе Modbus используется термин — client. Для ведомого устройства (slave) в протоколе Modbus используется термин — server. Да, это не опечатка, ведущий – клиент, ведомый – сервер. Думаю не будет ошибкой, если далее в статье мы будем пользоваться более привычными терминами: ведущий (master), ведомый (slave).
В соответствии с протоколом Modbus связь всегда начинает мастер, а ведомые устройства могут только отвечать на запросы мастера. Связь осуществляется посылкой пакета запроса (от мастера к ведомому) и посылкой ответного пакета (от ведомого к мастеру).
Типы протоколов Modbus:
- Modbus RTU:
Данные передаются в двоичном формате, разделителем пакетов служит отсутствие данных в течении времени при котором можно передать более 3,5 байт.
Протокол предназначен для шин: RS-485, RS-422, RS-232. - Modbus ASCII:
Данные передаются символами из таблицы ASCII в шестнадцатеричном формате, разделителями пакетов служат символы начала и конца пакета.
Каждый пакет начинается символом двоеточия, в кодировке ASCII и заканчивается символами возврата каретки ‘\r’, и переноса строки ‘\n’.
Например, для передачи адреса 10 в протоколе Modbus RTU будет передан байт 0x0A, а в протоколе Modbus ASCII будет передан байт символа ‘0’ и байт символа ‘A’, в кодировке ASCII.
Протокол предназначен для шин: RS-485, RS-422, RS-232. - Modbus TCP:
Данные передаются в двоичном формате и упаковываются в обычный TCP-пакет, для передачи по IP-сетям. Отличием данного протокола является отсутствие проверки целостности (CRC-16), так как TCP уже имеет собственный механизм контроля целостности.
Протокол предназначен для сетей TCP/IP.
Состав пакета Modbus RTU:
Состав пакета запроса от мастера к ведомому, аналогичен составу ответного пакета ведомого.
ADU (до 256 байт) | |||
АДРЕС (1 байт) |
PDU (до 253 байт) |
CRC-16 (2 байта) |
|
КОМАНДА (1 байт) |
ДАННЫЕ (до 252 байт) количество байт зависит от команды |
||
0…247 | 1…127 | 0…65535 |
- ADU (Application Data Unit) – пакет Modbus целиком.
- PDU (Protocol Data Unit) – основная часть пакета, состоит из команды и данных.
- АДРЕС – адрес ведомого устройства которому адресован пакет запроса, или от которого отправлен пакет ответа. Ведомые устройства могут иметь адреса от 1 до 247. Пакет запроса отправленный с адресом 0 является широковещательным, он адресован всем ведомым на шине, они обязаны выполнить запрос, но не должны на него отвечать.
- КОМАНДА – указывает какие действия должен выполнить ведомый. Команда в запросе может иметь значение от 1 до 127. Этот диапазон соответствует байту у которого сброшен старший бит. Если ведомый выполнил запрос мастера, то в ответном пакете он указывает тот же номер команды, без изменений. Если ведомый обнаружил ошибку в запросе, или не может его выполнить, то в ответном пакете он указывает номер команды с установленным старшим битом. То есть номер команды будет лежать в диапазоне от 129 до 255.
- ДАННЫЕ – состав и количество данных в запросе и ответе зависят от номера команды. Если ведомый обнаружил ошибку в запросе, или не может его выполнить, то в ответном пакете, в поле данных, он указывает код ошибки.
- CRC-16 – проверка целостности пакета, два байта передаются младшим байтом вперёд.
Для протокола Modbus значение CRC-16 рассчитывается используя реверсивный сдвиг, начальное значение равно 0xFFFF, порождающий полином равен 0xA001.
Регистры ведомых устройств:
Данные ведомых устройств хранятся в регистрах. Существует 4 типа регистров:
Адрес: | Название: | Назначение: | |||
---|---|---|---|---|---|
0…0x270E | (DI) | Discrete Input | 1 бит | Цифровые входы | R |
0…0x270E | (DO) | Discrete Output Coils | 1 бит | Цифровые выходы (регистры флагов) |
RW |
0…0x270E | (AI) | Analog Input Registers | 16 бит | Аналоговые входы | R |
0…0x270E | (AO) | Analog Output Holding Registers | 16 бит | Аналоговые выходы (регистры хранения) |
RW |
Данные в регистрах не обязательно связаны со значениями на входах и выходах ведомых устройств. Каждый регистр DI/DO хранит 1 бит. Каждый регистр AI/AO хранит 16 бит (2 байта).
Например, регистры AI и AO могут хранить входные данные и результаты вычислений, а регистры DI и DO флаги настроек, и биты состояний.
Команды протокола Modbus:
Номер: | Название: | Назначение: | |
---|---|---|---|
hex | dec | ||
0x01 | 1 | Read Coil Status | Чтение значений из нескольких регистров «DO» |
0x02 | 2 | Read Discrete Inputs. | Чтение значений из нескольких регистров «DI». |
0x03 | 3 | Read Holding Registers. | Чтение значений из нескольких регистров «AO». |
0x04 | 4 | Read Input Registers. | Чтение значений из нескольких регистров «AI». |
0x05 | 5 | Force Single Coil. | Запись значения в один регистр «DO». |
0x06 | 6 | Preset Single Register. | Запись значения в один регистр «AO». |
0x07 | 7 | Read Exception Status. | Чтение сигналов состояния статусных выходов. |
0x08 | 8 | Diagnostic. | Диагностика. |
0x09 | 9 | — — |
Команда не стандартизирована, но уже используется в различных устройствах. |
0x0A | 10 | ||
0x0B | 11 | Get Com Event Counter. | Чтение счетчика событий. |
0x0C | 12 | Get Com Event Log. | Чтение журнала событий. |
0x0D | 13 | — — |
Команда не стандартизирована, но уже используется в различных устройствах. |
0x0E | 14 | ||
0x0F | 15 | Force Multiple Coils. | Запись значений в несколько регистров «DO». |
0x10 | 16 | Preset Multiple Registers. | Запись значений в несколько регистров «AO». |
0x11 | 17 | Report Slave ID. | Чтение информации об устройстве. |
0x12 | 18 | — — |
— — |
0x13 | 19 | ||
0x14 | 20 | Read File Record. | Чтение из файла. |
0x15 | 21 | Write File Record. | Запись в файл. |
0x16 | 22 | Mask Write Register. | Запись значения в регистр «AO» с масками И и ИЛИ. |
0x17 | 23 | Read/Write Multiple Registers. | Чтение и запись нескольких регистров «AO». |
0x18 | 24 | Read FIFO Queue. | Чтение данных из буфера FIFO. |
Все перечисленные команды (кроме работы с файлами и буфером FIFO) реализованы в библиотеке iarduino_Modbus.
Библиотека iarduino_Modbus:
Библиотека iarduino_Modbus позволяет работать с устройствами по шине RS485 используя протокол Modbus RTU или Modbus ASCII. Так как у большинства плат Arduino и ESP нет шины RS485, библиотека использует аппаратную или программную шину UART. Сигналы шины UART необходимо преобразовать в сигналы шины RS485 при помощи конвертирующего модуля UART-RS485, например, на базе микросхемы на MAX485.
Конвертер подключается к выводам TX и RX шины UART, а так же назначается вывод разрешающий работу передатчика DE.
Подключение к шине RS485:
- Для подключения конвертера к микроконтроллеру используется вывод DE и выводы UART.
- TX — вывод шины UART микроконтроллера для передачи данных к конвертеру.
- RX — вывод шины UART микроконтроллера для получения данных от конвертера.
- DI — вывод шины UART конвертера для получения данных от микроконтроллера.
- RO — вывод шины UART конвертера для передачи данных к микроконтроллеру.
- DE — вывод конвертера разрешающий работу его передатчика на шине RS485.
- ~RE — вывод конвертера разрешающий работу его приёмника на шине RS485.
- Для подключения устройств к шине RS485 используются выводы A, B и GND.
- A, B — витая пара для передачи/приёма данных.
- GND — линия шины RS485 для выравнивания потенциалов.
- Источники питания могут быть разные для каждого устройства, или один на все, если напряжение питания устройств совпадают.
Подключение к программной шине Piranha UNO:
Вместо выводов 2, 8 и 9 платы Piranha UNO можно использовать любые выводы, указав их номера в скетче.
SoftwareSerial rs485(8, 9); // Создаём объект для работы с программной шиной UART указывая выводы RX, TX. ModbusClient modbus(rs485, 2); // Создаём объект для работы по протоколу Modbus указав объект программной шины UART и номер вывода DE конвертера UART-RS485. // ModbusClient modbus(rs485); // Если вывод DE не используется, то он и не указывается.
Подключение к аппаратной шине Piranha ULTRA:
В примере используется аппаратная шина UART1 использующая выводы 8 и 9. Вместо вывода 2 можно использовать любой вывод платы Piranha ULTRA, указав его номер в скетче. Если вывод DE конвертера не используется, то он и не указывается в скетче.
ModbusClient modbus(Serial1, 2); // Создаём объект для работы по протоколу Modbus указав класс Serial1 и номер вывода DE конвертера UART-RS485. // ModbusClient modbus(Serial1); // Если вывод DE не используется, то он и не указывается.
Подключение к аппаратной шине Piranha ESP32:
В примере используется аппаратная шина UART2 работающая на выводах 16 и 17. Вместо вывода 22 можно использовать любой вывод платы Piranha ESP32, указав его номер в скетче. Если вывод DE конвертера не используется, то он и не указывается в скетче.
ModbusClient modbus(Serial2, 22); // Создаём объект для работы по протоколу Modbus указав класс Serial2 и номер вывода DE конвертера UART-RS485. // ModbusClient modbus(Serial2); // Если вывод DE не используется, то он и не указывается.
Описание функций библиотеки:
В данном разделе описаны функции библиотеки iarduino_Modbus для работы с шиной RS485 по протоколу Modbus.
Синтаксис функций библиотеки iarduino_Modbus совместим с библиотекой ArduinoModbus.
Подключение библиотеки:
В примерах вход DE конвертера подключён к выводу D2 Arduino. Вместо вывода D2 можно использовать любой выход Arduino. Если конвертер не имеет вывода DE, или он не подключён к Arduino, то номер вывода DE не указывается при создании объекта.
- Если используется аппаратная шина UART:
#include <iarduino_Modbus.h> // Подключаем библиотеку для работы по протоколу Modbus. ModbusClient modbus(Serial, 2); // Создаём объект для работы по протоколу Modbus указав класс Serial и номер вывода DE конвертера UART-RS485. // ModbusClient modbus(Serial); // Если вывод DE не используется, то создаём объект не указывая его.
Если используется аппаратная шина UART-1, то вместо класса Serial указываем Serial1, для шины UART2 указываем Serial2 и т.д.
- Если используется программная реализация шины UART:
#include <SoftwareSerial.h> // Подключаем библиотеку для работы с программной шиной UART. #include <iarduino_Modbus.h> // Подключаем библиотеку для работы по протоколу Modbus. // SoftwareSerial rs485(8, 9); // Создаём объект для работы с программной шиной UART указывая выводы RX, TX. ModbusClient modbus(rs485, 2); // Создаём объект для работы по протоколу Modbus указав объект программной шины UART и номер вывода DE конвертера UART-RS485. // ModbusClient modbus(rs485); // Если вывод DE не используется, то создаём объект не указывая его.
Для программной реализации шины UART необходимо указать выводы которые будут использоваться как RX и TX, в примере указаны выводы 8, и 9 соответственно. При создании объекта modbus указывается не класс Serial, а объект для работы с программной шиной UART.
- Если используются две и более шины:
#include <iarduino_Modbus.h> // Подключаем библиотеку для работы по протоколу Modbus. ModbusClient modbus1(Serial1, 2); // Создаём объект для работы по протоколу Modbus указав класс Serial1 и номер вывода DE конвертера UART1-RS485_1. ModbusClient modbus2(Serial2, 3); // Создаём объект для работы по протоколу Modbus указав класс Serial2 и номер вывода DE конвертера UART2-RS485_2. ModbusClient modbus3(Serial3); // Создаём объект для работы по протоколу Modbus указав класс Serial3, вывод DE не используется.
В примере созданы три объекта для работы по трём аппаратным шинам UART. К двум первым шинам подключены конвертеры с выводом DE и в примере указаны номера выводов Arduino которые управляют этими выводами, а для третьей шины вывод DE не указан, значит этого вывода нет у конвертера третьей шины. Количество создаваемых объектов ограничено количеством шин и памятью микроконтроллера. Можно добавить еще и программную шину, подключив библиотеку SoftwareSerial как в примере выше.
Скорость передачи данных по шине Modbus равна скорости шины UART.
Функция begin();
- Назначение: Инициализация работы по протоколу Modbus.
- Синтаксис: begin();
- Параметры: Нет.
- Возвращаемое значение: Нет.
- Примечание:
- Протокол будет инициирован на той шине UART, которая указана при создании объекта.
- Функцию достаточно вызвать 1 раз в коде Setup(), до обращения к остальным функциям библиотеки.
- Пример:
modbus.begin(); // Инициируем работу по протоколу Modbus.
Функция setTypeMB();
- Назначение: Указание типа протокола Modbus.
- Синтаксис: setTypeMB( ТИП );
- Параметр: ТИП — может принимать значение MODBUS_RTU или MODBUS_ASCII.
- Возвращаемое значение: Нет.
- Примечание: Тип протокола по умолчанию MODBUS_RTU.
- Пример:
modbus.setTypeMB( MODBUS_RTU ); // Указываем тип протокола Modbus: MODBUS_RTU (по умолчанию), или MODBUS_ASCII.
Функция setTimeout();
- Назначение: Указание максимального времени ожидания ответа от ведомых устройств.
- Синтаксис: setTimeout( ВРЕМЯ );
- Параметр: ВРЕМЯ uint32_t — количество миллисекунд.
- Возвращаемое значение: Нет.
- Примечание:
- Максимальное время ожидания устанавливается, как для типа RTU, так и для ASCII.
- Максимальное время ожидания по умолчанию 10 мс.
- Библиотека не ждёт завершение времени ожидания, если модуль ответил раньше.
- Пример:
modbus.setTimeout( 15 ); // Указываем жать ответ от модулей не более 15 мс.
Функция setDelay();
- Назначение: Указание паузы до отправки сообщения.
- Синтаксис: setDelay( ВРЕМЯ );
- Параметр: ВРЕМЯ uint32_t — количество миллисекунд.
- Возвращаемое значение: Нет.
- Примечание:
- Пауза до отправки сообщения устанавливается только для протокола Modbus RTU.
- Пауза до отправки сообщения по умолчанию 3 мс.
- Пауза устанавливается между пакетами. Если после получения/отправки последнего пакета прошло больше времени, то новый пакет будет отправлен без паузы.
- Пример:
modbus.setDelay( 5 ); // Указываем выдерживать паузу между пакетами в 5 мс.
Функция coilRead();
- Назначение: Чтение одного регистра «Coil» (он же «Discrete Output»).
- Синтаксис: coilRead( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- Возвращаемое значение: int8_t — прочитанное значение (0/1), или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
bool i = modbus.coilRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Coil" с адресом 0.
- Пример с проверкой:
bool i; int8_t j = modbus.coilRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Coil" с адресом 0. if( j>=0 ){ i=j; } // Сохраняем прочитанное значение в переменную i. else { Serial.println("Error"); } // Выводим сообщение о ошибке чтения.
Функция discreteInputRead();
- Назначение: Чтение одного регистра «Discrete Input».
- Синтаксис: discreteInputRead( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- Возвращаемое значение: int8_t — прочитанное значение (0/1), или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
bool i = modbus.discreteInputRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Discrete Input" с адресом 0.
- Пример с проверкой:
bool i; int8_t j = modbus.discreteInputRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Discrete Input" с адресом 0. if( j>=0 ){ i=j; } // Сохраняем прочитанное значение в переменную i. else { Serial.println("Error"); } // Выводим сообщение о ошибке чтения.
Функция holdingRegisterRead();
- Назначение: Чтение одного регистра «Holding Register» (он же «Analog Output»).
- Синтаксис: holdingRegisterRead( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- Возвращаемое значение: int32_t — прочитанное значение (0…65535), или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
uint16_t i = modbus.holdingRegisterRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Holding Register" с адресом 0.
- Пример с проверкой:
uint16_t i; int32_t j = modbus.holdingRegisterRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Holding Register" с адресом 0. if( j>=0 ){ i=j; } // Сохраняем прочитанное значение в переменную i. else { Serial.println("Error"); } // Выводим сообщение о ошибке чтения.
Функция inputRegisterRead();
- Назначение: Чтение одного регистра «Input Register» (он же «Analog Input»).
- Синтаксис: inputRegisterRead( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- Возвращаемое значение: int32_t — прочитанное значение (0…65535), или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
uint16_t i = modbus.inputRegisterRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Input Register" с адресом 0.
- Пример с проверкой:
uint16_t i; int32_t j = modbus.inputRegisterRead(9, 0); // Читаем из модуля с адресом 9, значение регистра "Input Register" с адресом 0. if( j>=0 ){ i=j; } // Сохраняем прочитанное значение в переменную i. else { Serial.println("Error"); } // Выводим сообщение о ошибке чтения.
Функция coilWrite();
- Назначение: Запись в один регистр «Coil» (он же «Discrete Output»).
- Синтаксис: coilWrite( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА , ДАННЫЕ );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- ДАННЫЕ: bool — значение для записи 0 или 1.
- Возвращаемое значение: bool — результат записи значения в регистр (true или false).
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки записи:
modbus.coilWrite(9, 0, 1); // Для модуля с адресом 9, в регистр "Coil" с адресом 0, записываем значение 1.
- Пример с проверкой:
bool i = modbus.coilWrite(9, 0, 1); // Для модуля с адресом 9, в регистр "Coil" с адресом 0, записываем значение 1. if(i){ Serial.println( "Ok" ); } // Выводим сообщение о успешной записи. else { Serial.println("Error"); } // Выводим сообщение о ошибке записи.
Функция holdingRegisterWrite();
- Назначение: Запись в один регистр «Holding Register» (он же «Analog Output»).
- Синтаксис: holdingRegisterWrite( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА , ДАННЫЕ );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- ДАННЫЕ: uint16_t — значение для записи от 0 до 65535.
- Возвращаемое значение: bool — результат записи значения в регистр (true или false).
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки записи:
modbus.holdingRegisterWrite(9, 0, 1000); // Для модуля с адресом 9, в регистр "Holding Register" с адресом 0, записываем значение 1000.
- Пример с проверкой:
bool i = modbus.holdingRegisterWrite(9, 0, 1000); // Для модуля с адресом 9, в регистр "Holding Register" с адресом 0, записываем значение 1000. if(i){ Serial.println( "Ok" ); } // Выводим сообщение о успешной записи. else { Serial.println("Error"); } // Выводим сообщение о ошибке записи.
Функция registerMaskWrite();
- Назначение: Запись масок в один регистр «Holding Register» (он же «Analog Output»).
- Синтаксис: registerMaskWrite( [ АДРЕС_МОДУЛЯ ] , АДРЕС_РЕГИСТРА , AND , OR );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- АДРЕС_РЕГИСТРА: uint16_t — значение от 0 до 9999.
- AND: uint16_t — значение маски AND от 0 до 65535.
- OR: uint16_t — значение маски OR от 0 до 65535.
- Возвращаемое значение: bool — результат записи масок в регистр (true или false).
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Результат записи вычисляется по формуле: REG = ( REG & AND ) | ( OR & ~AND ).
- Каждый бит маски AND запрещает менять значение того же бита в регистре.
- Маска OR содержит биты для записи в регистр, если это не запрещено маской AND.
- Пример:
- Допустим сейчас значение регистра = ( 0101 0101 0101 0101 )2.
- Если маска AND = ( 0000 0000 1111 1111 )2, то она запрещает менять 8 младших битов.
- Для маски OR возьмем значение = ( 0111 0111 0111 0111 )2.
- После записи, значение регистра будет = ( 0111 0111 0101 0101 )2.
- 8 старших битов взяли значение из OR, а 8 младших остались без изменений.
- Так можно менять отдельные биты, без предварительного чтения регистра.
- Пример без проверки записи:
modbus.registerMaskWrite(9, 0, 0x00FF, 0x7777); // Для модуля с адресом 9, в регистр "Holding Register" с адресом 0, записываем маски AND=0x00FF и OR=0x7777.
- Пример с проверкой:
bool i = modbus.registerMaskWrite(9, 0, 0x00FF, 0x7777); // Для модуля с адресом 9, в регистр "Holding Register" с адресом 0, записываем маски AND=0x00FF и OR=0x7777. if(i){ Serial.println( "Ok" ); } // Выводим сообщение о успешной записи. else { Serial.println("Error"); } // Выводим сообщение о ошибке записи.
Функция beginTransmission();
- Назначение: Инициализация записи в несколько регистров «Coils» или «Holding Registers».
- Синтаксис: beginTransmission( [ АДРЕС_МОДУЛЯ ], ТИП, АДРЕС_РЕГИСТРА, КОЛИЧЕСТВО);
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- ТИП — может принимать значение COILS или HOLDING_REGISTERS.
- АДРЕС_РЕГИСТРА: uint16_t — адрес первого регистра для записи от 0 до 9999.
- КОЛИЧЕСТВО: uint16_t — количество записываемых регистров.
- Возвращаемое значение: bool — результат инициализации записи (true или false).
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Если задан некорректный диапазон адресов регистров, не будет записан ни один регистр.
- Данные для записи необходимо ставить в очередь функцией write().
- Запись данных поставленных в очередь осуществляется функцией endTransmission().
- Пример для функций beginTransmission(), write() и endTransmission(), указан ниже.
Функция write();
- Назначение: Постановка значения в очередь на запись, после beginTransmission().
- Синтаксис: write( ДАННЫЕ );
- Параметр: ДАННЫЕ uint16_t — значение 0/1 для «Coils», или 0…65535 для «Holding Registers».
- Возвращаемое значение: bool — результат постановки значения в очередь (true или false).
- Пример для функций beginTransmission(), write() и endTransmission(), указан ниже.
Функция endTransmission();
- Назначение: Выполнение инициированной ранее записи.
- Синтаксис: endTransmission();
- Параметр: Нет.
- Возвращаемое значение: bool — результат записи (true или false).
- Пример без проверки записи:
modbus.beginTransmission(9, COILS, 0, 3); // Инициируем запись для модуля с адресом 9, в регистры "Coils", начиная с адреса 0, всего 3 значения (3 регистра). modbus.write( 0 ); // Ставим значение в очередь на запись. Это значение будет записано в регистр, адрес которого был указан в modbus.beginTransmission(). modbus.write( 1 ); // Ставим значение в очередь на запись. Это значение будет записано в следующий регистр по порядку. modbus.write( 0 ); // Ставим значение в очередь на запись. Это значение будет записано в следующий регистр по порядку. modbus.endTransmission(); // Выполняем запись. // modbus.beginTransmission(9, HOLDING_REGISTERS, 5, 2); // Инициируем запись для модуля с адресом 9, в регистры "Holding Registers", начиная с адреса 5, всего 2 значения (2 регистра). modbus.write( 11111 ); // Ставим значение в очередь на запись. Это значение будет записано в регистр, адрес которого был указан в modbus.beginTransmission(). modbus.write( 22222 ); // Ставим значение в очередь на запись. Это значение будет записано в следующий регистр по порядку. modbus.endTransmission(); // Выполняем запись.
- Пример с проверкой:
modbus.beginTransmission(9, HOLDING_REGISTERS, 5, 2); // Инициируем запись для модуля с адресом 9, в регистры "Holding Registers", начиная с адреса 5, всего 2 значения (2 регистра). modbus.write( 11111 ); // Ставим значение в очередь на запись. Это значение будет записано в регистр, адрес которого был указан в modbus.beginTransmission(). modbus.write( 22222 ); // Ставим значение в очередь на запись. Это значение будет записано в следующий регистр по порядку. bool i = modbus.endTransmission(); // Выполняем запись. if(i){ Serial.println( "Ok" ); } // Выводим сообщение о успешной записи. else { Serial.println("Error"); } // Выводим сообщение о ошибке записи.
Функция requestFrom();
- Назначение: Чтение из нескольких регистров указанного типа.
- Синтаксис: requestFrom( [ АДРЕС_МОДУЛЯ ], ТИП, АДРЕС_РЕГИСТРА, КОЛИЧЕСТВО);
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- ТИП: COILS, или DISCRETE_INPUTS, или HOLDING_REGISTERS, или INPUT_REGISTERS.
- АДРЕС_РЕГИСТРА: uint16_t — адрес первого читаемого регистра от 0 до 9999.
- КОЛИЧЕСТВО: uint16_t — количество читаемых регистров.
- Возвращаемое значение: uint16_t — количество прочитанных регистров, или 0 при неудаче.
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Если задан некорректный диапазон адресов регистров, не будет прочитан ни один регистр.
- Для получения прочитанных данных нужно обращаться к функции read().
- Количество оставшихся доступных данных можно узнать функцией available().
- Пример для функций requestFrom(), available() и read(), указан ниже.
Функция available();
- Назначение: Получение количества значений, доступных для чтения функцией read().
- Синтаксис: available();
- Параметры: Нет.
- Возвращаемое значение: uint16_t — количество значений доступных для чтения read().
- Примечание: При обращении к любой функции библиотеки кроме available() и read(), буфер доступных значений будет очищен или перезаписан.
- Пример для функций requestFrom(), available() и read(), указан ниже.
Функция read();
- Назначение: Получение очередного прочитанного значения.
- Синтаксис: read();
- Параметры: Нет.
- Возвращаемое значение: int32_t — очередное прочитанное значение, или -1 при неудаче.
- Примечание: При обращении к любой функции библиотеки кроме available() и read(), буфер доступных значений будет очищен или перезаписан.
- Пример без проверки чтения:
modbus.requestFrom(9, COILS, 0, 3); // Читаем из модуля с адресом 9, регистры "Coils", начиная с адреса 0, всего 3 значения (3 регистра). while( modbus.available() ){ // Пока есть значения доступные для функции read()... Serial.println( modbus.read() ); // Выводим очередное прочитанное значение. } // // modbus.requestFrom(9, HOLDING_REGISTERS, 5, 2); // Читаем из модуля с адресом 9, регистры "Holding Registers", начиная с адреса 5, всего 2 значения (2 регистра). while( modbus.available() ){ // Пока есть значения доступные для функции read()... Serial.println( modbus.read() ); // Выводим очередное прочитанное значение. } //
- Пример с проверкой:
if( modbus.requestFrom(9, HOLDING_REGISTERS, 5, 2) ){ // Читаем из модуля с адресом 9, регистры "Holding Registers", начиная с адреса 5, всего 2 значения (2 регистра). while( modbus.available() ){ // Пока есть значения доступные для функции read()... Serial.println( modbus.read() ); // Выводим очередное прочитанное значение. } // }else{ Serial.println("Error"); } // Не удалось прочитать ни одного регистра.
Функция end();
- Назначение: Функция для совместимости с библиотекой ArduinoModbus.
- Синтаксис: end();
- Параметры: Нет
- Возвращаемое значение: Нет.
- Примечание: Функция позволяет перенести код написанный для библиотеки ArduinoModbus в скетч с библиотекой iarduino_Modbus, без сообщений об ошибках, но сама ничего не делает.
- Пример:
modbus.end(); // Пустая функция. Serial.end(); // Отключение шины. Функцию можно применять как к аппаратным шинам UART, так и к программной.
Функция exceptionStatusRead();
- Назначение: Чтение состояния 8 статусных выходов.
- Синтаксис: exceptionStatusRead( [ АДРЕС_МОДУЛЯ ] );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: int16_t — байт битов, или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
uint8_t i = modbus.exceptionStatusRead(9); // Читаем статусные выходы устройства с адресом 9. bool pin1 = bitRead(i, 0); // Получаем состояние 1 статусного выхода. bool pin2 = bitRead(i, 1); // Получаем состояние 2 статусного выхода. bool pin8 = bitRead(i, 7); // Получаем состояние 8 статусного выхода.
- Пример с проверкой:
int16_t i = modbus.exceptionStatusRead(9); // Читаем статусные выходы устройства с адресом 9. if( i<0 ){ // Если чтение не выполнено ... Serial.println( "Error" ); } // Выводим сообщение о ошибке чтения. }else{ // Если чтение выполнено ... bool pin1 = bitRead(i, 0); // Получаем состояние 1 статусного выхода. bool pin2 = bitRead(i, 1); // Получаем состояние 2 статусного выхода. bool pin8 = bitRead(i, 7); // Получаем состояние 8 статусного выхода. } //
Функция getSate();
- Назначение: Чтение состояния устройства.
- Синтаксис: getSate( [ АДРЕС_МОДУЛЯ ] );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: int8_t — состояние ( 0-свободно, 1-занято ), или -1 при неудаче.
- Примечание: Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Пример без проверки чтения:
bool i = modbus.getSate(9); // Читаем состояние устройства с адресом 9. if(i){ Serial.println( "Устройство занято" ); } else { Serial.println( "Устройство свободно" ); }
- Пример с проверкой:
int8_t i = modbus.getSate(9); // Читаем состояние устройства с адресом 9. if( i<0 ){ Serial.println( "Ошибка чтения" ); } if( i==0 ){ Serial.println( "Устройство свободно" ); } if( i>0 ){ Serial.println( "Устройство занято" ); }
Функция diagnostic();
- Назначение: Выполнение команды диагностики.
- Синтаксис: diagnostic( АДРЕС_МОДУЛЯ , НОМЕР_ФУНКЦИИ_ДИАГНОСТИКИ [ , ДАННЫЕ ] );
- Параметры:
- АДРЕС_МОДУЛЯ: uint8_t — значение от 1 до 247.
- НОМЕР_ФУНКЦИИ_ДИАГНОСТИКИ: uint16_t — значение от 0 до 65535.
- 0 — Проверка связи. Результатом диагностики будет значение аргумента ДАННЫЕ.
- 1 — Перезагрузка коммуникаций и диагностика внутренних систем устройства.
- 2 — Получить значение регистра диагностики. Каждый бит информирует о ошибке.
- 3 — Сменить символ конца пакета ASCII на значение аргумента ДАННЫЕ.
- 4 — Включить режим «Только прослушивание» (Listen Only Mode).
- 10 — Сбросить все счетчики и регистр диагностики.
- 11 — Получить значение счётчика всех запросов на шине.
- 12 — Получить значение счётчика запросов с ошибками CRC.
- 13 — Получить значение счётчика ответов об ошибках.
- 14 — Получить значение счётчика запросов адресованных устройству.
- 15 — Получить значение счётчика запросов которые остались без ответа.
- 16 — Получить значение счётчика ответов об ошибках CODE_ERR_NAK.
- 17 — Получить значение счётчика ответов о «занятости» CODE_ERR_BUSY.
- 18 — Получить значение счётчика ошибок переполнения приема символов.
- Остальные номера функций доступны в документации протокола Modbus.
- ДАННЫЕ: uint16_t — необязательное значение от 0 до 65535.
- Возвращаемое значение: int32_t — результат диагностики (0…65535), или -1 при неудаче.
- Примечание:
- Счётчик событий, это счётчик успешно выполненных запросов.
- Счётчик не реагирует на успешное выполнение команды чтения его значения.
- Пример:
uint16_t i = modbus.diagnostic(9, 0, 12345); // Проверка связи устройства с адресом 9. if( i!=12345 ){ Serial.println("Error"); } // Выявлена ошибка связи.
- Пример:
int32_t i = modbus.diagnostic(9, 2); // Получить значение регистра диагностики устройства с адресом 9. if(i<0){ Serial.println( "Ошибка чтения" ); }else if( i ){ Serial.println( "Модуль обнаружил сбои в работе своих систем" ); }else { Serial.println( "Устройство работает без ошибок" ); }
Функция getEventCounter();
- Назначение: Чтение счетчика событий.
- Синтаксис: getEventCounter( [ АДРЕС_МОДУЛЯ ] );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: int32_t — значение счётчика событий (0…65535), или -1 при неудаче.
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Счётчик событий, это счётчик успешно выполненных запросов.
- Счётчик не реагирует на успешное выполнение команды чтения его значения.
- Пример без проверки чтения:
uint16_t i = modbus.getEventCounter(9); // Читаем счётчик событий устройства с адресом 9. Serial.println( (String) "Устройство успешно выполнило "+i+" запросов." );
- Пример с проверкой:
int32_t i = modbus.getEventCounter(9); // Читаем счётчик событий устройства с адресом 9. if(i<0){ Serial.println( "Ошибка чтения" ); } else { Serial.println( (String) "Устройство успешно выполнило "+i+" запросов." ); }
Функция getEventLog();
- Назначение: Чтение журнала событий.
- Синтаксис: getEventLog( [ АДРЕС_МОДУЛЯ ] );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: uint8_t — количество данных, доступных для чтения функцией read().
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Для получения прочитанных данных нужно обращаться к функции read().
- Количество оставшихся доступных данных можно узнать функцией available().
- Данные будут получены в следующем порядке:
- uint16_t — слово состояния (0x0000 — устройство свободно, 0xFFFF — устройство занято).
- uint16_t — значение счётчика событий (0…65535).
- uint16_t — значение счётчика всех сообщений на шине (0…65535).
- uint8_t — массив байтов журнала событий (не более 64 байт).
- Пример без проверки чтения:
modbus.getEventLog(9); // Читаем журнал событий устройства с адресом 9. Serial.print( "Слово состояния = " ); Serial.println( modbus.read() ); Serial.print( "Счётчик событий = " ); Serial.println( modbus.read() ); Serial.print( "Счётчик сообщений = " ); Serial.println( modbus.read() ); Serial.println( "Байты журнала событий:" ); while( modbus.available() ){ Serial.println( modbus.read() ); }
- Пример с проверкой:
if( modbus.getEventLog(9) ){ // Читаем журнал событий устройства с адресом 9. if( modbus.available() ){ Serial.print( "Слово состояния = " ); Serial.println( modbus.read() ); } if( modbus.available() ){ Serial.print( "Счётчик событий = " ); Serial.println( modbus.read() ); } if( modbus.available() ){ Serial.print( "Счётчик сообщений = " ); Serial.println( modbus.read() ); } Serial.println( "Байты журнала событий:" ); while( modbus.available() ){ Serial.println( modbus.read() ); } }else{ Serial.println("Error"); // Модуль не поддерживает чтение журнала событий. }
Функция getInfo();
- Назначение: Чтение информации об устройстве.
- Синтаксис: getInfo( [ АДРЕС_МОДУЛЯ ] );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: uint8_t — количество данных, доступных для чтения функцией read().
- Примечание:
- Если адрес модуля не указан, команда будет отправлена всем устройствам.
- Для получения прочитанных данных нужно обращаться к функции read().
- Количество оставшихся доступных данных можно узнать функцией available().
- Данные будут получены в следующем порядке:
- uint8_t — идентификатор линейки устройств.
- uint8_t — индикатор пуска: (0x00 — off, 0xFF — on).
- uint8_t — массив данных об устройстве (количество байт зависит от устройства).
- Пример без проверки чтения:
modbus.getInfo(9); // Читаем информацию о устройстве с адресом 9. Serial.print( "Идентификатор линейки = " ); Serial.println( modbus.read() ); Serial.print( "Индикатор пуска = " ); Serial.println( modbus.read() ); Serial.println( "Остальные байты данных:" ); while( modbus.available() ){ Serial.println( modbus.read() ); }
- Пример с проверкой:
if( modbus.getInfo(9) ){ // Читаем информацию о устройстве с адресом 9. if( modbus.available() ){ Serial.print( "Идентификатор линейки = " ); Serial.println( modbus.read() ); } if( modbus.available() ){ Serial.print( "Индикатор пуска = " ); Serial.println( modbus.read() ); } Serial.println( "Остальные байты данных:" ); while( modbus.available() ){ Serial.println( modbus.read() ); } }else{ Serial.println("Error"); // Модуль не поддерживает чтение информации о устройстве. }
- Пример для модулей iarduino:
if( modbus.getInfo(9) ){ // Читаем информацию о устройстве с адресом 9. if( modbus.available() ){ Serial.println((String) "Идентификатор линейки "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Индикатор пуска "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Адрес модуля на шине "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Идентификатор устройства "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Версия прошивки "+modbus.read() ); } if( modbus.available()>=2){ Serial.println((String) "Регистр диагностики "+((modbus.read()<<8)|modbus.read()); } if( modbus.available() ){ Serial.println((String) "Младший байт регистра диагностики "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Количество регистров DO "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Количество регистров DI "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Количество регистров AO "+modbus.read() ); } if( modbus.available() ){ Serial.println((String) "Количество регистров AI "+modbus.read() ); } if( modbus.available()>=4){ Serial.println((String) "Задержка до ответа в мс"+(uint32_t)((modbus.read()<<24)|(modbus.read()<<16)|(modbus.read()<<8)|modbus.read()) ); } Serial.println( "Остальные байты данных:" ); while( modbus.available() ){ Serial.println( modbus.read() ); } }else{ Serial.println("Error"); // Модуль не поддерживает чтение информации о устройстве. }
Функция lastError();
- Назначение: Получение кода причины последней ошибки.
- Синтаксис: lastError();
- Параметры: Нет.
- Возвращаемое значение: uint8_t — код причины последней ошибки.
- ERROR_ILLEGAL_FUNCTION — Команда запроса не поддерживается модулем.
- ERROR_ILLEGAL_ADDRESS — У модуля отсутствует регистр с указанным адресом.
- ERROR_ILLEGAL_VALUE — Недопустимое значение в поле данных запроса.
- ERROR_DEVICE_FAILURE — Любая невосстановимая ошибка кроме первых трёх.
- ERROR_ACKNOWLEDGE — Модуль принял запрос, но на обработку требуется время.
- ERROR_DEVICE_BUSY — Ведомый занят, запрос проигнорирован.
- ERROR_MEMORY_PARITY — Ошибка чтения/записи файла.
- ERROR_GATEWAY_UNAVAILABLE — Шина перегружена данными или не настроена.
- ERROR_GATEWAY_NO_DEVICE — Ведомого устройства нет или от него нет ответа.
- ERROR_SYNTAX — Ошибка синтаксиса.
- ERROR_ADR_IARDUINO — Ошибка назначения или сортировки адресов устройств iarduino.
- ERROR_ADR_RESPONSE — Несовпадение адреса регистра в ответе.
- ERROR_VAL_RESPONSE — Несовпадение данных в ответе.
- ERROR_CRC_RESPONSE — Несовпадение CRC в принятом ответе.
- ERROR_LEN_REQUEST — Размер отправляемого запроса превышает размер буфера.
- ERROR_LEN_RESPONSE — Размер полученного ответа превышает размер буфера.
- Пример:
int8_t i = modbus.coilRead(9, 100); // Читаем из модуля с адресом 9 регистр "Coil" (DO) с адресом 100. if( i<0 ){ switch( modbus.lastError() ){ case ERROR_ILLEGAL_FUNCTION: Serial.println( F("Модуль не поддерживает команду или функцию запроса") ); break; case ERROR_ILLEGAL_ADDRESS: Serial.println( F("Запрос содержит недопустимый адрес регистра") ); break; case ERROR_ILLEGAL_VALUE: Serial.println( F("Запрос содержит недопустимое значение в поле данных") ); break; case ERROR_DEVICE_FAILURE: Serial.println( F("Ошибка запроса не связана с командой, адресом или данными") ); break; case ERROR_ACKNOWLEDGE: Serial.println( F("Модуль сообщает что занят и обработает запрос позже") ); break; case ERROR_DEVICE_BUSY: Serial.println( F("Модуль сообщает что занят и не будет обрабатывать запрос") ); break; case ERROR_MEMORY_PARITY: Serial.println( F("Модуль сообщает что произошла ошибка чтения/записи файла") ); break; case ERROR_GATEWAY_UNAVAILABLE: Serial.println( F("Шлюз неправильно настроен или перегружен запросами") ); break; case ERROR_GATEWAY_NO_DEVICE: Serial.println( F("Модуль которому адресован запрос не отвечает") ); break; case ERROR_SYNTAX: Serial.println( F("Ошибка синтаксиса") ); break; case ERROR_ADR_IARDUINO: Serial.println( F("Ошибка назначения или сортировки адресов устройств iarduino") ); break; case ERROR_ADR_RESPONSE: Serial.println( F("Ответ от модуля содержит недопустимый адрес регистра") ); break; case ERROR_VAL_RESPONSE: Serial.println( F("Ответ от модуля содержит недопустимое значение в поле данных") ); break; case ERROR_CRC_RESPONSE: Serial.println( F("Несовпадение CRC в ответе модуля") ); break; case ERROR_LEN_REQUEST: Serial.println( F("Размер запроса к модулю превышает буфер обмена или ADU") ); break; case ERROR_LEN_RESPONSE: Serial.println( F("Размер ответа от модуля превышает буфер обмена") ); break; default: Serial.println( F("Неизвестная ошибка") ); break; } }else{ Serial.println( F("Ошибки не обнаружены") ); }
Функции для работы с адресами:
Функции перечисленные в данном разделе предназначены для устройств iarduino.
Функция checkID();
- Назначение: Функция определения принадлежности устройства.
- Синтаксис: checkID( АДРЕС_МОДУЛЯ );
- Параметр: АДРЕС_МОДУЛЯ uint8_t — значение от 1 до 247.
- Возвращаемое значение: uint8_t — принадлежность устройства:
- DEVICE_MB_ABSENT — Устройство с указанным адресом отсутствует на шине.
- DEVICE_MB_DOUBLE — Адрес принадлежит двум и более устройствам.
- DEVICE_MB_IARDUINO — Устройство принадлежит линейке iarduino.
- DEVICE_MB_UNKNOWN — Устройство не принадлежит линейке iarduino.
- Пример:
uint8_t i = modbus.checkID(9); // Определяем принадлежность устройства с адресом 9. Serial.print(F("Адрес 9 на шине Modbus принадлежит ")); switch(i){ case DEVICE_MB_ABSENT: Serial.println(F("отсутствующему устройству." )); break; case DEVICE_MB_DOUBLE: Serial.println(F("двум и более устройствам." )); break; case DEVICE_MB_IARDUINO: Serial.println(F("устройству линейки iarduino.")); break; case DEVICE_MB_UNKNOWN: Serial.println(F("устройству не iarduino." )); break; }
Функция changeID();
- Назначение: Функция изменения адреса устройства iarduino на шине.
- Синтаксис: changeID( СТАРЫЙ_АДРЕС , НОВЫЙ_АДРЕС );
- Параметры:
- СТАРЫЙ_АДРЕС: uint8_t — значение от 1 до 247.
- НОВЫЙ_АДРЕС: uint8_t — значение от 1 до 247.
- Возвращаемое значение: bool — результат изменения адреса (true или false).
- Примечание:
- Новый адрес сохраняется в энергонезависимую память устройства.
- Выполнение функции занимает более 120 мс.
- Пример без проверки:
modbus.changeID(9, 10); // Меняем адрес устройства с 9 на 10.
- Пример с проверкой:
bool i = modbus.changeID(9, 10); // Меняем адрес устройства с 9 на 10. if(i){ Serial.println( "Ok" ); } // Выводим сообщение о успешной смене адреса. else { Serial.println("Error"); } // Выводим сообщение о ошибке смены адреса.
Функция sortID();
- Назначение: Функция сортировки адресов устройств iarduino по порядку.
- Синтаксис: sortID( ПЕРВЫЙ_АДРЕС_СПИСКА );
- Параметр: ПЕРВЫЙ_АДРЕС_СПИСКА uint8_t — значение от 1 до 247.
- Возвращаемое значение: int16_t — количество изменённых адресов, или -1 при неудаче.
- Примечание:
- Функция присваивает всем устройствам iarduino новые адреса по порядку.
- Функция работает с устройствами iarduino даже если у них одинаковые адреса.
- Функция пропускает адреса принадлежащие устройствам не iarduino.
- Новые адреса сохраняются в энергонезависимую память устройств iaduino.
- Выполнение функции занимает несколько секунд.
- Пример без проверки:
modbus.sortID(10); // Меняем адреса устройств в список начинающийся с адреса 10 (например, для трёх устройств, адреса станут: 10,11,12).
- Пример с проверкой:
int i=modbus.sortID(10); // Меняем адреса устройств в список начинающийся с адреса 10 (например, для трёх устройств, адреса станут: 10,11,12). if(i<0){Serial.println( "Не удалось отсортировать все адреса устройств" ); } else {Serial.println( (String) "Изменены адреса "+i+" устройств" ); }
Функция searchERR();
- Назначение: Обнаружение устройств iarduino с одинаковыми адресами.
- Синтаксис: searchERR();
- Параметры: Нет.
- Возвращаемое значение: uint8_t — количество адресов, доступных для чтения функцией read().
- Примечание:
- Для получения найденных адресов нужно обращаться к функции read().
- Количество оставшихся доступных данных можно узнать функцией available().
- Выполнение функции занимает несколько секунд.
- Пример:
uint8_t i = modbus.searchERR(); // Ищем адреса принадлежащие двум и более устройствам. Serial.println( (String) "На шине есть "+i+" адресов принадлежащих нескольким устройствам" ); // Выводим найденные адреса: while( modbus.available() ){ Serial.println( modbus.read() ); }
Функция findID();
- Назначение: Обнаружение всех адресов ведомых устройств на шине.
- Синтаксис: findID( [ МОДЕЛЬ ] );
- Параметр: МОДЕЛЬ uint8_t — идентификатор искомых устройств (от 1 до 255).
- Возвращаемое значение: uint8_t — количество адресов, доступных для чтения функцией read().
- Примечание:
- Для получения найденных адресов нужно обращаться к функции read().
- Количество оставшихся доступных данных можно узнать функцией available().
- Если вызвать функцию без аргумента то будут обнаружены все адреса всех устройств.
- Если вызвать функцию с идентификатором устройства, то будут найдены только те адреса, которые принадлежат устройствам iarduino указанной модели.
- Выполнение функции занимает несколько секунд.
- Пример обнаружения всех адресов:
uint8_t i = modbus.findID(); // Ищем все адреса всех устройств (даже не iarduino). Serial.println( (String) "На шине есть "+i+" устройств с адресами:" ); // Выводим найденные адреса: while( modbus.available() ){ Serial.println( modbus.read() ); }
- Пример обнаружения адресов принадлежащих устройствам iarduino указанной модели:
uint8_t i = modbus.findID(1); // Ищем адреса устройств iarduino с идентификатором 1. Serial.println( (String) "На шине есть "+i+" устройств с адресами:" ); // Выводим найденные адреса: while( modbus.available() ){ Serial.println( modbus.read() ); }
- Пример получения идентификатора (номера модели) устройства iarduino:
uint8_t i; if( modbus.getInfo(9) ){ // Читаем информацию о устройстве с адресом 9. modbus.read(); modbus.read(); modbus.read(); // Пропускаем ненужные данные. if( modbus.available() ){ i = modbus.read(); } // Получаем идентификатор устройства. }
Ссылки:
- Библиотека iarduino_Modbus.
From this article you will learn about the Modbus RTU protocol, which is widely used in the process control system.
Contents:
- Modbus RTU protocol description
- What are Modbus RTU commands?
- How can I send a Modbus RTU command to read discrete output? Command 0x01
- How can I send a Modbus RTU command to read a digital input? Command 0x02
- How can I send a Modbus RTU command to read analog output? Command 0x03
- How can I send the Modbus RTU command to read the analog input? Command 0x04
- How can I send a Modbus RTU command to write discrete output? Command 0x05
- How can I send a Modbus RTU command to record analog output? Command 0x06
- How can I send a Modbus RTU command to write multiple discrete pins? Command 0x0F
- How can I send a Modbus RTU command to record multiple analog outputs? Command 0x10
- What are the errors of the Modbus request?
- Programs for working with Modbus RTU protocol
- Equipment with Modbus RTU support
Modbus RTU protocol description
Modbus -communication protocol is based on the master-slave architecture. It uses RS-485, RS-422, RS-232 interfaces, as well as Ethernet TCP / IP networks (Modbus TCP protocol) for data transfer.
The Modbus RTU message consists of the address of the SlaveID device, the function code, the special data, depending on the function code and the CRC of the checksum.
SlaveID | Function code | Special data | CRC |
If you discard the SlaveID address and the CRC checksum, you get the PDU, Protocol Data Unit.
SlaveID is the address of the device, it can take a value from 0 to 247, addresses from 248 to 255 are reserved.
Data in the module is stored in 4 tables.
Two tables are read-only and two are read-write.
9999 values are placed in each table.
REGISTER NUMBER | REGISTER ADDRESS HEX | TYPE | NAME | TYPE |
---|---|---|---|---|
1-9999 | 0000 to 270E | read-write | Discrete Output Coils | DO |
10001-19999 | 0000 to 270E | read | Discrete Input Contacts | DI |
30001-39999 | 0000 to 270E | read | Analog Input Registers | AI |
40001-49999 | 0000 to 270E | read-write | Analog Output Holding Registers | AO |
read-write
The Modbus message uses the register address.
For example, the first register of AO Holding Register has the number 40001, but its address is 0000.
The difference between these two quantities is “offset”.
Each table has its own offset, respectively: 1, 10001, 30001 and 40001.
The following is an example of a Modbus RTU request for obtaining the AI value of the holding registers from registers # 40108 to 40110 with the address of the device 17.
11 03 006B 0003 7687
11 | THE ADDRESS OF THE SLAVEID DEVICE (17 = 11 HEX) |
03 | Functional code Function Code |
006B | The address of the first register (40108-40001 = 107 = 6B hex) |
0003 | The number of required registers (reading 3 registers from 40108 to 40110) |
7687 | CRC checksum |
In response to the Modbus RTU Slave device we get:
11 03 06 AE41 5652 4340 49AD
Where:
11 | DEVICE ADDRESS (17 = 11 hex) | SlaveID |
03 | Function code | Function Code |
06 | The number of bytes further (6 bytes follow) | Byte Count |
AE | The value of the upper register bit (AE hex) | Register value Hi (AO0) |
41 | The low-order bit of the register (41 hex) | Register value Lo (AO0) |
56 | The value of the upper register bit (56 hex) | Register value Hi (AO1) |
52 | The low-order bit of the register (52 hex) | Register value Lo (AO1) |
43 | The value of the upper register bit (43 hex) | Register value Hi (AO2) |
40 | The low-order bit of the register (40 hex) | Register value Lo (AO2) |
49 | Checksum | CRC value Hi |
AD | Checksum | CRC value Lo |
The value of the upper register bit
The analog output register AO0 has the value AE 41 HEX or 44609 in the decimal system.
The analog output register AO1 has a value of 56 52 HEX or 22098 in the decimal system.
The analog output register AO2 has a value of 43 40 HEX or 17216 in the decimal system.
The AE 41 HEX value is 16 bits 1010 1110 0100 0001, can take a different value, depending on the type of representation.
The value of register 40108 when combined with register 40109 gives a 32 bit value.
An example of a representation.
View type | Value range | Example in HEX | In decimal form |
---|---|---|---|
16-bit unsigned integer | 0 to 65535 | AE41 | 44,609 |
16-bit signed integer | -32768 to 32767 | AE41 | -20,927 |
two character ASCII string | 2 char | AE41 | ® A |
discrete on/off value | 0 and 1 | 0001 | 0001 |
32-bit unsigned integer | 0 to 4,294,967,295 | AE41 5652 | 2,923,517,522 |
32-bit signed integer | -2,147,483,648 to 2,147,483,647 | AE41 5652 | -1,371,449,774 |
32-bit single precision IEEE floating point number | 1,2·10−38 to 3,4×10+38 | AE41 5652 | -4.395978 E-11 |
four character ASCII string | 4 char | AE41 5652 | ® A V R |
Back to contents
What are Modbus RTU commands?
Here is a table with the codes for reading and writing the Modbus RTU registers.
FUNCTION CODE | WHAT THE FUNCTION DOES | VALUE TYPE | ACCESS TYPE | |
---|---|---|---|---|
01 (0x01) | Read DO | Read Coil Status | Discrete | Read |
02 (0x02) | Read DI | Read Input Status | Discrete | Read |
03 (0x03) | Read AO | Read Holding Registers | 16 bit | Read |
04 (0x04) | Read AI | Read Input Registers | 16 bit | Read |
05 (0x05) | Write one DO | Force Single Coil | Discrete | Write |
06 (0x06) | Write one AO | Preset Single Register | 16 bit | Write |
15 (0x0F) | Multiple DO recording | Force Multiple Coils | Discrete | Write |
16 (0x10) | Multiple AO recording | Preset Multiple Registers | 16 bit | Write |
Back to contents
How can I send a Modbus RTU command to read discrete output? Command 0x01
This command is used to read the values of the DO digital outputs.
The PDU request specifies the start address of the first DO register and the subsequent number of required DO values. In the PDU, the DO values are addressed starting from zero.
The DO values in the response are in one byte and correspond to the value of the bits.
The bit values are defined as 1 = ON and 0 = OFF.
The low bit of the first data byte contains the DO value whose address was specified in the request. The remaining values of DO follow the increasing value to the highest value of the byte. Those. from right to left.
If less than eight DO values were requested, the remaining bits in the response will be filled with zeros (in the direction from the low to high byte). Byte Count The number of bytes further indicates the number of full bytes of data in the response.
Example of a DO query from 20 to 56 for the device’s SlaveID address 17. The address of the first register will be 0013 hex = 19, because The account is maintained from 0 address (0014 hex = 20, -1 zero offset = we get 0013 hex = 19).
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
01 | Functional code | 01 | Functional code |
00 | Address of the first register Hi bytes | 05 | Number of bytes more |
13 | Address of the first register Lo bytes | CD | Register value DO 27-20 (1100 1101) |
00 | Number of registers Hi bytes | 6B | Register value DO 35-28 (0110 1011) |
25 | Number of registers Lo bytes | B2 | Register value DO 43-36 (1011 0010) |
0E | Checksum CRC | 0E | Register value DO 51-44 (0000 1110) |
84 | Checksum CRC | 1B | Register value DO 56-52 (0001 1011) |
45 | Checksum CRC | ||
E6 | Checksum CRC |
The output states of DO 27-20 are shown as the values of the byte CD hex, or in the binary system 1100 1101.
In register DO 56-52, 5 bits on the right were requested, and the remaining bits are filled with zeros to the full byte (0001 1011).
Channels | — | — | — | DO 56 | DO 55 | DO 54 | DO 53 | DO 52 |
---|---|---|---|---|---|---|---|---|
Bits | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
Hex | 1B |
Back to contents
How can I send a Modbus RTU command to read a digital input? Command 0x02
This command is used to read the values of digital inputs DI.
Example of a DI request from the registers from # 10197 to 10218 for the device’s SlaveID address 17. The address of the first register will be 00C4 hex = 196, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
02 | Functional code | 02 | Functional code |
00 | Address of the first register Hi bytes | 03 | Number of bytes more |
C4 | Address of the first register Lo bytes | AC | Register value DI 10204-10197 (1010 1100) |
00 | Number of registers Hi bytes | DB | Register value DI 10212-10205 (1101 1011) |
16 | Number of registers Lo bytes | 35 | Register value DI 10218-10213 (0011 0101) |
BA | Checksum CRC | 20 | Checksum CRC |
A9 | Checksum CRC | 18 | Checksum CRC |
Back to contents
How can I send a Modbus RTU command to read analog output? Command 0x03
This command is used to read the values of the analog outputs AO.
Example of an AO request from registers from # 40108 to 40110 for the SlaveID of the device address 17. The address of the first register will be 006B hex = 107, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
03 | Functional code | 03 | Functional code |
00 | Address of the first register Hi bytes | 06 | Number of bytes more |
6B | Address of the first register Lo bytes | AE | Register value Hi #40108 |
00 | Number of registers Hi bytes | 41 | Register value Lo #40108 |
03 | Number of registers Lo bytes | 56 | Register value Hi #40109 |
76 | Checksum CRC | 52 | Register value Lo #40109 |
87 | Checksum CRC | 43 | Register value Hi #40110 |
40 | Register value Lo #40110 | ||
49 | Checksum CRC | ||
AD | Checksum CRC |
Back to contents
How can I send the Modbus RTU command to read the analog input? Command 0x04
This command is used to read the values of analog inputs AI.
Example of an AI request from the register # 30009 for the SlaveID of the device address 17. The address of the first register is 0008 hex = 8, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
04 | Functional code | 04 | Functional code |
00 | Address of the first register Hi bytes | 02 | Number of bytes more |
08 | Address of the first register Lo bytes | 00 | Register value Hi #30009 |
00 | Number of registers Hi bytes | 0A | Register value Lo #30009 |
01 | Number of registers Lo bytes | F8 | Checksum CRC |
B2 | Checksum CRC | F4 | Checksum CRC |
98 | Checksum CRC |
Back to contents
How can I send a Modbus RTU command to write discrete output? Command 0x05
This command is used to record one value of the DO digital output.
The value of FF 00 hex sets the output to ON.
The value 00 00 hex sets the output to OFF.
All other values are invalid and will not be affected by the output value.
The normal response to such a request is an echo (a repeat request in the response), is returned after the DO state has been changed.
An example of a DO record with register # 173 for the SlaveID address of the device 17. The register address will be 00AC hex = 172, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
05 | Functional code | 05 | Functional code |
00 | Address of the first register Hi bytes | 00 | Address of the first register Hi bytes |
AC | Address of the first register Lo bytes | AC | Address of the first register Lo bytes |
FF | Value of Hi bytes | FF | Value of Hi bytes |
00 | Value of Lo bytes | 00 | Value of Lo bytes |
4E | Checksum CRC | 4E | Checksum CRC |
8B | Checksum CRC | 8B | Checksum CRC |
The DO173 output state has changed from OFF to ON.
Back to contents
How can I send a Modbus RTU command to record analog output? Command 0x06
This command is used to record one value of the analog output AO.
Example of recording in AO with register # 40002 for SlaveID address of the device 17. The address of the first register will be 0001 hex = 1, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
06 | Functional code | 06 | Functional code |
00 | Address of the first register Hi bytes | 00 | Address of the first register Hi bytes |
01 | Address of the first register Lo bytes | 01 | Address of the first register Lo bytes |
00 | Value of Hi bytes | 00 | Value of Hi bytes |
03 | Value of Lo bytes | 03 | Value of Lo bytes |
9A | Checksum CRC | 9A | Checksum CRC |
9B | Checksum CRC | 9B | Checksum CRC |
Back to contents
How can I send a Modbus RTU command to write multiple discrete pins? Command 0x0F
This command is used to record multiple values of DO’s digital output.
An example of writing in several DOs with registers from # 20 to # 29 for the SlaveID address of the device 17. The register address will be 0013 hex = 19, since Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
0F | Functional code | 0F | Functional code |
00 | Address of the first register Hi bytes | 00 | Address of the first register Hi bytes |
13 | Address of the first register Lo bytes | 13 | Address of the first register Lo bytes |
00 | Number of registers Hi bytes | 00 | Number of recorded registers Hi bytes |
0A | Number of registers Lo bytes | 0A | Number of recorded registers Lo bytes |
02 | Number of bytes more | 26 | Checksum CRC |
CD | Byte Value DO 27-20 (1100 1101) | 99 | Checksum CRC |
01 | Byte Value DO 29-28 (0000 0001) | ||
BF | Checksum CRC | ||
0B | Checksum CRC |
The answer returns the number of registers recorded.
Back to contents
How can I send a Modbus RTU command to record multiple analog outputs? Command 0x10
ЭThis command is used to record multiple values of the analog output AO.
An example of recording in several AO with registers # 40002 and # 40003 for the SlaveID address of the device 17. The address of the first register will be 0001 hex = 1, because Account is maintained from 0 address.
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
11 | Device address | 11 | Device address |
10 | Functional code | 10 | Functional code |
00 | Address of the first register Hi bytes | 00 | Address of the first register Hi bytes |
01 | Address of the first register Lo bytes | 01 | Address of the first register Lo bytes |
00 | Number of registers Hi bytes | 00 | Number of recorded registers Hi bytes |
02 | Number of registers Lo bytes | 02 | Number of recorded registers Lo bytes |
04 | Number of bytes more | 12 | Checksum CRC |
00 | Value Hi 40002 | 98 | Checksum CRC |
0A | Value Lo 40002 | ||
01 | Value Hi 40003 | ||
02 | Value Lo 40003 | ||
C6 | Checksum CRC | ||
F0 | Checksum CRC |
Back to contents
What are the errors of the Modbus request?
If the device receives a request, but the request can not be processed, the device will respond with an error code.
The response will contain the modified Function code, the high-order bit will be 1.
Example:
IT WAS | IT BECOME |
---|---|
FUNCTIONAL CODE IN REQUEST | Functional error code in response |
01 (01 hex) 0000 0001 | 129 (81 hex) 1000 0001 |
02 (02 hex) 0000 0010 | 130 (82 hex) 1000 0010 |
03 (03 hex) 0000 0011 | 131 (83 hex) 1000 0011 |
04 (04 hex) 0000 0100 | 132 (84 hex) 1000 0100 |
05 (05 hex) 0000 0101 | 133 (85 hex) 1000 0101 |
06 (06 hex) 0000 0110 | 134 (86 hex) 1000 0110 |
15 (0F hex) 0000 1111 | 143 (8F hex) 1000 1111 |
16 (10 hex) 0001 0000 | 144 (90 hex) 1001 0000 |
Sample request and response with error:
BYTE | REQUEST | BYTE | ANSWER |
---|---|---|---|
(Hex) | Field name | (Hex) | Field name |
0A | Device address | 0A | Device address |
01 | Functional code | 81 | Functional code with changed bit |
04 | Address of the first register Hi bytes | 02 | Error code |
A1 | Address of the first register Lo bytes | B0 | Checksum CRC |
00 | Number of registers Hi bytes | 53 | Checksum CRC |
01 | Number of registers Lo bytes | ||
AC | Checksum CRC | ||
63 | Checksum CRC |
Explanation of error codes
01 | FUNCTION CODE ACCEPTED CAN NOT BE PROCESSED. |
02 | The data address specified in the request is not available. |
03 | The value contained in the query data field is an invalid value. |
04 | An unrecoverable error occurred while the slave attempted to perform the requested action. |
05 | The slave has accepted the request and processes it, but it takes a long time. This response prevents the host from generating a timeout error. |
06 | The slave is busy processing the command. The master must repeat the message later when the slave is freed. |
07 | The slave can not execute the program function specified in the request. This code is returned for an unsuccessful program request using functions with numbers 13 or 14. The master must request diagnostic information or error information from the slave. |
08 | The slave detected a parity error when reading the extended memory. The master can repeat the request, but usually in such cases, repairs are required. |
Back to contents
Programs for working with Modbus RTU protocol
The following are the programs that make it easier to work with Modbus.
DCON Utility Pro with support for Modbus RTU, ASCII, DCON. Download
Modbus Master Tool with support for Modbus RTU, ASCII, TCP. Download
Modbus TCP client with Modbus TCP support. Download
Back to contents
Equipment with Modbus RTU support
Gateway
RS-485 Modules M-7000
RS-485 Modules tM
RS-485 Modules ADAM-4000
RS-485 / RS-232 Modules SCM9B
RS-485 Control Modules Lighting LC
RS-485 Infrared IR Modules
Compact Terminal TPD and VPD
Temperature and humidity meters
Back to contents