Пример передачи данных по протоколу Modbus на Delphi

 

Для примера будем использовать контроллер ОВЕН ПЛК100. Для использования Modbus ASCII или Modbus RTU нужно подключить контроллер к компьютеру через COM порт, а для Modbus TCP по сети.

В конфигурации ПЛК добавляем компонент Modbus(slave). В группе Modbus[FIX] добавляем компонент соответствующий той связи которая соединяет компьютер с контроллером. Далее в группе Modbus(slave) добавляем регистры как показано на картинке

Компонент Modbus c набором переменных в конфигурации контроллера
Переменные Modbus
  • wdvr - двухбайтовое без знаковое
  • intvr - двухбайтовое знаковое
  • dwvr - четырех байтовое без знаковое
  • dintvr - четырех байтовое знаковое
  • fltvr - четырех байтовое с плавающей точкой
  • str - изходящая строк
  • str_ - продолжение исходящей строки
  • str2 - входящая строка

Продолжению входящей строки нет необходимости присваивать имя, так как напрямую к этой переменной обращаться не будем.

Программа контроллера будет с периодом 100мс изменять значения всех переменных, что-бы данные изменения наблюдать в программе на Delphi

Создаем VCL приложение в Delphi
Программа на Delphi для примера Modbus соединения

Добавляем на форму

  • StatusBar с одним текстовым полем
  • KRTimer, в свойствах Interval=500
  • KRCOMPortConnector, в свойствах устанавливаем необходимые настройки подключения
  • KRSpeeInfo, в свойстве Component выбираем коннектор
  • KRModbusMaster, в свойствах выбираем коннектор
  • KRModbusClient, в свойствах выбираем компонент KRModbusMaster, устанавливаем адрес устройства единицу и тип Modbus протокола mbtACSII
  • Создаем Modbus переменную, для этого в структуре объектов на компоненте KRModbusClient1 открываем контекстное меню и выбираем пункт "Add". В свойствах переменной устанавливаем Interval=500, MCVarType=MCT_WORD, Name="mb000_wdvr"
  • Создаем Modbus переменную, в свойствах устанавливаем Interval=500, MCVarType=MCT_SMALLINT, Name="mb001_intvr", RegisterIndex=1
  • Создаем Modbus переменную, в свойствах устанавливаем Interval=500, MCVarType=MCT_DWORD, Name="mb002_dwvr", RegisterIndex=2
  • Создаем Modbus переменную, в свойствах устанавливаем Interval=500, MCVarType=MCT_INT, Name="mb004_dintvr", RegisterIndex=4
  • Создаем Modbus переменную, в свойствах устанавливаем Interval=500, MCVarType=MCT_SINGLE, Name="mb006_fltvr", RegisterIndex=6
  • Создаем Modbus переменную, в свойствах устанавливаем Interval=500, MCVarType=MCT_STRING, Name="mb008_str", RegisterIndex=8
  • Создаем Modbus переменную, в свойствах устанавливаем MCVarType=MCT_STRING, Name="mb012_str2", RegisterIndex=12, UpAftWrite=true, WaitForUpdates=true

Если нужно установить соединение по Modbus RTU, то в свойствах компонента KRModbusClient установить MBType=mbtACSII

Если нужно установить связи по Modbus TCP, то вместо коннектора KRCOMPortConnector нужно использовать KRTCPConnector и в свойствах компонента KRModbusClient установить MBType=mbtTCP

Пример передачи данных с контроллера ОВЕН ПЛК100 в программу на Delphi по протоколу Modbus
 
Поделиться:
0
Alexey Novikov
05.01.2023 15:43
 
Здравствуйте вопрос , пытаюсь прикрутить счетчик взлет тэр , 2 регистра : 332801 0x8020 Накопленный прямой объем целая часть, м3 V+

332803 0x8022 Накопленный прямой объем дробная часть, м3
(тип – число с плавающей запятой)
V+ Работа
1. Первый регистр почему то читается с адреса 0x8021 тут какое то не соответствие адресации логической и физической с одной стороны указан 332801 , с другой 0х8020 = 32800 ... читаем 32801:
Первый если читать как int некорректно преобразуется изза лишних байт в которых 00 (AE0000) а реальное значение AE = 174 .
2. второй регистр не читается не с 32803 не с 32802. Подскажите что не так?
Показать полностью
13
Руслан Кандирал
05.01.2023 16:17
Alexey Novikov
 
Здравствуйте! 332801 - это запись в стандарте Modbus данных, где первая цифра указывает на тип регистра ( 3 - input register, 4 - holding register ), остальная часть это адрес регистра плюс один.
То есть 332801 - это input регистр с адресом 0x8020, значение которого можно прочитать функцией 0x04, производить запись в такие регистры нельзя (только для чтения)
Если бы адрес выглядел так 432801, то это holding регистр с адресом 0x8020, значение которого можно прочитать функцией 0x03, а записать функциями 0x06 или 0x10.
А 332803 - это input регистр с адресом 0x8022
Показать полностью
0
Alexey Novikov
05.01.2023 16:42
Руслан Кандирал
 
А мне и нужно только прочитать , но не получается сделать это корректно. Я читаю как input. Ну в принципе интуитивно сделал как Вы и описали , первый читаю int 32800 и single 32802 но в первом не правильное преобразование , а второй вообще не приходит
Показать полностью
13
Руслан Кандирал
05.01.2023 16:58
Alexey Novikov
 
Ещё попробуйте разные варианты со значениями параметров HighByteFirst, HighWordFirst
Они отвечают за последовательность байт и слов при преобразовании
Показать полностью
0
Alexey Novikov
05.01.2023 17:16
Руслан Кандирал
 
Спасибо, с первым регистром сработала комбинация HighByteFirst и HighWordFirst в true, но второй почему так и не идет. Хотя утилита modscan видит переменную, но видит почему то со сдвигом в +2 то что у меня 32800 там 32802 то что дробная часть 32803, но корректно.
Показать полностью
0
Alexey Novikov
05.01.2023 17:32
Alexey Novikov
 
Вобщем интересный глюк , они не читаются именно вместе , убрал у modbusclient первое поле стал читаться второй )) Почему так может быть?
Показать полностью
13
Руслан Кандирал
05.01.2023 17:42
Alexey Novikov
 
Не знаю. Переменные не должны зависеть друг от друга. Попробуйте использовать компонент KRMBMon, что-бы просматривать передаваемые данные
Показать полностью
0
Alexey Novikov
05.01.2023 18:21
Руслан Кандирал
 
он подключен , нет логического объяснения , может слишком быстро опрашивает? Можно как то регулировать интервал между запросами переменных?
Показать полностью
13
Руслан Кандирал
05.01.2023 18:27
Alexey Novikov
 
В коннекторе свойство Interval
Показать полностью
0
Alexey Novikov
05.01.2023 18:36
Руслан Кандирал
 
это между переменными интервал или между повторами опроса всех переменных? поставил 200 стало через раз то нормально то какой то мусор. Видимо между переменными слепляются как то пакеты что ли. И еще вопрос 64 бита поддержки нет и не планируется? если dll компилировать то только под 32 ПО которого все меньше
Показать полностью
13
Руслан Кандирал
05.01.2023 18:55
Alexey Novikov
 
эти библиотеки уже очень устарели, я давно не обновлял. Текущая версия очень сильно изменилась ( что касается modbus ). Как раз сейчас я делаю формирование пакетов modbus на ассемблерных вставках и под x32 и x64. Я из-за этого, в принципе и не обновляю, что не доделан modbus и не доделан инсталятор
Показать полностью
0
Alexey Novikov
05.01.2023 19:03
Руслан Кандирал
 
сейчас уже актуально сразу и под linux ))
Показать полностью
0
Turgun Xujaev
26.07.2022 08:23
 
Здравствуйте, мы купили L2s Laser Distance Sensor который работает через modbus rtu . в инструкция ”L2&L2s Laser Distance Sensor Manual Ver 1.0.pdf” сказано что чтобы получить информации нужно оправить 01 03 00 10 00 02 C5 CE ответ : 01 03 04 00 00 9C 40 92 C3

Как программно отправит запрос и получить ответ (длину).
Спасибо
Показать полностью
13
Руслан Кандирал
26.07.2022 08:34
Turgun Xujaev
 
Здравствуйте!
01 - Адрес устройства
03 - Функция чтения Holding Registers
00 10 - Адрес регистра x0010
00 02 - количество регистров ( тип DWORD )
C5 CE - контрольная сумма

Вам нужен коннектор в зависимости от типа подключения и компонент KRModbusMaster
Показать полностью
0
TeleProg
30.05.2022 20:08
 
Здравствуйте.
Как я понял, программа работает в роли MASTER, а плк в роли SLAVE.
А как поменять их роли, чтобы программа была SLAVE, а плк в роли MASTER?
Может есть пример?
Показать полностью
13
Руслан Кандирал
30.05.2022 20:27
TeleProg
 
Здравствуйте! У меня реализован только Master для Delphi.
Показать полностью
0
Turgun Xujaev
17.01.2022 22:36
 
Здравствуйте, на шести датчиков принимаем значение , отлично, теперь купили 8-канальный RS485 Релейный Модуль , пример программе показана что нужно отправить запрос (01-адресс, 06 – запись, 0001 – номер реле, 0100-включение или 0200- отключение) 010600010100D99A – для включение, 010600010200D96A – для выключение реле. Куда и как послать эти значение, чтобы переключить реле. Спасибо
Показать полностью
13
Руслан Кандирал
21.01.2022 17:21
Turgun Xujaev
 
Здравствуйте!

Вы можете использоваnm описанный в статье пример с коннектором KRComPortConnector.
В компоненте KRModbusClient устанавливаете тип протокола mbtRTU, адрес 01 и удаляете все переменные кроме одной.
В переменной устанавливаете:
Name - KRMBRegister1
MCVarType - MCT_WORD
RegisterIndex - 0001( номер реле )
WriteFunction - mbwfWriteHoldingRegister ( функция записи 06 )

Теперь для включения реле делаем так
KRMBRegister1.Value := $0100;
Показать полностью
0
Turgun Xujaev
26.11.2021 13:08
 
Здравствуйте, Руслан!. как мне программно переключить реле tpm202 , спасибо
Показать полностью
13
Руслан Кандирал
26.11.2021 13:34
Turgun Xujaev
 
Здравствуйте! Я конкретно с этим прибором не работал, но судя по инструкции, сначала нужно перевести канал на внешнее управление ( канал №1 регистр 0x0007 отправить 1), а затем отправить команду включения/выключения ( канал №1 регистр 0x0009 отправить 0 - выключение, 1000 - включение).
Показать полностью
0
Дилшод Искандаров
07.12.2021 07:14
Руслан Кандирал
 
Приветсвую вас Руслан. Хотел бы пообщаться с вами через Телеграм или Скайп. Нужен ваш професиональный совет в решении нашей проблемы.Как можно связаться с вами?
Показать полностью
0
Евгений Чистяков
29.04.2021 14:47
 
Руслан, добрый день. Есть несколько счетчиков СВ01 и преобразователь RS485 to TCP.
Подключаюсь к счетчикам без проблем (KRCOMPortConnector, KRModbusMasterб KRModbusClient). А как считать текущее значение счетчика - просто не понимаю. Подскажите, пожалуйста. Также, хотелось бы производить сброс на 0.
Спасибо.
Показать полностью
13
Руслан Кандирал
29.04.2021 14:56
Евгений Чистяков
 
Здравствуйте, Евгений!
Что вы имеете в виду под фразой "считать текущее значение" ?
Получить значения регистра можно так:
Показать полностью
0
Евгений Чистяков
29.04.2021 15:34
Руслан Кандирал
 
Т.е. я хочу узнать конечную наработку (значение, отображаемое на табло). А как обратиться к счетчику, не знаю. Почему регистр называется mb012_var1? Т.е. где взять название нужного регистра, чтобы считать время?
Показать полностью
13
Руслан Кандирал
29.04.2021 15:37
Евгений Чистяков
 
Название взял для примера. В KRModbusClient вы добавляете регистры и можете задать им любые имена, а в программе по этому имени к ним обращаться.
Показать полностью
0
Евгений Чистяков
29.04.2021 15:42
Руслан Кандирал
 
