Описание протокола 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(); } // Получаем идентификатор устройства. }
Обсуждение