Здравствуйте! Подскажите как указывать адрес в WinCC для ТРМ201? По Modbus TCP указываешь к примеру %MW4105 и все работает. По RTU формат 4х40001. Объясните пожалуйста
Вид для печати
Здравствуйте! Подскажите как указывать адрес в WinCC для ТРМ201? По Modbus TCP указываешь к примеру %MW4105 и все работает. По RTU формат 4х40001. Объясните пожалуйста
Так, иначе) как мне получить измеренное значение с трм на эту панель) у трм регистр 4105. WinCC дает ставить только 5 знаков , если по rtu
Если ТРМ подключать через МКОН и к нему панель, то указываешь MW4105 и данные идут.
4х40001 это формат, какой WinCC разрешает
Есть соглашение, тянущееся по историческим причинам от разработчиков Modbus - Modicon - что 3x40001 это регистр 0 (или 1 - всегда нужно проверять практикой) к которому обращаются функцией чтения 03.
Т.е. регистр 4501 соответствует 3x44502 или 3x44501 в другой нотации.
Вот смотрите, через modscan64 пытаешься ТРМ считать, не получается. Там адреса с 4хххх. Например через Овен ОРС, указываешь 4105 все идет
modscan64 платная программа и даже воспроизвести эксперимент не каждый сможет.
Для изучения работы с Modbus читал уставку в ТРМ212 самописным тестовым кодом на FreePascal с использованием библиотеки работы с COM-портом Synaser
Обратите внимание, что после приёма значения из порта, для адекватного вывода на печать приходилось выполнять обмен байт в принятом 16 или 32 разрядном числе.
Кроме того, число типа Float занимает 2 регистра и не может быть типом MW, скорее уж MD - DWORD.
Сама функция чтения была обёрткой над вызовом функции 03Код:MB_ReadUInt16(ser, ModBusSetting_Addr, trm212SP, uiR);
Writeln(uiR);
Sleep(100);
MB_ReadSignedInt16(ser, ModBusSetting_Addr, trm212SP, siR);
Writeln(siR);
Sleep(100);
MB_ReadFloat32(ser, ModBusSetting_Addr, trm212SP_, fR);
Writeln(fR: 0: 1);
Sleep(100);
MB_ReadChar8(ser, ModBusSetting_Addr, trm212DEV, cR);
Writeln(cR);
Sleep(100);
MB_ReadChar8(ser, ModBusSetting_Addr, trm212VER, cR);
Writeln(cR);
Вызов функции 03Код:function MB_ReadUInt16(var SerialPort: TBlockSerial; DevAddr, RegNo: word;
var R: UInt16): integer;
begin
Result := MB_ReadRegesters(SerialPort, DevAddr, RegNo, 1, R);
R := Swap(R);
end;
function MB_ReadFloat32(var SerialPort: TBlockSerial; DevAddr, RegNo: word;
var R: Float32): integer;
var
t: TDWordConvert;
begin
Result := MB_ReadRegesters(SerialPort, DevAddr, RegNo, 2, t);
t.dw := Swap(t.dw);
t.w0 := Swap(t.w0);
t.w1 := Swap(t.w1);
R := t.f32;
end;
При этом, адреса регистров задавались в 16-ричной системе счисленияКод:function MB_ReadRegesters(var SerialPort: TBlockSerial;
DevAddr, RegNo, RegCount: word; var ResBuffer): integer;
var
Buffer: TBuffer;
crc16: word;
i: integer;
ReadLen: word;
begin
if not SerialPort.InstanceActive then
begin
//ComException('can not read from a closed port.');
exit;
end;
if (DevAddr > 247) then
begin
if DevAddr > 40000 then
DevAddr := DevAddr - 40001
else
exit;
end;
{
Запрос
00 - адрес устройства
01 - функция
02 - адрес первого из группы считываемых регистров hi
03 - то же, lo
04 - количество считываемых регистров hi
05 - то же, lo
06 - crc16 lo
07 - то же hi
}
Buffer[0] := lo(DevAddr);
Buffer[1] := $03;
Buffer[2] := hi(RegNo);
Buffer[3] := lo(RegNo);
Buffer[4] := hi(RegCount);
Buffer[5] := lo(RegCount);
crc16 := Crc16InitValue;
for i := 0 to 5 do
Crc16 := UpdateCrc16(Buffer[i], crc16);
Buffer[6] := lo(crc16);
Buffer[7] := hi(crc16);
SerialPort.SendBuffer(@Buffer, 8);
SerialPort.RecvBufferEx(@Buffer, RegCount * 2 + 5, Timeout_1s div 10);
{
Ответ в корректном случае
00 - адрес устройства
01 - функция
02 - количество байт в ответных данных
03 - данные lo
04 - данные hi
05 - CRC16 lo
06 - CRC16 hi
В случае ошибки
или таймаут
или устройство пришлёт
00 - адрес устройства
01 - (=$83) код функции с установленным старшим битом
02 - код ошибки (exception code)
03 - lo crc16
04 - hi crc16
}
if Buffer[01] = $83 then
begin
ReadLen := 5;
end
else
begin
ReadLen := Buffer[2] + 3;
end;
Crc16 := Crc16InitValue;
for i := 0 to ReadLen - 1 do
Crc16 := UpdateCrc16(Buffer[i], crc16);
if Crc16 <> Buffer[ReadLen] + 256 * Buffer[succ(ReadLen)] then
begin
//ошибка приёма пакета - не правильный crc16
end
else
begin
//writeln('CRC16 Ok');
end;
if RegCount * 2 = Buffer[02] then
begin
move(Buffer[3], TBuffer(ResBuffer), RegCount * 2);
end
else
begin
//ошибка приёма пакета - не правильное количество принятых байт
end;
Result := SerialPort.LastError;
end;
Код:{LvoP. Оперативные параметры (только чтение: Modbus-функция 0x03)}
trm212STAT = $0000; {binary}
trm212PV1 = $0001; {Signed Int16}
trm212PV2 = $0002; {Signed Int16}
trm212LUPV = $0003; {Signed Int16}
trm212SP = $0004; {Signed Int16} {запись: Modbus-функция 0x10}
trm212SET_P = $0005; {Signed Int16}
trm212O = $0006; {Int16}
{LvoP. Рабочие параметры прибора (чтение: Modbus-функция 0x03), запись: Modbus-функция 0x10}
trm212r_L = $0007; {Int16}
trm212r_out = $0008; {Signed Int16}
trm212R_S = $0009; {Int16}
trm212AT = $000A; {Int16}
{LvoP. Оперативные параметры прибора только чтение: Modbus-функция 0x03)}
trm212DEV = $1000; {Char[8]}
trm212VER = $1004; {Char[8]}
trm212STAT_ = $1008; {binary}
trm212PV1_ = $1009; {Float32}
trm212PV2_ = $100B; {Float32}
trm212LUPV_ = $100D; {Float32}
trm212SP_ = $100F; {Float32}
trm212SET_P_ = $1011; {Float32}
trm212O_ = $1013; {Float32}
Приводите параметры обмена по modscan64:
- адрес регистра
- количество регистров
- порядок байт
- тип данных
и остальное
AlexandrGr - да вроде все правильно, только иначе написано - 3х функция 40001 где 4(адресация Holdings)
соответственно 4x функция - 30001 адресация Inputs
Да, прошу прощения, неправильно по памяти записал нотацию...
Конечно же, нужно было как поправил AlexandrGr, а написал в смысле как уточнил melky.
Функцией 03 читают регистры 4x (Holding register), которые имеют диапазон значений 40001...49999 (они же при помещении в запрос имеют значения 0...9998). Т.е. 40001 это логический адрес регистра, а 0 это его же физический адрес.
В разных реализациях Modbus по разному разрешается задавать адрес. Например, в ПЛК Koyo DL06 адрес регистра задавался логический и физический адрес регистра из карты Modbus для подключаемого прибора (в моём случае ВКТ-5) никак не подходил по диапазону (40001...49999) - потребовалось приведение к логическому.
Всем спасибо, решено! RS-485 надо было А к В, В к А. Странно конечно, ну да ладно. А так да, правильно сказали - регистр 44105
Уважаемые коллеги!
Завал! Кто поможет буду вспоминать с благодарностью.
Проблема - пытаюсь выполнить опрос приборов ОВЕН (ТРМ10, ТРМ201 и др.) по протоколу Modbus Rtu средствами API и полный швах, мыслей нет.
Представляю прототип программы в среде Delphi 11.
Срочности никакой нет. Просто хочу понять как реализовать.
С праздником Пасхи и наступающими праздниками.
С уважением
У меня дельфи нет, я прогал раньше.
Я просто так открыл блокнотом код и стал смотреть ради интереса, поэтому могу дать ложную надежду.
К чему хочу придраться:
1. Все вот эти вот "Memo1.Lines.Add" указывают на то, что проект делался быстро-быстро и криво.
Лучше бы ты сделал процедуру типа AddToLog, и красиво её вызывал. Потому что если ты когда-нибудь захочешь поименовать Memo1 во что-то понятное типа txtLog, то замучаешься везде исправлять.
2. Не понял, зачем там разные расчёты CRC. Ты же делаешь опрос по Modbus RTU! А там CRC имеет ОДИН единственный алгоритм.
Вообще даже Modbus - медленный протокол. CRC явно считается быстрее, чем передача данных по протоколу.
3. Я не увидел составления посылок для протокола Modbus в принципе. Ты там тестируешь CRC, выводишь всякую инфу о возможностях портов... а где посылки данных-то?
Мои мысли о том, что тебе надо сделать:
0. Видимо, почитать про протокол Modbus. Он же работает как Запрос-Ответ. То есть, чтобы получить данные - надо что-то ПОСЛАТЬ. А у тебя в коде на "btnWritePortClick" написано "Пока Не использую".
1. Потренироваться опрашивать настоящие устройства через другие программы так, чтобы ты видел там реальные байты, какие идут между ними. Эти самые Запросы-Ответы. Такие программы могут быть OPC-серверами, Modbus Poll и прочими.
2. Разобраться с тем, как посылать и принимать байты в порт.
Составляешь массив байтов и потправляешь его в порт.
И получаешь данные из порта и склеиваешь их в массив байтов. НЕ в строку, так как строка с байтами работать не будет.
3. Потом из документации по Modbus понять, как составлять запрос (Адрес, Команда, Данные, CRC).
И уже его отсылать и смотреть, что тебе ответят.
Спасибо за советы. С чем то соглашусь. Почему в Delphi? Она мне ближе.
Проект делал долго. Криво? Соглашусь спора нет.
Документацию читал, изучал, вопросов по протоколу нет.
Расчет CRC разными алгоритмами провел для их сравнения по быстродействию. Для себя определил наимолее быстпый и им пользуюсь.
Формирование команды запроса реализовано. Как внутри запроса на Чтение, так и отдельной командой. Но вот здесь и кроются проблемы.Цитата:
3. Потом из документации по Modbus понять, как составлять запрос (Адрес, Команда, Данные, CRC).
И уже его отсылать и смотреть, что тебе ответят
В *zip файле есть исплняемая программа. Там можно посмотреть на формирование команды.
С уважением
Как-то для изучения Modbus делал простенькую консольную программу на FreePascal - опрашивал измеренное значение на ТРМ212.
Для обмена с портом использовал библиотеку Synaser (она - часть библиотеки Synapse).
Сделал примитивно - запрос-ответ (с учётом таймаута).
А правильно - нужно работать с потоками, чтобы интерфейс не зависал.
Встречал ссылку на статью "Работа с COM-портом с помощью потоков", с комментарием, что в ней правильно описано
https://usbsergdev.narod.ru/doc.html
Могу и свои наработки показать, но они примитивные, незаконченные в части диагностики обмена.
Работа в консоли меня не напрягает, приоритет отдаю функционалу утилиты (тест прибора нужно выполнить здесь и сейчас, а не зависать над красотами GUI) и поэтому программа консольная. Вывод консоли можно легко перенаправить в текстовый файл и сравнивать файлы из меню файл-менеджеров.
Использую их с небольшими изменениями под ситуацию для диагностики обмена по Modbus - то сканировать все адреса+скорость+чётность, то сканировать все регистры у устройства. Можно и готовый ModbusPoll использовать, но он платный, а у меня руки есть - могу сам сделать.
В прошлом году нужно было обосновать неправильность чужой программы комплекса на нескольких ПР200 - нужно было показать, что нет обмена состояниями от одного из Slave. Сначала доказал, что на Slave доступен всего один регистр для чтения, потом показал, что он не изменяется при большинстве нештатных ситуаций. Все проверки - модификациями этой программы.
Вложение 83277
Также, для обмена с устройствами производства Овен есть готовая библиотека и к ней идут примеры
https://owen.ru/product/biblioteka_win_dll
Моя цель - выполнять запросы посредством функций API (ReadFile и WriteFile);
Взаимодействие с приборами ОВЕН по протоколу ОВЕН мною давно освоены. Сейчас проблема с Modbus RTU т.к. тенденция разработчика это полный отказ от протокола ОВЕН. Могу и ошибатся.
С уважением
В статье по ссылке - как раз WinAPI, только с применением потоков. А что язык программирования не Delphi, так для WinAPI названия и параметры точно совпадают.
Библиотеку Овен можно посмотреть - там не только протокол ОВЕН. Но, если нужно самостоятельные реализации без внешних dll, то конечно, библиотека не подойдёт.
Библиотеку Synaser всё равно рекомендую - она открытая, богатая на подпрограммы, и тоже на WinAPI (где-то в глубине) - там есть и настройки таймаутов.
По Вашей проблеме ничего не понятно. Вы привели код. Какая у него проблема? Нет опроса?
Тогда установите на компьютер прослушку (сниффер) COM-порта, подключитесь к нужному и смотрите дамп обмена - или не то посылаете или не обрабатываете ответ или порт не так настроен - будет хотя бы понятно, чего не хватает.
За исчерпывающую информацию спасибо. Непременно ей воспользуюсь.
Посмотрел документацию на библиотеку и, к своему сожалению, не нашел функций работы с протоколом Modbus RTU. Упоминается протокол ОВЕН.
Потому что TCP-драйвер считает адреса с нуля (0, 1, 2...), а RTU-драйвер (чаще всего) считает с единицы (1, 2, 3...). Вот эта разница в единицу и есть корень всех проблем