Руслан, как добавлять регистры, я понял. Я не понял, каким образом взять со счетчика текущую наработку (значение, отображаемое на табло).
Где взять (посмотреть) название регистра или адрес (или еще какой идентификатор), отвечающего за текущую наработку.
Показать полностью
13
Руслан Кандирал
29.04.2021 15:51
Евгений Чистяков
 
В инструкции к прибору есть список регистров.
Если это ваш прибор
https://owen.ru/product/sv01/documentati...

то вам нужно указать
RegisterIndex = 22
MCVarType = MCT_DWORD
Показать полностью
0
Евгений Чистяков
29.04.2021 16:17
Руслан Кандирал
 
Руслан, спасибо большое. Данные получаю, осталось расшифровать. :)
Показать полностью
2
Олег Линник
05.12.2020 22:57
 
Здравствуйте, Руслан!
Как вы смотрите на модернизацию ваших Modbus компонентов? С целью ускорения и улучшения их работы.
Если вы не против, могу прислать Вам свой готовый вариант сделанный из ваших исходников. Вот вкратце что я сделал. Переписал KRThread с использованием событий и WaitForMultiplyObject. Теперь там нет задержек Sleep и Application.ProcessMessages. Переписал KRThreadQueue с использованием событий и критических секций. Теперь события для активации параллельного потока генерирует очередь. Применил эти новые объекты только для компонентов Modbus.
Старался всё сделать максимально аккуратно, чтобы по минимуму затрагивать ваш исходный код.
Но всё таки осмелился переделать принцип с очередями и потоками :)
Остался только один параллельный поток в Коннекторе и в нем же две очереди - входная и выходная. Изолирование данных между процессами происходит в очередях.
Теперь запрос формируется в Мастере (основной поток) и упаковывается в помощнике Мастера (основной поток) -> поступает во входную очередь Коннектора и сигнализирует параллельному потоку Коннектора.
Параллельный поток Коннектора отправляет запрос и получает ответ (через COM-BT-TCP) и добавляет полученный ответ в выходную очередь. Далее через Synchronize происходит извлечение ответа из выходной очереди и вызов Callback функций уже в основном потоке, благодаря чему они стали безопасными.
В результате сократился исполняемый код и время отклика. С параллельными потоками и изолированием данных между ними я дружу давно, так что вряд-ли где-то с ними накосячил.
Я проверил свой вариант на Modbus RTU через COM-порт. Проверить остальное сейчас возможности нет, хотя переделка затронула и BT и TCP коннекторы.
Готов выслать вам свой вариант, если вам это интересно.
Спасибо за внимание и понимание :)
Показать полностью
2
Олег Линник
02.12.2020 19:32
 
Добрый день, Руслан! Продолжаю изучение ваших Modbus компонентов. Есть вопросы. В родительском классе TKRThread.KRExecutePaused; этот метод реально используется в наследниках? Нашёл только использование TKRThread.KRExecutePausedFirst. И общий вопрос каково назначение TKRThread.Active := false кроме остановки выполнения KRExecute, отключения в KRExecutePausedFirst. Ещё какие-то действия? или только это?
Ещё только начал разбираться. Сейчас такое понимание: Каждый регистр имеет три таймера. Основной таймер Interval через обработчик Message заставляет Мастера добавлять в очередь запрос. Таким образом очередь пополняется запросами. Это всё в основном потоке. Кроме этого в Мастере работает параллельный поток который извлекает запросы из очереди и передает их в Коннектор и в коннекторе есть
две свои очереди - входных запросов и выходных ответов. Коннектор и его очереди работает в основном потоке. Кроме этого в коннекторе есть свой параллельный поток, который и обрабатывает очереди.
Если я правильно понял, тогда вопрос: какой смысл в параллельном потоке Мастера, если он только передает запрос из одной очереди в другую?
Показать полностью
13
Руслан Кандирал
03.12.2020 15:36
Олег Линник
 
Здравствуйте, Олег!

TKRThread может находится в активно и неактивном (пауза) состоянии. Изменяется состояние свойством Active

В активном режиме циклично вызывается метод KRExecute, в неактивном KRExecutePaused
При переходи из активного режима в неактивный один раз вызывается KRExecutePausedFirst

В основном, когда я использую TKRThread , то в KRExecute создаются необходимые объекты(соединения) и идет работа с ними, а в KRExecutePausedFirst удаляются(останавливается) все что было сделано(активировано) в KRExecute. KRExecutePaused использую редко.

Каждый регистр имеет три таймера.
Если вы имеете ввиду таймер из TKRVariable
https://github.com/kandiral/Kandiral/blo...
то да. Обработчик таймеров находится тут
TKRVariable.TmWP
Первый таймер для обновления по Interval. Второй и третий для синхронизации с основным потоком при вызове событий OnValUpdated и OnError

Если я правильно понял, тогда вопрос: какой смысл в параллельном потоке Мастера, если он только передает запрос из одной очереди в другую?
В очередь мастеру попадает информация о запросе(TMBPack), а поток мастера извлекает эту информацию, формирует на её основе пакет и отправляет этот пакет в очередь коннектора. Ответный пакет коннектор возвращает мастеру, который проверяет пакет на ошибки, вытаскивает, при необходимости, необходимые данные и вызывает CallBack
Показать полностью
2
Олег Линник
03.12.2020 18:03
Руслан Кандирал
 
Ещё вопрос. А почему в TKRCOMPortThread._exec вы не используете синхронизацию WaitForSingleObject чтобы не использовать тайминги типа WaitRespTime. У вас же в асинхронном порте есть решения для синхронной записи и чтения порта.
Показать полностью
13
Руслан Кандирал
03.12.2020 21:59
Олег Линник
 
Не совсем понял.
Вы могли бы привести код, как вы видите использование WaitForSingleObject вместо таймаута WaitRespTime?
Показать полностью
2
Олег Линник
04.12.2020 00:05
Руслан Кандирал
 
