КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Работа с протоколом Modbus RTU/ASCI по шине RS485

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

Ссылки:




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями