Modbus Slave на Arduino
KRModbusSlave
KRModbusSlave - родительский класс для классов KRModbusRTUSlave и KRModbusASCIISlave. Содержит основные параметры.Конструктор
Методы
KRRegisters
KRRegisters - родительский класс для обработчика данных.Методы
KRModbusRTUSlave
KRModbusRTUSlave - обработчик пакетов Modbus RTU. Является наследником KRModbusSlave и перегружает только метод _DO().KRModbusASCIISlave
KRModbusASCIISlave - обработчик пакетов Modbus ASCII. Является наследником KRModbusSlave и перегружает только метод _DO().KRModbusRTUSlaveRS485
KRModbusRTUSlaveRS485 - обработчик пакетов Modbus RTU с поддержкой преобразователя UART TTL-RS485. Является наследником KRModbusSlave и перегружает только метод _DO().Конструктор
Пример использование
Для примера будем передавать данные из контроллера в программу на Delphi.
Создаем обработчик данных на базе класса KRRegisters. В нем объявляются те данные, которые должны быть доступны посредством протокола Modbus. Для примера будем использовать локальные переменные следующих типов: word, int, uint32_t, long, float и строку. Строка в Arduino будет представлена в виде побайтового массива, это поможет избежать проблем с кодировкой. Еще будем передавать массив регистров и данные из EEPROM.
В проекте создадим обработчик данных и обработчик протокола Modbus. В зависимости от того, какого типа Modbus (RTU или ASCII) необходим, подключаем соответствующий обработчик. Для того, что бы была видна динамика изменения данных будем менять их значения через определенный период времени.
Форма программы на Delphi