Ваши методы:
function TKRAsyncComPort.Read(var Buffer; Count: Integer): Integer;
который использует
function TKRAsyncComPort.WaitForAsync(var AsyncPtr: PKRACPAsync): Integer;
и тут всё хорошо:
WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, INFINITE);
только можно таймаут задать вместо INFINITE
Тут функция ReadFile выполняется асинхронно, тоесть возвращается сразу и пустая. А потом когда порт считает нужное количество байт возникнет событие AsyncPtr^.Overlapped.hEvent - значит в буфере готовы данные в нужном количестве.
Мне кажется такая реализация более правильная, хотя я не спец в COM-портах, могу ошибаться.
-------------------------
В принципе ваша реализация чтения ответа с порта, хоть и не обычная, но жизнеспособная. И насколько разобрался, большой WaitRespTime не должен снижать скорость, если пакеты не разорванные. Мне только одно не понравилось: Если порт не отвечает (порт же намного медленнее компьютера) то цикл while начинает постоянно долбить COMPort.InputCount и ClearCommError().
Там хотя бы задержку добавить если InputCount = 0.
Вот это место имею ввиду:
while(getTickCount-tm<FWriteTimeout+FReadTimeout)do begin
if FCOMPort.InputCount > 0 then begin
.................................................
end;
if tm0<getTickCount then break;
end;
Тут если порт не сразу отвечает сыпется в порт ClearCommError(). миллион раз в секунду.
Показать полностью
13
Руслан Кандирал
04.12.2020 13:07
Олег Линник
 
Можно использовать синхронный и асинхронный ввод/вывод с порта.

KRComPort - синхронный
KRAsyncComPort - асинхронный

Я использую синхронный режим для контроля всех таймингов самостоятельно. Какова разница в скорости(эффективности), я как-то об этом не задумывался. Может, если будет время, попробую по экспериментировать с асинхронным режимом.
Показать полностью
2
Олег Линник
03.12.2020 17:32
Руслан Кандирал
 
Спасибо. В очередях и потоках более менее разобрался. Вопрос такой. Сущности созданные процедурой New передаются в очередь и потом в потоке удаляются Dispose. А если они не попали в поток? Кто их удалит? Например при завершении приложения? Или метод Queue.Clear
Разве не нужно в этом Clear делать Dispose для всех Queue.List.Item ?
Показать полностью
13
Руслан Кандирал
03.12.2020 21:58
Олег Линник
 
В метод Clear очереди добавлять очистку памяти нет смысла, так как очередь не знает что за указатели в нее добавляются.
А вот те классы, которые используют эту очередь должны подчищать за собой память. При закрытии приложения это не особо важно, так как windows очистит всю память, которая выделялась под это приложение.
Да, вы правы, это не есть правильно нужно будет поправить.
Показать полностью
1
Владимир Груздев
02.12.2020 16:47
 
Здравствуйте, Руслан! Есть гениальный код
reg00.Value:=55;
time:=time+1;
label43.Caption:=floattostrF(time,FFfixed,4,1);
проблема в том что в данном виде он вызывает торможение программы, то есть в лейбл счетчик выводится периодически с явными задержками. Если убрать первую строчку все работает как часы.
Связи с контроллером нет, тестирую программу в офисе.
В чем может быть проблема?
Показать полностью
13
Руслан Кандирал
03.12.2020 15:07
Владимир Груздев
 
Здравствуйте, Владимир!

Слишком мало информации для понимания проблемы. Как именно вызывается данный код?
Если reg00 это TKRMBRegister, то строка
reg00.Value:=55;
просто помещает данные для Modbus запроса в очередь на обработку.
Показать полностью
1
Владимир Груздев
20.01.2021 14:14
Руслан Кандирал
 
По идее все так. И в программе все сделано на 100% как в Вашем примере выше. Но по факту жутко тормозит. Причем проблемы только с записью, я считываю порядка 30 регистров и там все ок, никаких проблем с отображением информации после считывания. Можно ли куда-нибудь скинуть Вам целую программу? Мне кажется так было бы быстрее)
Программа выполняется по таймеру2(основная часть) раз в 1сек.
В таймере 2 мы отправляем посылки на modbus rtu (без ваших компонентов).
В таймере 3 принимаем посылки modbus rtu + считываем регистры modbusTCP через ваши компоненты
В таймере 4 шлем посылки управления modbus rtu + вашими кимпонентами шлем посылки управления. И именно в этой части программа жутко тормозит.
Показать полностью
13
Руслан Кандирал
20.01.2021 14:24
Владимир Груздев
 
Здравствуйте, Владимир!

Какая версия Delphi?

За последний месяц все компоненты касающиеся протокола Modbus очень сильно изменились. Удалось почти в двое увеличить скорость работы и при этом снизить нагрузку на ЦП

На сайте обновления выложу скорее всего в феврале (нужно изменить описание тех модулей которые были изменены)

Сейчас у меня готовы библиотеки до версии XE7, сегодня к вечеру(ночи) сделаю до 10.3

Могу выслать вам обновления на почту
Показать полностью
2
Олег Линник
30.11.2020 21:02
 
Здравствуйте, Руслан! Огромная благодарность за то что позволяете пользоваться результатами своего труда! Столкнулся с проблемой при использовании ваших компонентов. Подключаюсь к устройству по Modbus RTU. Есть описание протокола устройства (там всё обычно) и официальная программа, которая работает хорошо и значит проблема не в подключении. Подключение через USB-UART (виртуальный COM порт 9600 бод). Для написания своей программы на Дельфи установил ваши компоненты (всё ок, спасибо), за основу взял ваш проект - пример из этой статьи. Само собой настроил компонент для порта, подключил его к мастеру, в клиенте выбрал RTU. Начал добавлять регистры в клиент.
Interval=500, MCVarType=MCT_WORD, Name="myReg1", RegisterIndex=1. В соответствии с регистром меняю нужное поле TKRField.
С одним регистром работает всё хорошо - читаю двухбайтное значение из устройства. Как только добавляю ещё регистры тоже для чтения двух байт - начинаются проблемы. Начинает очень плохо читать и врать. В мониторе видно, что на два или три запроса приходит один ответ. Выходит что запросы на чтение отправляются не дождавшись ответа. И ответ получается не на тот запрос.
Пожалуйста, помогите разобраться.
P.S. В дельфи я не новичёк.
Показать полностью
13
Руслан Кандирал
30.11.2020 22:15
Олег Линник
 
Здравствуйте, Олег!

В коннекторе есть следующие свойства

ReadTimeout и WriteTimeout. По факту есть только таймаут на чтение (максимальное время ожидания ответа) и оно равно сумме этих двух свойств. :Желательно значение этих свойств увеличить по максимуму, как минимум по 500 или лучше по 1000, а там как покажет практика. По сути, так как мы заранее знаем длину ответного пакета, то большое значение таймаута ни как на скорость не повлияет если только нет реальных проблем со связью.

