- Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
- Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
- Не надо писать свой вопрос в первую попавшуюся тему - всегда лучше создать новую тему.
- За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения. Непонятно? - Читать здесь.
- Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
- Перед тем как что-то написать - читать здесь, а затем здесь и здесь.
- Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.
CP341, Опрос Modbus-устройства.
Модератор: Глоб.модераторы
-
- здесь недавно
- Сообщения: 6
- Зарегистрирован: 14 сен 2016, 08:26
- Имя: Сорокин Дмитрий Валентинович
CP341, Опрос Modbus-устройства.
Добрый день, нашел тут тему подобную, но обновлю. У кого есть опыт не могли бы помочь?
Хотел сориентироваться в примерах Modbus RTU протоколом.
Дело в том что ставят задачу опрашивать несколько устройств через CP341 соответственно через Simatic 300 серии.
Хотел бы проверить реально действующие примеры на таком железе.
Интересует именно формирование разных запросов на разные устройства с Modbus. Немного не хватает опыта по такого рода задачам,
хотелось бы определится как должны формироваться запросы.
Насколько я понимаю сколько будет разных посылок, столько надо будет формировать различных вызовов DB на каждый отдельный запрос.
Каким образом лучше формировать сами посылки запросов чтобы система работала гибко и просто /если принять во внимание необходимость опроса разных устрйоств.
Хотел сориентироваться в примерах Modbus RTU протоколом.
Дело в том что ставят задачу опрашивать несколько устройств через CP341 соответственно через Simatic 300 серии.
Хотел бы проверить реально действующие примеры на таком железе.
Интересует именно формирование разных запросов на разные устройства с Modbus. Немного не хватает опыта по такого рода задачам,
хотелось бы определится как должны формироваться запросы.
Насколько я понимаю сколько будет разных посылок, столько надо будет формировать различных вызовов DB на каждый отдельный запрос.
Каким образом лучше формировать сами посылки запросов чтобы система работала гибко и просто /если принять во внимание необходимость опроса разных устрйоств.
-
- эксперт
- Сообщения: 1467
- Зарегистрирован: 25 июл 2008, 10:25
- Имя: Эдуард Владимирович
- Страна: СССР
- город/регион: Оренбург
- Благодарил (а): 46 раз
- Поблагодарили: 105 раз
CP341, Опрос Modbus-устройства.
Донгл драйвера Модбас, что мастер, что слейв, стоил в районе 1500 евро для CP341 ))) Да, на оф. форуме Сименса хорошие люди раздавали самописные драйвера под любые конфигурации, но, форум полетел... Может рассмотрите варианты:
- покупки самой дешевой модели S7-1200 с нужным коммуникационником, и официально и модбас бесплатен, и примеров куча.
- покупки аппаратного шлюза модбас/профибас как вариант от Anybus http://www.industrialnets.ru/catalogue/ ... odbus-rtu/
- покупки самой дешевой модели S7-1200 с нужным коммуникационником, и официально и модбас бесплатен, и примеров куча.
- покупки аппаратного шлюза модбас/профибас как вариант от Anybus http://www.industrialnets.ru/catalogue/ ... odbus-rtu/
--------------------------------------------------------------------------------------------
-
- здесь недавно
- Сообщения: 6
- Зарегистрирован: 14 сен 2016, 08:26
- Имя: Сорокин Дмитрий Валентинович
-
- эксперт
- Сообщения: 1467
- Зарегистрирован: 25 июл 2008, 10:25
- Имя: Эдуард Владимирович
- Страна: СССР
- город/регион: Оренбург
- Благодарил (а): 46 раз
- Поблагодарили: 105 раз
CP341, Опрос Modbus-устройства.
Примеры под новые 1200 посмотрите - там и как порт открыть и как организовать вызов, т.е. считать, сменить признак на запись, записать, сменить адрес устройства и опять считать и так по кругу - в этом и есть отличие от встроенного профибаса где коммуникационник опросы сам устраивает )))
--------------------------------------------------------------------------------------------
-
- здесь недавно
- Сообщения: 2
- Зарегистрирован: 27 ноя 2016, 08:49
- Имя: Перерва Алексей Петрович
- Страна: Россия
- город/регион: Краснодар
- Поблагодарили: 1 раз
CP341, Опрос Modbus-устройства.
Держи пример:
Код: Выделить всё
FUNCTION_BLOCK FB10
VAR_INPUT
enable : BOOL; //разрешение работы
Addr_CP : INT; // ID
dbD1 : INT := 0; //номер блока данных первого пакета
P1_EN : BOOL := 0; //разрешения обмена первого пакета
dbD2 : INT := 0; //номер блока данных второго пакета
P2_EN : BOOL := 0; //разрешения обмена для второго пакета
END_VAR
VAR_IN_OUT
init {S7_read_back := 'false'}: BOOL := true;
END_VAR
VAR_OUTPUT
Err_Send : BOOL; // Ошибка функции передачи
Err_Recv : BOOL; // Ошибка функции приема
ERROR : BOOL; // ERROR LINK
ERR_P1 : BOOL; //ошибка приема/передачи первого пакета
ERR_P2 : BOOL; //ошибка приема/передачи второго пакета
ERR_SND : WORD; //код ошибки функции передачи
ERR_RCV : WORD; //код ошибки функции приема
ES_CNT {S7_visible := 'false'} : INT; //счетчик ошибок передачи
ER_CNT {S7_visible := 'false'} : INT; //счетчик ошибок приема
paket {S7_visible := 'false'} : INT; //номер текущего пакета
Point {S7_visible := 'false'} : INT; //Точка входа
timeout {S7_visible := 'false'} : INT; //ожидание ответа
ACK_ERR {S7_visible := 'false'} : BOOL; //ведомый возвращает ошибку
ACK_ERR_CODE {S7_visible := 'false'} : INT; //ответный код ошибки ведомого
ACK_ERR_CNT {S7_visible := 'false'} : INT; //счетчик ошибок в ответе ведомого
CRCERR {S7_visible := 'false'} : BOOL; //ошибка CRC пакета
CRCERR_CNT {S7_visible := 'false'} : INT; //счетчик ошибок CRC
END_VAR
VAR_TEMP
// Temporary Variables
i,k, CRCCount : INT;
ERR_SFC : INT; //ошибка вызова SFC
lenght : INT; //размер копируемых данных
STATUS : WORD;
LEN_SEND : WORD; // Длина передаваемого пакета
LEN_RECV : WORD; // Длина принатого пакета
Err_S : BOOL;
Err_R : BOOL;
tbit : BOOL;
TempPointer: ANY; // Artificial variable
AnyPointer AT TempPointer: STRUCT
s7const :BYTE; // константа s7
datatype :BYTE; // тип данных
lenght :WORD; // размер передаваемых данных
db_num :WORD; // номер блока данных
area :DWORD; //стратовый адрес
END_STRUCT;
tdword : DWORD;
stdword AT tdword : STRUCT
B1: BYTE;
B2: BYTE;
B3: BYTE;
B4: BYTE;
END_STRUCT;
END_VAR
VAR
// Static Variables
Done : BOOL; // Передача успешна
NDR : BOOL; // Прием успешен
REQ : BOOL; // Разрешение перeдачи
CRC16 : WORD;
SND_DATA : P_SND_RK;
RCV_DATA : P_RCV_RK;
RECV_Message : ARRAY [1..250] OF BYTE; // Массив принятых данных
SEND_Message : ARRAY [1..40] OF BYTE; // Массив переданных данных
Addr_Slave: INT; //адрес ведомого
END_VAR
//-----------------------------------------------------------------
//инициализация
IF init THEN
Err_Send := false;
Err_Recv := false;
ERROR := false;
ERR_P1 := false;
ERR_P2 := false;
ACK_ERR := false;
CRCERR := false;
init := false;
paket :=0;
point :=0;
timeout :=0;
ERR_SND :=0;
ERR_RCV :=0;
ES_CNT :=0;
ER_CNT :=0;
ACK_ERR_CODE :=0;
ACK_ERR_CNT :=0;
CRCERR_CNT :=0;
END_IF;
//рарешение работы
IF NOT enable THEN RETURN; END_IF;
//-----------------------------------------------------------------
//формирование пакета
IF point = 0 THEN //формирование запроса
CASE paket OF
0 ://Запрос первого пакета
IF NOT P1_EN THEN //разрешение на обмен данных параметров
paket := paket +1;
RETURN;
END_IF;
Send_Message[1] := INT_TO_BYTE(4); // Адрес слейва
Send_Message[2] := B#16#03; //функция чтения входов
Send_Message[3] := B#16#00; //стартовый адрес hi
Send_Message[4] := B#16#14; //стартовый адрес lo
Send_Message[5] := B#16#00; //количество параметров hi
Send_Message[6] := B#16#01; //количество параметров lo:
CRCCount :=6;
1://Запрос второго пакета
IF NOT P2_EN THEN //разрешение на обмен данных параметров
paket := paket +1;
RETURN;
END_IF;
Send_Message[1] := INT_TO_BYTE(5); // Адрес слейва
Send_Message[2] := B#16#03; //функция чтения входов
Send_Message[3] := B#16#00; //стартовый адрес hi
Send_Message[4] := B#16#3A; //стартовый адрес lo,
Send_Message[5] := B#16#00; //количество параметров hi
Send_Message[6] := B#16#02; //количество параметров lo
CRCCount :=6;
ELSE:
paket :=0;
RETURN;
END_CASE;
//расчет контрольной суммы
CRC16:=W#16#FFFF;
FOR i := 1 TO CRCCount BY 1 DO
CRC16 := CRC16 XOR Send_Message[i];
FOR k := 1 TO 8 DO
IF (CRC16 AND W#16#0001)=W#16#0000 THEN
CRC16 := SHR (IN:=CRC16, N:=1);
ELSE
CRC16 := SHR (IN:=CRC16, N:=1);
CRC16 := CRC16 XOR W#16#A001;
END_IF;
END_FOR;
END_FOR;
point :=1;
END_IF;//IF point = 0
//-----------------------------------------------------------------
//отправка пакета
IF Point = 1 THEN // Передача данных
CASE paket OF
0,1: //первый пакет
Send_Message[7]:=WORD_TO_BYTE(CRC16);
Send_Message[8]:=WORD_TO_BYTE(SHR (IN:=CRC16, N:=8));
LEN_SEND := 8;
END_CASE;
//берем указатель на буфер отправки для получения номера ДБ
TempPointer := SEND_Message;
SND_DATA(SF := 'S' // IN: CHAR
,REQ := REQ // IN: BOOL
,R := false // IN: BOOL
,LADDR := Addr_CP // IN: INT
,DB_NO := WORD_TO_INT(AnyPointer.db_num)// IN: INT
,DBB_NO := DWORD_TO_INT(AnyPointer.area AND DW#16#7bff_ffff)/8 // IN: INT
,LEN := WORD_TO_INT(LEN_SEND)// IN: INT
);
DONE := SND_DATA.DONE; // OUT: BOOL
Err_S := SND_DATA.ERROR; // OUT: BOOL
STATUS:= SND_DATA.STATUS; // OUT: WORD
IF DONE THEN
Err_Send := false;
ES_CNT :=0;
Point := point+1;
ERR_SND :=0;
ELSIF NOT REQ THEN
REQ := true;
ELSE
REQ := false;
END_IF;
IF Err_S THEN
Err_Send := true;
ERR_SND := STATUS;
ES_CNT := ES_CNT + 1;
IF (ES_CNT < 3) THEN
point :=1; //если счетчик ошибок чтения меньше заданного, то отправляем пакет еще раз
RETURN;
ELSE
//если счетчик равен заданному, то переходим к отправке другого пакета
Point := 0;
paket := paket +1;
ES_CNT := 0;
END_IF;//IF (ES_CNT
END_IF; //Err_S = true
END_IF;//IF Point = 1
//-----------------------------------------------------------------
//прием пакета
IF Point = 2 THEN // Прием данных
//берем указатель на буфер приема для определения номера дб и смещения
TempPointer := RECV_Message;
RCV_DATA(EN_R := true// IN: BOOL
,R := false // IN: BOOL
,LADDR :=Addr_CP // IN: INT
,DB_NO := WORD_TO_INT(AnyPointer.db_num) // IN: INT
,DBB_NO := DWORD_TO_INT(AnyPointer.area AND DW#16#7bff_ffff)/8 // IN: INT
);
NDR := RCV_DATA.NDR; // OUT: BOOL
Err_R := RCV_DATA.ERROR; // OUT: BOOL
//LEN_RECV := INT_TO_WORD(RCV_DATA.LEN); // OUT: INT
STATUS := RCV_DATA.STATUS; // OUT: WORD
IF NDR THEN //если есть подтверждение приема, то переходим к разбору пакета
point := point +1;
Timeout :=0;
ERR_RCV :=0;
Err_Recv :=false;
ELSE //если нет подтверждения
ERR_RCV :=STATUS;
IF Err_R OR (timeout >= 10) THEN //проверяем на ошибку таймаут
ER_CNT := ER_CNT +1;
timeout :=0;
//IF Err_R THEN point :=1; END_IF;
ELSE
timeout :=timeout +1;
END_IF;
IF ER_CNT >=3 THEN //если счетчик ошибок больше заданного, то
timeout :=0;
ER_CNT :=0;
ERR_Recv := true; //и выставляем флаг ошибки чтения
paket := paket +1; //переходим к чтению следующего пакета
point :=0;
//RETURN;
END_IF;
END_IF;//IF NDR
END_IF; //if point =2
//-----------------------------------------------------------------
//разбор принятого пакета
IF point =3 THEN
IF (Err_Send = false) AND (Err_Recv = false) THEN //если нету ошибок приема/передачи
//проверка на корректность отработки функции
IF ((RECV_Message[2] AND b#16#80)<>0) THEN //если есть признак ошибки в ответе
ACK_ERR_CODE := BYTE_TO_INT(RECV_Message[3]);
ACK_ERR := true;
IF ACK_ERR_CNT >=3 THEN
ACK_ERR_CODE := 0;
ACK_ERR_CNT := 0;
ACK_ERR := false;
CASE paket OF
0: //ошибка приёма первого пакета
ERR_P1 := true;
1: //ошибка приёма второго пакета
ERR_P2 := true;
ELSE
;
END_CASE;
point :=0;
paket := paket+1;
RETURN;
ELSE//ACK_ERR_CNT >=3
ACK_ERR_CNT := ACK_ERR_CNT +1;
point := 0;
//RETURN;
END_IF;//ACK_ERR_CNT >=3
RETURN;
ELSE //IF ((RECV_Message[2] если есть признак ошибки в ответе
ACK_ERR_CODE := 0;
ACK_ERR_CNT := 0;
ACK_ERR := false;
END_IF; //IF ((RECV_Message[2] если есть признак ошибки в ответе
//проверка контрольной суммы принятого пакета
//расположение контрольной суммы зависит от вида пакета
CRCCount := BYTE_TO_INT(RECV_Message[3])+3;
//расчет контрольной суммы
CRC16:=W#16#FFFF;
FOR i := 1 TO CRCCount BY 1 DO
CRC16 := CRC16 XOR RECV_Message[i];
FOR k := 1 TO 8 DO
IF (CRC16 AND W#16#0001)=W#16#0000 THEN
CRC16 := SHR (IN:=CRC16, N:=1);
ELSE
CRC16 := SHR (IN:=CRC16, N:=1);
CRC16 := CRC16 XOR W#16#A001;
END_IF;
END_FOR;
END_FOR;
IF (RECV_Message[CRCCount+1] <> WORD_TO_BYTE(CRC16 AND w#16#ff))
OR (RECV_Message[CRCCount+2] <>WORD_TO_BYTE(SHR (IN:=CRC16, N:=8)))
THEN
CRCERR_CNT := CRCERR_CNT +1;
CRCERR := true;
IF (CRCERR_CNT >= 3) THEN
CRCERR_CNT := 0;
CRCERR := false;
//выставление соответствующего бита ошибок
CASE paket OF
0: //ошибка приёма первого пакета
ERR_P1 := true;
1: //ошибка приёма второго пакета
ERR_P2 := true;
ELSE
;
END_CASE;
Paket := Paket+1;
Point := 0;
RETURN;
ELSE
Point := 1;
END_IF;
RETURN;
ELSE//IF (RECV_Message[CRCCount+1]
CRCERR_CNT := 0;
CRCERR := false;
END_IF;//IF (RECV_Message[CRCCount+1]
ERR_P1 := false;
//если не было ошибок, то разбираем принятый пакет
CASE paket OF
0://первый пакет
Byte_to_bit(ibyte := RECV_Message[5] // IN: BYTE
,obit0 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.0 // OUT: BOOL
,obit1 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.1 // OUT: BOOL
,obit2 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.2 // OUT: BOOL
,obit3 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.3 // OUT: BOOL
,obit4 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.4 // OUT: BOOL
,obit5 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.5 // OUT: BOOL
,obit6 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.6 // OUT: BOOL
,obit7 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD1)).dx0.7 // OUT: BOOL
); // VOID
ERR_P1 := false;
1://второй пакет
Byte_to_bit(ibyte := RECV_Message[5] // IN: BYTE
,obit0 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.0 // OUT: BOOL
,obit1 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.1 // OUT: BOOL
,obit2 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.2 // OUT: BOOL
,obit3 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.3 // OUT: BOOL
,obit4 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.4 // OUT: BOOL
,obit5 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.5 // OUT: BOOL
,obit6 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.6 // OUT: BOOL
,obit7 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx1.7 // OUT: BOOL
); // VOID
Byte_to_bit(ibyte := RECV_Message[7] // IN: BYTE
,obit0 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.0 // OUT: BOOL
,obit1 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.1 // OUT: BOOL
,obit2 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.2 // OUT: BOOL
,obit3 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.3 // OUT: BOOL
,obit4 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.4 // OUT: BOOL
,obit5 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.5 // OUT: BOOL
,obit6 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.6 // OUT: BOOL
,obit7 := WORD_TO_BLOCK_DB(INT_TO_WORD(dbD2)).dx2.7 // OUT: BOOL
); // VOID
ERR_P2 := false;
ELSE
point :=0;
paket :=0;
RETURN;
END_CASE;//paket
;
ELSE//если есть ошибки приема/передачи
//выставление соответствующего бита ошибок
CASE paket OF
0: //ошибка приёма первого пакета
ERR_P1 := true;
1: //ошибка приёма второго пакета
ERR_P2 := true;
ELSE
;
END_CASE;
paket :=paket+1;
point :=0;
;
END_IF; //если нет ошибок приема/передачи
paket := paket +1;
IF paket >=2 THEN //переход на начало после приёма последнего пакета
paket :=0;
point :=0;
END_IF;
point:= 0;
END_IF;//IF point =3
;
END_FUNCTION_BLOCK
-
- здесь недавно
- Сообщения: 4
- Зарегистрирован: 15 сен 2014, 07:30
- Имя: Вяткин Олег Николаевич
- Страна: Казахстан
- Благодарил (а): 3 раза
CP341, Опрос Modbus-устройства.
Добрый день, передо мной стоит та же задача, Вы нашли решение проблемы? у меня ср341 вроде общается с терминалом (rx/ tx при отправление како го либо запроса) , но не могу ни как правильно сформировать запрос или ответ получить, вобщем кроме мигания лампочек ни чего не добился.....правильно ли я понимаю, что CP общается с устройством через FB8, FB7? может кто-нибудь сможет доходчиво разжевать формирование запросов-ответов
-
- здесь недавно
- Сообщения: 7
- Зарегистрирован: 05 июн 2017, 08:50
- Имя: Александр
- Страна: Россия
- город/регион: Екатеринбург
CP341, Опрос Modbus-устройства.
Как вы это сделали? Расскажите пожалуйста. Не могу сформировать запрос и получить ответ от Овен ТРМ-138