Программа на Delphi будет отображать значения переменных из контроллера с периодом обновления 2 раза в секунду. А так же будет возможность изменять значения переменных. С помощью выпадающего списка в верхнем правом углу формы устанавливаем тип протокола RTU или ASCII.
Оптимизация
Добавил оптимизированные варианты библиотек:
ModbusRTUTestUART0ModbusRTUTestRS485UART0
Библиотеки привязаны к порту UART0, и для передачи данных не используется стандартные средства управления аппаратными UART портами. Управление портом осуществляется напрямую, через регистры.
ModbusRTUTestUART0minModbusRTUTestRS485UART0min
Алгоритм обработки протокола упрощен, но при этом функционал не меняется. Подробнее рассказывается в видео ролике.
Скачать:
![]() | Набор библиотек KRModbus для контроллеров Arduino от 30.07.2019 |
![]() | Программа для тестирования библиотек Modbus на контроллерах Arduino |
изменяются значения в пяти верхних окнах левой колонки, остальные значения неизменны, нижнее окно
правой колонки пустое, нижнее окно средней колонки закрашено черным.
при отключении Ардуино надпись "No Connection", закрашиваются черным все окна левой колонки,
нижнее окно средней колонки и верхнее окно правой колонки.
При компиляции файлов из папки source одинаково для Win64 и Win32 выдает ошибку
"Unit 'KRCOMPortSets' not found". При удалении KRCOMPortSets из Uses ошибок становится больше.
Как лечить проблему? Может быть, надо взять более старую версию компонентов? Одновременно для
Delphi и Arduino? Какую и где? Или есть решение поизящней?
SoftwareSerial Serial1(10,9); // RX, TX (пробовал другое название порта не помогло)
KRModbusRTUSlaveRS485 mb(Serial1,data,3,3);
так не работает, работает если только в первом параметре стоит Serial
пробовал и так
SoftwareSerial Serial21(10,9)
KRModbusRTUSlave mb(Serial21,data,3); что-то все равно не получается, наверное все таки что то делаю не так?
и еще.. есть такой преобразователь
Я использую преобразователь на чипе MAX485
Обновленные библиотеки:
Пример ModbusRTURS485:
Data50 и Data51 - это тот же класс Data, только в них используются регистры 50 и 51 соответственно.
Нет. Так как пакет из буфера порта будет забирать только один из объектов, какой успеет.
1. Программа для тестирования не работает под win7 (64), даже под администратором, не видит com port, другое ПО видит.
2. РТУ Модбас подразумевает использование только 1 битных и 16 битных переменных, которые четко распределены по адресному пространству (дискретные, коилы, инпуты, холдинги). Нумерация каждого из типов начинается с 0 адреса, при этом для исключения пересечения адресов МК должен автоматически разбрасывать данные по разным массивам в зависимости от пренадлежности к определенным данным (например коил и холдинг с адресом 0 должны находится в разных массивах). Не увидел этой реализации в данной библиотеке.
3. Слишком мало коментариев в коде и скудно описана библиотека. Наример нет четкого описания - какие прерывания используются, таймеры и другой функционал, который может быть затронут пользователем для реализации своих других функций. Нет полного описания переменных доступных для работы с библиотекой и их организацие и т.д.
Например задача: Есть устройство Мастер какое-то- есть слейв МК например на АВР 2560 - МК выполняет кучу разностей - работа с двигателями, датчиками, ацп и т.п. и обменивается с мастером посредством Модбас РТУ, имея по 10 регистров коилов, 10 холдингов, 10 инпутов. Физический адрес каждого из типов начинается с 0, логический с 00001, 30001, 40001. запись/чтение доступно только для инпутов, остальные только чтение.
2. В данной библиотеке используются только 16 битные регистры и обрабатываются только следующие функции:
Read Holding Registers (03H)
Read Input Registers (04H)
Write Holding Register (06H)
Write Holding Registers (10H)
Изначально, когда я писал эти библиотеки, задача была не привязываться к каким либо данным(массивам). Чтобы программист сам решал какую именно информацию отправлять по определенному адресу и куда ее сохранять. Если вы хотите чтобы вся передаваемая информация хранилась в массивах это довольно просто реализовать. Пример:
3. Библиотека очень проста и весь функционал умещается в одном методе _DO(). Вот последовательность выполняемых действий метода _DO():
1) Проверка наличия данных в порту. Если буфер пуст, то возвращаем управление(return).
2) Если время после последнего прихода данных не превысило waitRespTime и время с прихода первого байта не превысило tmReadTimeout, то возвращаем управление.
3) Считывание данных с буфера порта с параллельным подсчетом контрольной суммы
4) Если контрольная сумма не соответствует двум последним байтам пакет, то возвращаем управление.
5) Если номер функции не соответствует 03H, 04H, 06H, 10H, то отправляем ошибку мастеру и возвращаем управление
6) Если пришел запрос чтения (03H, 04H), то вызываем метод
uint16_t Data::read(uint16_t index)
Метод read может вызываться несколько раз подряд, в зависимости от того, сколько регистров нужно считать. Полученные значения отправляются мастеру
7) Если пришел запрос записи (06H, 10H), то вызываем метод
void Data::write(uint16_t index, uint16_t value)
Метод write также может вызываться несколько раз подряд, в зависимости от того, сколько регистров нужно записать. Мастеру отправляется положительный ответ
При желании функционал с дискретными данными можно дописать.
2. Протокол Модбас достаточно стандартизирован, отклонение от его стандартов - это другой протокол, возможно основанный на модбас, но другой. (
2. Протокол передачи данных - это набор правил согласно которым эти данные передаются. В моей библиотеки выдержаны правила передачи данных. Что именно передавать решает программист, использующий библиотеку.
2. Правила для modbus закрепелны не только для передачи данных но и для формирования данных которые передаются: начиная от группировку данных по их типам (регистры 00000-40000, офсет адресов перед передачей), формирования пакета от мастера к слейву и обратно, временных и скоростных характеристик передачи-частота опроса мастером, тайм аут, паузы между запросами, поведение если в сети несколько слейвов, расшифровка посылок, распределение их по регистрам согласно правилам формирования регистров модбас.
Для работы тестовой программы/скетча нужно:
1) Открыть скетч ModbusRTUTestRS485 и указать нужный порт и номер дискретного входа к которому подключен контакт TXEN(на преобразователях MAX485 контакты DE и RE замыкаются на этот вход)
KRModbusRTUSlaveRS485 mb(/*Порт*/Serial,data,/*TXEN*/2,1);
2) В строке
Serial.begin(9600);
указать нужную скорость передачи данных
3) Залить скетч в контроллер
4) Запустить тестовую программу на компьютере, настроить порт и в выпадающем списке(верхний правый угол) выбрать Modbus RTU. Затем нажать кнопку Connect
Если связи не будет, то попробуйте использовать программы для тестирования Modbus(например qmodbus), чтобы определить в чем проблема в программе или в отсутствии данных с контроллера.
2. Давайте не будем спорить по этому поводу. Сейчас многие производители приборов и контроллеров используют протокол Modbus только как инструмент передачи данных. И зачастую им даже не важно какими именно функциями вы будите считывать (03H, 04H) или записывать (06H, 10H) данные. Моя задача была реализовать передачу данных с максимально быстрой обработкой пакетов и с минимальными затратами ресурсов контроллера
Лучшую, в плане реализации (в плане быстродействия не проверял еще) бибилотеку под модбас_РТУ для слейва привел в ссылке выше.
Я обычно сам пишу на LabView все подряд под win при необходимости.
procedure BytesFromWord(wd: Word; out bt0,bt1: byte);
begin
bt0:=wd and $FF ; // Igor - было bt0:=wd
-----------------------
function MBMASCIIReadHoldingRegisters(ADevAddr: byte; AStartReg, ACount: Word;
ABuffer: PKRBuffer; out ARecvLen: Byte): Byte;
....
// bt:=-WordRec(wd).Lo; // Похоже $FF - X 18 08 2019 Igor
bt:= $FF -WordRec(wd).Lo;
В следующей версии компонентов будут эти изменения
Здорово! Спасибо большеое автору а то иначе ничего не найти в инете для modbus и Delphi!
Form1.KRCOMPortConnector1.OnSendAsync:=send; типы пааметов не совпадают byte и integer.
Было бы очень здорово если бы вы выложили верисю + демо под XE10.2 или XE10.3
Form1.KRCOMPortConnector1.OnSendAsync:=send;
Это связано с тем, что в новой версии(Kandiral_23.10.2018.zip) тип события OnSendAsync изменился.
Пакет библиотек и тестовую программу перезалил.
Для KRModbusRTUSlaveRS485.h работает нормально У меня есть скрины ноя не знаю как суда вставить. Заранее Спасибо! Может чего подскажете? Причем интересно, что через преобразователь RS232-RS485 на терминал компа работает нормально.
Работает нормально.
Что-бы вставить картинку, нужно ее загрузить на любой файлообменник или в облако, а в комментарий добавить ссылку.
Завтра протестирую
TKRCOMPortSets does not contain a member named 'Init' как решить подскажите пожалуйста.
Ваше приложение ModbusTester.exe работает и успешно сконнэктилось с FX3U по Modbus через переходник.
Tools->Options->Enviroment Options->Delphi Options->Library
И еще - при установке компонент можно выбрать d210 или d150. Я установила d210. Это правильно? и в чем разница?
Пути ко всем папкам и подпапкам из архива (Kandiral_01.03.2018\Kandiral) прописаны в Tools->Options->Enviroment Options->Delphi Options->Library
Версия Delphi 10.2 Tokyo
Соответственно это delphi XE и XE7. Если у вас Delphi XE7 или выше, нужно ставить d210. В противном случае d150.
Что именно выдает компилятор? Если он не может найти указанные классы, значит вы не правильно указали пути к библиотекам.
Вот последняя компонентов:
Перед установкой нужно деинсталировать старые компоненты
Вот краткая инструкция по установке
Все удалила и установила заново. Теперь работает.
Свойство StrLen
В таких случаях удобно использовать мониторинг как в примере, где видны исходящие и входящие пакеты.
И как я понял для отображения вы используете компонент KRField. У него есть свойство ErrorToHint, если его включить, то в момент когда поле черное достаточно навести указатель мышки и выскочит всплывающая подсказка с текущей ошибкой.
В библиотеках с RS485 в названии сначала идет номер TXEN входа, а затем адрес
Какую именно библиотеку вы используете?
KRModbusRTUSlaveRS485 mb(Serial,data,TXEN,1); - в этой строчке сложно ошибиться)
Если успею сегодня, если нет, тогда уже завтра
Я так понял вы про Delphi, а не про Arduino.
На восьмой минуте начинается про TModbusClient
Я в примерах на Delphi обычно делаю форму мониторинга пакетов, на которой отображается содержимое исходящих и входящих пакетов. Там можно проверить на сколько правильные идут пакеты. В ModbusRTU первый байт - это адрес устройства и в исходящем, и во входящем пакетах.
По идее библиотека KRModbusRTUSlave.h должна была работать с AltSoftSerial.
Вот сделал библиотеку заточенную под AltSoftSerial
Но я ее не тестировал, так как сейчас нет возможности. На выходных проверю.
8 - RX
9 - TX
volatile добавляется к тем переменным, которые используются и в основном потоке и в параллельном. Конкретно в этой библиотеке не используются прерывания и параллельные потоки, по этому добавлять volatile нет ни какого смысла.
AltSoftSerial в разных контроллерах использует разные дискретные входа
//
// Board Transmit Receive PWM Unusable
// ----- -------- ------- ------------
// Teensy 3.0 & 3.1 21 20 22
// Teensy 2.0 9 10 (none)
// Teensy++ 2.0 25 4 26, 27
// Arduino Uno 9 8 10
// Arduino Leonardo 5 13 (none)
// Arduino Mega 46 48 44, 45
// Wiring-S 5 6 4
// Sanguino 13 14 12
Вы к каким подключились?
Если же есть вероятность переполнения буфера, то можно пропускать такие запросы следующей проверкой
По поводу несуществующего регистра. Класс KRModbusRTUSlaveRS485 ничего не считывает и не записывает. Для этих действий он вызывает виртуальные методы класса KRRegisters write и read. В примере эти методы описываются в классе Data. Если у вас зависает контроллер при обращении к несуществующему регистру, то нужно искать проблему в коде этих методов.
Но вместо элемента "НЕ" Который стоит перед "TX". Изменил SoftwareSerial.cpp. Инвертировал просто.
Вот здесь:
И вот здесь:
Данное решение позволила упростить схему и использовать стандартный переходник который можно купить на алиэкспресс. Хочу пояснить. (не реклама!!!) У меня вот этот переходник
Обновите эти файлы
в папке \Kandiral\Automation\ и перекомпилируйте набор компонентов
Мой вариант Modbus Slave для Arduino работает быстрее SimpleModbusSlave и использует меньше ресурсов контроллера.
Если все так просто, как в видео то с меня лайки под все твои видео. А то я с другими библиотеками ModBus 4-й день бьюсь головой об стену.