И есть свойство WaitRespTime - таймаут между частями пакетов. В последовательных соединениях (в TCP коннекторе это свойство не имеет значения) пакеты могут фрагментироваться. То есть может прийти часть пакета, а затем через определенный промежуток времени следующая часть. Вот WaitRespTime и есть таймаут между этими частями. Это свойство влияет на скорость работы и чем оно ниже тем быстрее скорость обмена пакетами. Но при низком значении может быть нестабильная работа протокола. Скорее всего именно из-за этого параметра у Вас и проблема с разсинхроном пакетов. Тут можно изначально выставить большое значение (300-500), убедится в стабильной работе и затем постепенно пробовать уменьшать пока не будет достигнуто оптимальное значение.
Показать полностью
2
Олег Линник
30.11.2020 23:22
Руслан Кандирал
 
Спасибо за быстрый и полезный ответ!!! Увеличение WaitRespTime до 500 миллисек реально помогло. При 450 уже сбои. Странно, но если пакеты идут без промежутков, то это свойство не должно снижать общую скорость обмена. Сейчас у меня считывается шесть регистров по два байта в каждом. Задержка между реальным изменением параметра на устройстве до отображения в программе примерно 2 секунды. Это очень много. Пробовал одновременно уменьшать Register.Interval и Timer.Interval до 200 мс но несильно ускорилось.
Я правильно понимаю, что чем больше работающих одновременно экземпляров TKRMBRegister в клиенте, тем медленнее будет общий результат. И что для ускорения нужно одним запросом читать сразу несколько нужных регистров в массив. И потом уже из массива брать нужные значения. Или как то по другому решать проблему?
Показать полностью
2
Олег Линник
01.12.2020 00:12
Олег Линник
 
Прочитал про CallBack метода TKRModbusMaster1.ReadHoldingRegisters - буду пробовать :)
Посмотрел исходники. Получается что запросы на чтение/запись попадают в очередь для асинхронной обработки в параллельном потоке. А можно как-то контролировать эту очередь, чтобы не переполнить её. И как-нибудь приоритетно что-то записать/прочитать. Без очереди так сказать :)
Показать полностью
13
Руслан Кандирал
01.12.2020 12:09
Олег Линник
 
"Странно, но если пакеты идут без промежутков, то это свойство не должно снижать общую скорость обмена"

В принципе, да. Сам алгоритм приема/передачи пакетов можно посмотреть в процедуре TKRCOMPortThread._exec
https://github.com/kandiral/Kandiral/blo...

Register.Interval - это свойство работает следующим образом: отправляем запрос; ждем ответ или таймаут; обрабатываем ответ; простаиваем время заданное в Register.Interval и начинаем с начала. То есть мы не забиваем очередь через каждые 200мс (если Register.Interval=200) и фактическая скорость обновления получается равна "Время получения ответа"+Register.Interval (время формирования запроса и обработки ответа можно не учитывать так как эти процессы происходят относительно быстро)

KRTimer.Interval - это интервал обновления значений в визуальных элементах. На передачу данных ни как не влияет.

На скорость влияет не количество TKRMBRegister, а как часто обновляются значения этих регистров. То есть, регистров может быть много, но некоторые могут обновляться через секунду другие через две. То есть, там где значение изменяется часто, опрашиваем часто, другие реже. Некоторые регистры я опрашиваю только при подключении (значение которых не меняется в контроллере) и при их изменении через программу.

Для обновления данных одним запросом нужно использовать TKRModbusMaster. Я во многих комментариях это описывал. Или использовать TKRMBRegisterс типом MCT_ARRAY

Очередей используется три и три потока (не считая основного). Очередь используется для синхронизации между потоками, а потоки нужны в любом случае. В основном потоке делать такие вещи нельзя, так как это снизит скорость передачи данных и будет тормозить основной интерфейс. Два потока и две очереди находятся в коннекторе, вот статья по этой теме:
https://kandiral.ru/delphi/princip_rabot...

и один поток и очередь в TKRModbusMaster. Для контроля заполнения очередей есть компоненты KRConnectorQueueBar и KRMBQueueBar
Показать полностью
13
Руслан Кандирал
01.12.2020 12:18
Руслан Кандирал
 
И еще для ускорения работы в свойстве WaitTime коннектора и KRModbusMaster нужно поставить единицу. Это простой между циклами потока. Я использую потоки KRThread, а этот поток работает циклично, по аналогии с контроллерами
https://github.com/kandiral/Kandiral/blo...
Показать полностью
2
Олег Линник
01.12.2020 16:33
Руслан Кандирал
 
Спасибо за подробные ответы. Сейчас пытаюсь разобраться с потоками в ваших компонентах Modbus и синхронизацией между ними. Думал, там только один асинхронный поток, кроме основного. Пока не буду отвлекать вопросами, хочу сначала сам разобраться.
Показать полностью
1
Владимир Груздев
09.11.2020 08:58
 
Здравствуйте, Руслан. В свою рабочую программу вставил ваш пример обмена для tcprtu, теперь после завершения программы вылезают ошибки "invalid floating" или ошибки с перекрытием памяти. И продолжается это бесконечно после закрытия формы, как будто обмен продолжается.
Посоветуйте пожалуйста как это убрать.
Показать полностью
13
Руслан Кандирал
09.11.2020 09:04
Владимир Груздев
 
Здравствуйте, Владимир!

Все компоненты, где используются потоки нужно останавливать в FormCloseQuery, как в примере выше
Показать полностью
1
Владимир Груздев
09.11.2020 10:39
Руслан Кандирал
 
Именно так, я скопировал из Вашей программы.
Причем без компонента StatusBar1 - invalid floating.
C ним - ошибка записи памяти по адресу.
Upd: исправил остановкой всех своих таймеров. Просто странно раньше они ошибок не выдавали.
Показать полностью
13
Руслан Кандирал
09.11.2020 10:48
Владимир Груздев
 
То есть, у Вас так и все равно возникает ошибка?
Убедитесь что именно событие OnCloseQuery, а не OnClose или OnDestroy

