mirror of
https://github.com/sergeyyarkov/IM151-ModbusTCP-Multigraph.git
synced 2026-10-11 02:09:14 +03:00
project
This commit is contained in:
commit
da1a2a5897
BIN
Im151_mo.zip
Normal file
BIN
Im151_mo.zip
Normal file
Binary file not shown.
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# IM151-ModbusTCP-Multigraph
|
||||
|
||||
Пример чтения данных по протоколу Modbus для прибора Мультиграф Сталь без использования библиотеки Modbus.
|
||||
Опрос данных осуществлятся за счет следующих функций: `TCON`, `TRCV`, `TSEND` и `TDISCONN`.
|
||||
|
||||
[Ссылка на прибор.](https://tpchel.ru/Multigraf_stal)
|
||||
|
||||
[Карта Modbus регистров](./modbus-register-map.pdf)
|
||||
|
||||
В репозитории находится проект Step7 v5.6 для `IM151-8F PN/DP CPU`.
|
||||
BIN
modbus-register-map.pdf
Normal file
BIN
modbus-register-map.pdf
Normal file
Binary file not shown.
402
read.SCL
Normal file
402
read.SCL
Normal file
@ -0,0 +1,402 @@
|
||||
(* Текущий отправляемый запрос на получение значения по адресу Modbus. *)
|
||||
TYPE UDT_MbCurrReqData
|
||||
STRUCT
|
||||
FnCode : BYTE := B#16#0;
|
||||
StartAddr : WORD := W#16#0;
|
||||
QtyRegs : WORD := W#16#0;
|
||||
END_STRUCT
|
||||
END_TYPE
|
||||
|
||||
(* Структура Modbus TCP пакета. *)
|
||||
TYPE UDT_MbTCPAduReq
|
||||
STRUCT
|
||||
TransactionId : WORD := W#16#1;
|
||||
ProtocolType : WORD := W#16#0;
|
||||
DataLength : WORD := W#16#6;
|
||||
UnitId : BYTE := B#16#0;
|
||||
FnCode : BYTE := B#16#4;
|
||||
StartAddr : WORD := W#16#0;
|
||||
QtyRegs : WORD := W#16#0;
|
||||
END_STRUCT
|
||||
END_TYPE
|
||||
|
||||
(* Параметры TCP соединения. *)
|
||||
TYPE TCON_PAR
|
||||
STRUCT
|
||||
(* Размер типа `TCON_PAR`. Всегда 64 байт*)
|
||||
block_length : WORD := W#16#40;
|
||||
|
||||
(* Идентификатор соединения. От W#16#1 до W#16#0FFF *)
|
||||
id : WORD := W#16#1;
|
||||
|
||||
(*
|
||||
* Тип соединения.
|
||||
* W#16#11 — TCP/IP native
|
||||
* W#16#12 — ISO on TCP
|
||||
* W#16#13 — UDP
|
||||
* W#16#01 — TCP/IP native (режим совместимости)
|
||||
*
|
||||
* Примечание:
|
||||
* TCP (compatibility mode): B#16#01 for CPU 315 or 317 <= FW V2.3
|
||||
* TCP: B#16#11 for CPU 315 or 317 >= FW V2.4,
|
||||
* IM 151-8 PN/DP CPU, CPU314C, CPU319, CPU412(H), CPU414(H), CPU416(H), CPU417(H)
|
||||
* These specifications data may vary depending on the firmware used.
|
||||
* For additional information, refer to:
|
||||
* http://support.automation.siemens.com/WW/view/en/24294554
|
||||
*
|
||||
*)
|
||||
connection_type : BYTE := B#16#11;
|
||||
|
||||
(*
|
||||
* Идентификатор типа установления соединения.
|
||||
* Для TCP и ISO on TCP:
|
||||
* TRUE: Активная установка соединения (клиент).
|
||||
* FALSE: Пассивная установка соединения (сервер).
|
||||
*
|
||||
* Для UDP — всегда FALSE.
|
||||
*)
|
||||
active_est : BOOL := FALSE;
|
||||
|
||||
(*
|
||||
* Идентификатор PN/IE интерфейса. Находится в конфигурации HW как номер слота:
|
||||
* IM 151-8 PN/DP CPU — B#16#1
|
||||
* CPU 314C, 315 или 317 — B#16#2
|
||||
* CPU 319 — B#16#3
|
||||
* CPU 412(H), 414(H), 416(H) and 417(H) — B#16#5
|
||||
* CPU в стойке 1 станции H — B#16#15
|
||||
*
|
||||
* Примечание:
|
||||
* In H stations: The S7400 PN CPU in rack 0 communicates via local_device_id = 5 and the
|
||||
* CPU in rack 1 communicates via local_device_id = 15Hex.
|
||||
*)
|
||||
local_device_id : BYTE := B#16#5;
|
||||
|
||||
(*
|
||||
* Размер параметра `local_tsap_id` в байтах:
|
||||
* Для TCP — 0 или 2 (для активного соединения допустимо только 0).
|
||||
* Для ISO on TCP — 2 или 16.
|
||||
* Для UDP — 2.
|
||||
*)
|
||||
local_tsap_id_len : BYTE := B#16#2;
|
||||
|
||||
(* Параметр не используется и должно присвоено значение 0. *)
|
||||
rem_subnet_id_len : BYTE := B#16#0;
|
||||
|
||||
(* Размер параметра `rem_staddr`:
|
||||
* Неустановленное соединение — B#16#0
|
||||
* Установленное — B#16#4
|
||||
*)
|
||||
rem_staddr_len : BYTE := B#16#0;
|
||||
|
||||
(* Размер параметра `rem_tsap_id`. Активное соединения — 2, пассивное — 0. *)
|
||||
rem_tsap_id_len : BYTE := B#16#0;
|
||||
|
||||
(* Размер параметра `next_staddr`. Для PN интерфейса — B#16#0. *)
|
||||
next_staddr_len : BYTE := B#16#0;
|
||||
|
||||
(* Параметр используется для установки локального порта. В режиме сервера (пассивное соединение).
|
||||
*
|
||||
* Для `connection_type` W#16#01:
|
||||
* local_tsap_id[1] младший байт порта в hex
|
||||
* local_tsap_id[2] старший байт порта в hex
|
||||
* local_tsap_id[3-16] B#16#00
|
||||
*
|
||||
* Для `connection_type` W#16#11:
|
||||
* local_tsap_id[1] старший байт порта в hex
|
||||
* local_tsap_id[2] младший байт порта в hex
|
||||
* local_tsap_id[3-16] B#16#00
|
||||
*)
|
||||
local_tsap_id : ARRAY[1..16] OF BYTE;
|
||||
|
||||
(* Параметр не используется и должно присвоено значение 0. *)
|
||||
rem_subnet_id : ARRAY[1..6] OF BYTE;
|
||||
|
||||
(* Массив байтов IP адреса удаленного партнера. IP адрес не назначается для неустановленного соединения.
|
||||
* Порядок байтов зависит от `connection_type`.
|
||||
* Пример для IP адреса 192.168.0.1:
|
||||
* Для `connection_type` — B#16#01
|
||||
* rem_staddr[1] = B#16#01 (1),
|
||||
* rem_staddr[2] = B#16#00 (0),
|
||||
* rem_staddr[3] = B#16#A8 (168),
|
||||
* rem_staddr[4] = B#16#C0 (192),
|
||||
* rem_staddr[5-6]= B#16#00 (reserved)
|
||||
|
||||
* Для `connection_type` — B#16#11
|
||||
* rem_staddr[1] = B#16#C0 (192),
|
||||
* rem_staddr[2] = B#16#A8 (168),
|
||||
* rem_staddr[3] = B#16#00 (0),
|
||||
* rem_staddr[4] = B#16#01 (1),
|
||||
* rem_staddr[5-6]= B#16#00 (reserved)
|
||||
*)
|
||||
rem_staddr : ARRAY[1..6] OF BYTE;
|
||||
|
||||
(*
|
||||
* Параметр используется для установки порта партнера. Порядок байтов зависит от `connection_type`. Диапозон зависит от CPU.
|
||||
*
|
||||
* Для `connection_type` — B#16#01:
|
||||
* local_tsap_id[1] младший байт порта в hex
|
||||
* local_tsap_id[2] старший байт порта в hex
|
||||
* local_tsap_id[3-16] B#16#00
|
||||
*
|
||||
* Для `connection_type` — B#16#11
|
||||
* local_tsap_id[1] старший байт порта в hex
|
||||
* local_tsap_id[2] младший байт порта в hex
|
||||
* local_tsap_id[3-16] B#16#00
|
||||
*)
|
||||
rem_tsap_id : ARRAY[1..16] OF BYTE;
|
||||
|
||||
(* Этот параметр определяет номер стойки и слота коммуникационного процессора который будет использоваться.
|
||||
* Если CPU использует встроенный PN интерфейс, значение параметра должно быть 0.
|
||||
*)
|
||||
next_staddr : ARRAY[1..6] OF BYTE;
|
||||
|
||||
(* Параметр не используется и должно присвоено значение 0. *)
|
||||
spare : WORD := W#16#0;
|
||||
END_STRUCT
|
||||
END_TYPE
|
||||
|
||||
(* Блок DB хранения данных о приборе. *)
|
||||
DATA_BLOCK "multigraphSteelDataDB"
|
||||
STRUCT
|
||||
IP : ARRAY[1..6] OF BYTE := B#16#C0, B#16#A8, B#16#0B, B#16#02; // IP адрес: 192.168.11.2
|
||||
SerialNumber : DWORD; // Серийный номер прибора
|
||||
DeviceType : WORD; // Тип прибора
|
||||
SteelTempSquare : REAL; // Измеренная площадка по температуре в град.
|
||||
CurrDate : DWORD; // Текущая дата прибора
|
||||
CurrTime : DWORD; // Текущее время прибора
|
||||
END_STRUCT
|
||||
BEGIN
|
||||
END_DATA_BLOCK
|
||||
|
||||
FUNCTION_BLOCK "multigraphSteelReadFB"
|
||||
|
||||
TITLE = 'multigraphSteelRead'
|
||||
VERSION: '0.1'
|
||||
VAR_INPUT
|
||||
netId : WORD := W#16#1; // Идентификатор сетевого подключения
|
||||
ip : ARRAY[1..6] OF BYTE;
|
||||
unitId : BYTE; // Идентификатор подчиненного Modbus устройства, всегда `1`
|
||||
intervalBit : BOOL; // Чтение данных по запросу
|
||||
connect : BOOL; // Запрос за подключение к прибору
|
||||
END_VAR
|
||||
|
||||
VAR_OUTPUT
|
||||
netError : BOOL; // Ошибка соединения
|
||||
netStatus : WORD; // Статус ошибки подключения
|
||||
isConnected : BOOL; // Статус подключения
|
||||
isBusy : BOOL;
|
||||
rdError : BYTE; // Код ошибки Modbus
|
||||
END_VAR
|
||||
|
||||
CONST
|
||||
mbRdInputRegsFn := 16#04; // Код функции Modbus на чтение входных регистров
|
||||
mbHRRegsFn := 16#03; // Код функции Modbus на чтение и запись регистров хранения
|
||||
mbADULength := 12; // Кол-во байт при отправке TCP пакета
|
||||
mbMaxResLength := 260; // Кол-во байт в ответе от сервера
|
||||
mbMaxReqRegsLength := 5; // Максимальное кол-во опрашиваемых адресов
|
||||
|
||||
(*
|
||||
* Modbus адреса прибора.
|
||||
* Карта регистров: https://tpchel.ru/upload/news/2024/2.556.124%20%D0%9F%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB%20Modbus_%D0%9C-%D0%A1%D0%A2%D0%90%D0%9B%D0%AC_05.02.2024.pdf
|
||||
*)
|
||||
mbDeviceTypeAddr := W#16#0006; // Тип прибора / Формат: U16
|
||||
mbSerialNumberAddr := W#16#0047; // Серийный номер прибора / Формат: U32
|
||||
mbSteelTempSquareAddr := W#16#1388; // Измеренная площадка по температуре в град. / Формат: F32
|
||||
mbCurrDateAddr := W#16#0069; // Текущая дата прибора / Формат: U32 / (1-2й байты - год(старший), 3й байт - месяц, 4й байт - день)
|
||||
mbCurrTimeAddr := W#16#006B; // Текущее время прибора / Формат: U32 / (1й байты - час (старший), 2й байт - минута, 3й байт - секунда, 4й - не исп.)
|
||||
END_CONST
|
||||
|
||||
VAR
|
||||
netConnect : TCON;
|
||||
netDisconn : TDISCON;
|
||||
netSend : TSEND;
|
||||
netReceive : TRCV;
|
||||
netConfig : "TCON_PAR";
|
||||
onRisingEdgeBit : BOOL;
|
||||
intervalBitLastScan : BOOL;
|
||||
isReqJobDone : BOOL;
|
||||
isReqSent : BOOL := FALSE;
|
||||
mbReqADU : "UDT_MbTCPAduReq";
|
||||
mbRawResADU : ARRAY[1..mbMaxResLength] OF BYTE;
|
||||
mbReqRegisters : ARRAY[1..mbMaxReqRegsLength] OF "UDT_MbCurrReqData"; // Опрашиваемые адреса
|
||||
mbReqIdx : INT := 1;
|
||||
END_VAR
|
||||
|
||||
VAR_TEMP
|
||||
i : INT;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
isReqJobDone := netConnect.DONE;
|
||||
isBusy := netConnect.BUSY;
|
||||
netError := netConnect.ERROR;
|
||||
netStatus := netConnect.STATUS;
|
||||
|
||||
IF NOT isConnected THEN
|
||||
IF NOT netConnect.BUSY THEN
|
||||
netDisconn(ID := netId, REQ := intervalBit);
|
||||
isReqSent := FALSE;
|
||||
END_IF;
|
||||
|
||||
(* ** Конфигурирование параметров TCP *************************************************** *)
|
||||
netConfig.block_length := W#16#40;
|
||||
netConfig.connection_type := B#16#11; // TCP
|
||||
netConfig.id := netId;
|
||||
netConfig.rem_subnet_id_len := 0;
|
||||
netConfig.local_tsap_id_len := 0;
|
||||
netConfig.local_device_id := B#16#01; // Slot ID (X1) / IM 151-8 PN/DP CPU
|
||||
|
||||
(* IP адрес прибора *)
|
||||
netConfig.rem_staddr := ip;
|
||||
netConfig.rem_staddr_len := B#16#4;
|
||||
|
||||
(* Порт прибора *)
|
||||
netConfig.rem_tsap_id[1] := B#16#01;
|
||||
netConfig.rem_tsap_id[2] := B#16#F6;
|
||||
netConfig.rem_tsap_id_len := B#16#02;
|
||||
|
||||
(* Клиент *)
|
||||
netConfig.active_est := TRUE;
|
||||
|
||||
(* ** Конфигурирование адресов Modbus прибора ******************************************* *)
|
||||
mbReqRegisters[1].FnCode := mbRdInputRegsFn;
|
||||
mbReqRegisters[1].StartAddr := mbDeviceTypeAddr;
|
||||
mbReqRegisters[1].QtyRegs := W#16#0001;
|
||||
|
||||
mbReqRegisters[2].FnCode := mbRdInputRegsFn;
|
||||
mbReqRegisters[2].StartAddr := mbSerialNumberAddr;
|
||||
mbReqRegisters[2].QtyRegs := W#16#0002;
|
||||
|
||||
mbReqRegisters[3].FnCode := mbRdInputRegsFn;
|
||||
mbReqRegisters[3].StartAddr := mbSteelTempSquareAddr;
|
||||
mbReqRegisters[3].QtyRegs := W#16#0002;
|
||||
|
||||
mbReqRegisters[4].FnCode := mbHRRegsFn;
|
||||
mbReqRegisters[4].StartAddr := mbCurrDateAddr;
|
||||
mbReqRegisters[4].QtyRegs := W#16#0002;
|
||||
|
||||
mbReqRegisters[5].FnCode := mbHRRegsFn;
|
||||
mbReqRegisters[5].StartAddr := mbCurrTimeAddr;
|
||||
mbReqRegisters[5].QtyRegs := W#16#0002;
|
||||
|
||||
netConnect(REQ := connect, ID := netId, CONNECT := netConfig);
|
||||
|
||||
(* Инициализация на первый запрос *)
|
||||
mbReqADU.UnitId := unitId;
|
||||
mbReqADU.TransactionId := 1;
|
||||
mbReqIdx := 1;
|
||||
mbReqADU.FnCode := mbReqRegisters[mbReqIdx].FnCode;
|
||||
mbReqADU.StartAddr := mbReqRegisters[mbReqIdx].StartAddr;
|
||||
mbReqADU.QtyRegs := mbReqRegisters[mbReqIdx].QtyRegs;
|
||||
END_IF;
|
||||
|
||||
isConnected := netConnect.STATUS = 0 AND netConnect.ERROR = 0;
|
||||
onRisingEdgeBit := intervalBit AND NOT intervalBitLastScan;
|
||||
|
||||
IF isConnected THEN
|
||||
(* ** Прием данных *********************************************************************************************************************************************************** *)
|
||||
|
||||
(* 1. При получении данных идет проверка на ошибку Modbus и случае ошибки от сервера Modbus `rdError` будет его кодом (https://www.simplymodbus.ca/exceptions.htm).
|
||||
* 2. Если ошибка отсутствует, то происходит проверка запрошенного адреса с адресом в массиве `mbReqRegisters`, и происходит чтение данных.
|
||||
* 3. После успешного чтения происходит запрос другого Modbus адреса.
|
||||
*)
|
||||
netReceive(ID := netId, EN_R := TRUE, DATA := mbRawResADU, LEN := 0); // данные приходят асинхронно.
|
||||
IF netReceive.NDR THEN
|
||||
IF BYTE_TO_BOOL(SHR(IN := mbRawResADU[8], N := 7)) AND 1 THEN
|
||||
rdError := mbRawResADU[9];
|
||||
ELSE
|
||||
rdError := 0;
|
||||
|
||||
IF mbReqADU.StartAddr = mbSerialNumberAddr THEN
|
||||
"multigraphSteelDataDB".SerialNumber := READ_U32_BE(BYTE1 := mbRawResADU[10], BYTE2 := mbRawResADU[11], BYTE3 := mbRawResADU[12], BYTE4 := mbRawResADU[13]);
|
||||
ELSIF mbReqADU.StartAddr = mbDeviceTypeAddr THEN
|
||||
"multigraphSteelDataDB".DeviceType := READ_U16_BE(BYTE1 := mbRawResADU[10], BYTE2 := mbRawResADU[11]);
|
||||
ELSIF mbReqADU.StartAddr = mbSteelTempSquareAddr THEN
|
||||
"multigraphSteelDataDB".SteelTempSquare := READ_F32(BYTE1 := mbRawResADU[10], BYTE2 := mbRawResADU[11], BYTE3 := mbRawResADU[12], BYTE4 := mbRawResADU[13]);
|
||||
ELSIF mbReqADU.StartAddr = mbCurrDateAddr THEN
|
||||
"multigraphSteelDataDB".CurrDate := READ_U32_BE(BYTE1 := mbRawResADU[10], BYTE2 := mbRawResADU[11], BYTE3 := mbRawResADU[12], BYTE4 := mbRawResADU[13]);
|
||||
ELSIF mbReqADU.StartAddr = mbCurrTimeAddr THEN
|
||||
"multigraphSteelDataDB".CurrTime := READ_U32_BE(BYTE1 := mbRawResADU[10], BYTE2 := mbRawResADU[11], BYTE3 := mbRawResADU[12], BYTE4 := mbRawResADU[13]);
|
||||
END_IF;
|
||||
|
||||
FOR i := 1 TO mbMaxResLength DO mbRawResADU[i] := 0; END_FOR;
|
||||
|
||||
mbReqIdx := mbReqIdx + 1;
|
||||
|
||||
IF mbReqIdx > mbMaxReqRegsLength THEN
|
||||
mbReqIdx := 1;
|
||||
END_IF;
|
||||
|
||||
(* Следующий запрос *)
|
||||
mbReqADU.FnCode := mbReqRegisters[mbReqIdx].FnCode;
|
||||
mbReqADU.StartAddr := mbReqRegisters[mbReqIdx].StartAddr;
|
||||
mbReqADU.QtyRegs := mbReqRegisters[mbReqIdx].QtyRegs;
|
||||
END_IF;
|
||||
END_IF;
|
||||
(* *************************************************************************************************************************************************************************** *)
|
||||
|
||||
(* ** Отправка на запрос адреса Modbus *************************************************************************************************************************************** *)
|
||||
IF netSend.ERROR THEN
|
||||
isConnected := FALSE;
|
||||
END_IF;
|
||||
|
||||
(*IF onRisingEdgeBit THEN
|
||||
IF netSend.BUSY THEN
|
||||
RETURN; // Не отсылать новый запрос если предыдущий еще не отправился
|
||||
ELSE
|
||||
IF isReqSent THEN
|
||||
RETURN; // Не отсылать новый запрос запрос если придыдущий успешно отправлен но данные еще не пришли.
|
||||
END_IF;
|
||||
isReqSent := TRUE;
|
||||
END_IF;
|
||||
END_IF;*)
|
||||
|
||||
netSend(ID := netId, LEN := mbADULength, REQ := onRisingEdgeBit, DATA := mbReqADU);
|
||||
|
||||
IF netSend.DONE THEN
|
||||
isReqSent := FALSE;
|
||||
mbReqADU.TransactionId := INT_TO_WORD(WORD_TO_INT(mbReqADU.TransactionId) + 1);
|
||||
END_IF;
|
||||
|
||||
intervalBitLastScan := intervalBit;
|
||||
END_IF;
|
||||
(* *************************************************************************************************************************************************************************** *)
|
||||
END_FUNCTION_BLOCK
|
||||
|
||||
FUNCTION "READ_U16_BE" : WORD
|
||||
VAR_INPUT
|
||||
BYTE1 : BYTE;
|
||||
BYTE2 : BYTE;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
READ_U16_BE := SHL(IN := BYTE_TO_WORD(BYTE1), N := 8) OR BYTE_TO_WORD(BYTE2);
|
||||
END_FUNCTION
|
||||
|
||||
FUNCTION "READ_U32_BE" : DWORD
|
||||
VAR_INPUT
|
||||
BYTE1 : BYTE;
|
||||
BYTE2 : BYTE;
|
||||
BYTE3 : BYTE;
|
||||
BYTE4 : BYTE;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
READ_U32_BE := SHL(IN := BYTE_TO_DWORD(BYTE1), N := 24) OR SHL(IN := BYTE_TO_DWORD(BYTE2), N := 16) OR SHL(IN := BYTE_TO_DWORD(BYTE3), N := 8) OR BYTE_TO_DWORD(BYTE4);
|
||||
END_FUNCTION
|
||||
|
||||
FUNCTION "READ_F32" : REAL
|
||||
VAR_INPUT
|
||||
BYTE1 : BYTE;
|
||||
BYTE2 : BYTE;
|
||||
BYTE3 : BYTE;
|
||||
BYTE4 : BYTE;
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
READ_F32 := DWORD_TO_REAL(IN := SHL(IN := BYTE_TO_DWORD(BYTE1), N := 24) OR SHL(IN := BYTE_TO_DWORD(BYTE2), N := 16) OR SHL(IN := BYTE_TO_DWORD(BYTE3), N := 8) OR BYTE_TO_DWORD(BYTE4));
|
||||
END_FUNCTION
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user