Если это не помогает, то нужно в событии OnCloseQuery самостоятельно сбрасывать компоненты(объекты) и писать информацию в логи, что-бы определить в каком именно классе возникает ошибка.
Показать полностью
0
Ян Иванцев
26.06.2020 15:30
 
Здравствуйте, Руслан! Скажите, пожалуйста, как можно увеличить скорость реакции всей системы на ввод данных в поле или отображение на экране? У меня порядка 40-ка регистров. В программе Modbus Poll, например, время реакции (при опросе 100мс) при вводе данных в один из регистров, составляет визуально доли секунды. В вашем случае у меня получается 1-2 сек. Interval у TKRMBRegister пробовал менять 0,1,100,500 - реакции нет. Что вы посоветуете? Заметил, что обмен у Modbus Poll идет общим массивом всех сорока регистров за раз, а у вас порегисторно. Это так?
Показать полностью
13
Руслан Кандирал
26.06.2020 16:12
Ян Иванцев
 
Ещё учтите что скорость обновления значение в поле TKRField зависит от подключенного таймера, а не от свойства Interval в KRMBRegister
Показать полностью
0
Ян Иванцев
06.07.2020 11:44
Руслан Кандирал
 
Здравствуйте, Руслан! Еще вопрос - вы пишете что нумеровать registerIndex нужно не через единицу (1,2,3,4...) а в соответствии с выбранным VarType (byte,word,byte - 1,3,4...) Это принципиально?
Показать полностью
13
Руслан Кандирал
06.07.2020 11:54
Ян Иванцев
 
Здравствуйте, Ян. Вашей программе не важен индекс регистра, это просто то значение, которое будет отправлено slave устройству. Всё зависит от устройства к которому вы подключяетесь. Например в контроллерах Овен данные modbus регистров структурированы и индекс DWORD значения должен быть всегда чётным.
То есть индексы зависят только от устройства к которому вы подключеетесь.
Показать полностью
13
Руслан Кандирал
26.06.2020 16:03
Ян Иванцев
 
Здравствуйте, Ян!
Каждый TKRMBRegister обновляется отдельным запросом.
Для увеличения скорости в коннекторе и в KRModbusMaster свойство WaitTime установите единицу. Это пауза между запросами. Ещё можно снизить значение WaitRespTime в KRModbusMaster, но при слишком низком значении может быть разсинхронизация пакетов, что приведет к появлению ошибок. То есть желательно это свойство по немного уменьшать и проверять связь на стабильность.
Чтение всех регистров одним запросом можно реализовать на уровне класса TKRModbusMaster, то есть без использования KRModbusClient, KRMBRegisters. Про работу с KRModbusMaster я писал в нескольких комментариях, например тут
https://kandiral.ru/delphi/primer_pereda...
Показать полностью
0
Ян Иванцев
26.06.2020 16:30
Руслан Кандирал
 
Да, веселее заработало. Спасибо!
Показать полностью
0
Андрей Савельев
20.03.2020 20:54
 
Здравствуйте, Руслан! Спасибо за Ваши компоненты, очень выручают.
Возникла необходимость опрашивать датчик, подключаемый через COM порт с периодом 40 мс. В свойстве компонента TKRMBRegister.Interval выставил значение 40, но посмотрев передаваемую информацию через com port toolkit обнаружил, что команды на обновление данных отправляются фактически через 95 мс, а порой пауза доходит до 130 мс. Так же почему-то свойство Value объекта KRMBRegister часто остается необновленным. Подскажите, что и где можно покрутить, чтобы опрос датчика осуществлялся с необходимым периодом?
Показать полностью
13
Руслан Кандирал
20.03.2020 21:18
Андрей Савельев
 
Здравствуйте, Андрей!
Таймер повторного обновления включается после получения ответа. То есть фактический период обновления составляет Interval+"Время на обработку предыдущего пакета"
Для приблизительно одинакового периода между запросами лучше Interval установить в ноль, а обновление делать по таймеру. Например есть таймер с интервалом 40
Еще в коннекторе и в KRModbusMaster значения свойства WaitTime установите в еденицу
Показать полностью
0
Андрей Савельев
20.03.2020 21:51
Руслан Кандирал
 
Спасибо, попробую так.
Показать полностью
0
Ян Иванцев
16.03.2020 10:18
 
Руслан, добрый день. Скажите, пожалуйста, в вашем проекте возможно ли динамически (с помощью включаемых разных форм) менять количество переменных modbus и, соответственно. длину посылки?
Показать полностью
0
Руслан Кандирал
16.03.2020 10:32
Ян Иванцев
 
Здравствуйте! Да количество переменных можно изменять программно. Что значит изменить длину посылки? Каждая переменная обновляется отдельным запросом. Если вы не хотите что-бы одна из переменных обновлялась, установите значение свойства Interval этой переменной в ноль.
Показать полностью
0
Александр Александров
22.01.2020 23:54
 
Здравствуйте. помогите пожалуйста. мне нужно из Delphi передать integer в Owen OPC Server. мне очень нужно. Мне нужен пример. к сожалению врядли разберусь. Только как считать с другого устройства а мне нужно наоборот получается.
Показать полностью
13
Руслан Кандирал
23.01.2020 06:42
Александр Александров
 
Какую связь(com порт, tcp/ip) и протокол(modbus TCP, modbus RTU, modbus ASCII) нужно использовать?
Показать полностью
0
Евгений Старцев
20.11.2019 12:11
 
Здравствуте!
На основе данного проекта реализован опрос ТРМ202, ТРМ138, МВ110-8А. Все работает замечательно.
НО только с китайским преобразователем RS485-USB на чипе CH340.


Ни с преобразователем АС4 Овен(чип Silicon Labs CP210x), ни с преобразователем Bolid(чип XR21B1411) программа не может связаться с приборами. Конфигураторы Овен связываются с приборами при использовании любого преобразователя.
Прошу помощи.
Показать полностью
13
Руслан Кандирал
20.11.2019 12:42
Евгений Старцев
 
Здравствуйте! Проверьте связь через программы тестирования Modbus(например QModbus)
Показать полностью
0
Евгений Старцев
20.11.2019 13:18
Руслан Кандирал
 
QModbus отличная связь. Запросил по 3 функции 2 регистра - ответ корректный.
Проверил программой - связи нет.
Показать полностью
Комментарий удален
0
Сергей Железцов
09.10.2019 08:24
 
А еще есть предложение на будущее.
Добавить TKRValueListEditor, наследника TValueListEditor, но совсеми "плюшками" TKRField, чтобы редактировать сразу множество регистров в одном компоненте.
И есть вопрос: если мне необходимо соединяться с несколькими устройствами, как мне лучше поступить, менять в run-time свойство Addres у KRModbusClient и переподключаться или завести несколько KRModbusClient (около 1,5 десятка - датчики и модули управления)?
Показать полностью
13
Руслан Кандирал
09.10.2019 15:52
Сергей Железцов
 
По TValueListEditor, это уже не так быстро. Может быть со временем.

Если вам нужно одновременно работать с несколькими приборами, то конечно нужно создавать отдельно KRModbusClient для каждого. Если приборы(набор регистров) одинаковые, то KRModbusClient можно на клонировать через copy/past вместе с переменными, единственное, что так как каждая переменная это компонент(от класса TComponent), то имена одинаковые быть не могут, их(имена) нужно будет откорректировать.
Менять адрес - это, я думаю, подойдет только в том случае, если вы работаете с устройствами поочередно. Вообще, в переподключении (свойство Active) нет необходимости, но нужно убедится, что все пакеты от предыдущего устройства уже обработаны. Нужно все таки KRModbusClient выключить (Active:=false), затем сделать задержку через Application.ProcessMessages (время задержки подобрать на практике), затем поменять адрес и включить.
Показать полностью
0
Сергей Железцов
09.10.2019 17:01
Руслан Кандирал
 
Думаю попробовать создавать динамически экземпляры KRModbusClient, а отдельно в настройках например, в ini файле брать свойства для каждого
Показать полностью
0
Сергей Железцов
08.10.2019 11:33
 
Предлагаю сделать у переменной Modbus свойство "ByteSort" - порядок байт. Например InSAT сделала так:

Или хотя бы просто цифрами указывать без редактора....
Показать полностью
13
Руслан Кандирал
08.10.2019 11:50
Сергей Железцов
 
Ок. Я думал, ранее, об этом. Но так как острой необходимости в изменении последовательности байт/слов не было, то я это так и не сделал.
Показать полностью
0
Сергей Железцов
08.10.2019 12:05
Руслан Кандирал
 
Есть такая необходимость, т.к. это не оговаривается стандартом протокола Modbus и разные производетели задают у своих устройств этот параметр сами, причем у разных регистров по-разному может быть.
Конечно если я сам создаю устройство (программирую контроллер) то я сам в какой последовательности запишу, в такой и прочту. Но вот если устройство произведено кем то другим и в инструкции к нему указывается, что например, показания датчика давления находятся во входных регистрах по адресу 8, порядок чтения - старшим байтом вперед, то мне уже остается править значение перед выводом в поле (хорошо что у вашего компонента KRField есть нужное событие). Но было бы гибче, да и логичнее, настраивать это в свойстве переменной. Т.к. поле KRField может менять "переменную-хозяина", допустим, в run-time режиме.
Показать полностью
13
Руслан Кандирал
08.10.2019 12:23
Сергей Железцов
 
Я понимаю о чем вы говорите. Я имел ввиду, что лично мне этот функционал не нужен был на практике. Хорошо, сегодня-завтра я это сделаю и обновлю компоненты.
Показать полностью
0
Сергей Железцов
08.10.2019 12:33
Руслан Кандирал
 
Спасибо, что услышали. И вообще спасибо за компоненты, начинаю потихоньку осваивать, много интересных решений у вас для работы с Modbus.
Показать полностью
13
Руслан Кандирал
09.10.2019 02:26
Сергей Железцов
 
Обновил библиотеки. Новые свойства у переменных Modbus: HighByteFirst, HighWordFirst, HighDWordFirst
Показать полностью
0
Сергей Железцов
09.10.2019 08:17
Руслан Кандирал
 
Скачал, установил, выставил свойства - все работает. Отлично! Большое спасибо, с меня бонус в ближайший аванс )
А если свободный порядок байт? Т.е. аналог на скрине тому, что если сниму все чекбоксы, то передвигая байты стрелками влево/вправо могу сам задавать нужную комбинацию.
Показать полностью
0
Алексей Григорьев
05.08.2019 19:28
 
Здравствуте, Руслан! В ваших примерах передачи данных вы создаете в delphi(с помощью TKRMBRegister) modbus переменные и соответственно передаете их. А если переменные уже заранее заданы в программе delphi и их нужно передать в плк, то как тогда эти переменные передавать с помощью ваших компонентов?
Показать полностью
13
Руслан Кандирал
05.08.2019 20:17
Алексей Григорьев
 
Здравствуйте, Алексей! Как именно заданы ваши переменные? Можете показать часть кода? Мне не совсем понятно, что вы имеете ввиду.
Показать полностью
0
Алексей Григорьев
05.08.2019 21:01
Руслан Кандирал
 
Например, в программе delphi есть следующее объявление NDvizh : integer. Переменная NDvizh является порядковым номером движения заготовки по конвейеру. Данную переменную требуется передать в плк. Каким образом можно это сделать с помощью ваших компонентов через modbus?
Я пытался присвоить значение своих переменных вашим(modbus-переменным), но появлялась ошибка Unit1.pas(71): E2010 Incompatible types: 'TKRMBRegister' and 'Integer'
Показать полностью
13
Руслан Кандирал
05.08.2019 21:57
Алексей Григорьев
 
Кидаете на форму коннектор (соответствующий вашему подключению), KRModbusMaster, KRModbusClient. Настраиваете их как в примере. В KRModbusClient создаете переменную с именем например mb_NDvizh с типом MCT_WORD (это один регистр, без знаковое значение), указываете индекс регистра в свойстве RegisterIndex. И по событию нажатия кнопки можно сделать так
Показать полностью
0
Алексей Григорьев
06.08.2019 10:04
Руслан Кандирал
 
Отлично, спасибо за помощь!
Показать полностью
1
Сергей Никитенко
05.07.2019 12:49
 
Здравствуйте, Руслан.
Я всё к предыдущему вопросу....именно при закрытии одной программы (не важно какой именно) теряется связь с контроллером(из всех программ на delphi и связь ПЛК с панелью СП310(RS232), через codesys по ethernet связь остаётся(вижу что переменные передаваемые панелью СП310 не обновляются, через непонятное время начинают обновляться), как будто ПЛК делает сброс всех соединений...)...может как-то некорректно закрывается TCP соединение...???
KRTimer1.Enabled:=FALSE;
KRModbusClient1.Active:=FALSE;
KRModbusMaster1.Active:=FALSE;
KRTCPConnector1.Active:=FALSE;
Для решения данной ситуации пытаюсь реализовать следующее:
1) сделать закрытие сетевого соединения (как будто выдернул ethernet из ПК);
2) KRTimer1.Enabled:=FALSE;
KRModbusClient1.Active:=FALSE;
KRModbusMaster1.Active:=FALSE;
KRTCPConnector1.Active:=FALSE;
3) восстановить сетевое соединение и закрыть программу...
тогда вторая программа на 2 ПК не разрывает соединение с контроллером...
Показать полностью
13
Руслан Кандирал
08.07.2019 21:04
Сергей Никитенко
 
Здравствуйте, Сергей

Провел тест. Всё нормально. Вот видео
https://kandiral.ru/downloads/bandicam%2...

Вот исходники
https://kandiral.ru/downloads/modbus_tes...
Показать полностью
1
Сергей Никитенко
09.07.2019 09:32
Руслан Кандирал
 
Здравствуйте, Руслан.
У Вас всё нормально работает....но у меня почему-то не хочет...может контроллер что-то глючит овен 110-32......вообщем реализовал так как писал:
1) закрытие сетевого соединения (как будто выдернул ethernet из ПК);
2) KRTimer1.Enabled:=FALSE;
KRModbusClient1.Active:=FALSE;
KRModbusMaster1.Active:=FALSE;
KRTCPConnector1.Active:=FALSE;
3) восстановить сетевое соединение и закрыть программу...
Показать полностью
13
Руслан Кандирал
05.07.2019 15:10
Сергей Никитенко
 
Здравствуйте, Сергей
Ок. Я протестирую и отпишусь.
Показать полностью
1
Сергей Никитенко
03.07.2019 15:36
 
Здравствуйте, Руслан.
Спасибо за предыдущий ответ...разбираюсь с программой в контроллере..
Возник ещё - вопрос: возможно ли из двух приложений на делфи (с разных ПК) опрашивать один и тот же контроллер по TCP?
Опробовал следующее: в одной программе опрашиваю порт 502(стандартный), в другой 503 ...когда обе программы проконнектились до контроллера всё нормально, но стоит закрыть программу на одном ПК, на втором теряется связь...на некоторое время...потом восстанавливается..
Показать полностью
13
Руслан Кандирал
03.07.2019 16:42
Сергей Никитенко
 
Здравствуйте, Сергей
У меня была похожая ситуация. Только на одном компьютере было запущено две программы, которые подключались к одному контроллеру. Вроде проблем не было.
Вот скрин настроек подключения в codesys
Показать полностью
1
Владимир Груздев
02.07.2019 12:58
 
Здравствуйте, Руслан!
Программист из меня так себе, пытаюсь сделать программку для связи дельфи - контроллер по modbus tcp, и застопорился я на вашем замечательном наборе компонентов, который я скачал, но не знаю как подключить к Delphi 10.3. Пробовал Components-install new-KRmodbusМaster и вылезает окно KRConnector с кучей ошибок на незнакомые элементы. Несовместимость с 10.3 или я делаю что то не так?
Показать полностью
13
Руслан Кандирал
03.07.2019 01:09
Владимир Груздев
 
Опубликовал последнюю версию компонентов
https://kandiral.ru/delphi/nabor_kompone...

И сделал инструкцию
https://kandiral.ru/delphi/instrukciya_p...
Показать полностью
1
Владимир Груздев
03.07.2019 09:55
Руслан Кандирал
 
Огромное спасибо! Буду пробовать.
Показать полностью
13
Руслан Кандирал
02.07.2019 14:00
Владимир Груздев
 
Здравствуйте, Владимир
Давно уже собирался обновить набор компонентов и инструкцию по установке. Через несколько часов выложу текстовую инструкцию. Видео обзор сделаю вечером или даже ближе к ночи.
Показать полностью
0
Андрей Марченко
01.07.2019 19:09
 
Здравствуйте, Руслан...
Есть ли у вас эти компоненты для работы с Modbus RTU для старого дельфи 2007го года ?
(Delphi® 2007 for Win32® Version 11.0.2804.9245). Надо вставить в старый проект работу с модбасом...
Показать полностью
13
Руслан Кандирал
01.07.2019 19:20
Андрей Марченко
 
Здравствуйте, Андрей
Компоненты тестировались на Delphi с версии XE (ver 15). А что мешает откомпилировать старый проект под Delphi XE?
Показать полностью

Новости сайта

18.07.2021 14:30
Новая статья по экспорту Google Sheet в MS Excel
Экспорт таблиц Google Sheets в файл MS Excel с помощью Google Script, а также сохранение на Google Drive и отправка на e-mail
30.03.2021 14:53
Обновлен набор компонентов для Delphi
Пакет компонентов для Delphi обновлен до версии от 30.03.2021 г.
14.03.2021 04:54
Обновлен скрипт экспорта Google таблиц в PDF
Добавлены новые возможности при экспорте Google таблиц в PDF файл
22.01.2021 11:45
Обновлен пакет компонентов для Delphi
В пакет компонентов для Delphi было внесено большое количество изменений, касающиеся протокола Modbus и мультиязычности

Популярные статьи

Экспорт таблиц Google Sheets в PDF файл, на email и на Google диск
Экспорт таблиц Google Sheets в PDF файл с помощью Google Script с последующим скачиванием или сохранением на Google Drive или отправкой на почту
Набор компонентов для Delphi
Набор компонентов для Delphi включает в себя компонент для работы с файловой системой, сетью, автоматизацией и другие.
Пример передачи данных по протоколу Modbus на Delphi
Пример передачи данных с контроллера ОВЕН ПЛК100 в программу на Delphi по протоколу Modbus
Инструкция по установке компонентов на Delphi
Подробная инструкция по установке компонентов на Delphi с видео обзором