Started Modbus Master for Levitronix Pumps and Flow sensors

This commit is contained in:
2026-03-09 18:45:56 +01:00
parent e994e9970c
commit 218d815380
11 changed files with 550 additions and 215 deletions

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Levi_ChDStatus" Id="{d1bf4f7b-9f39-4e8d-86cd-899174d80a62}">
<Declaration><![CDATA[{attribute 'pack_mode' := '1'}
TYPE ST_Levi_ChDStatus :
STRUCT
// Detected bubble in sensor. Stays active while bubble detect hold time
bBubbleDetected : BIT;
// Sensor signal is abnormal. (Empty sensor, unplugged sensor, too many bubbles)
bMeasError : BIT;
// Reverse flow
bReverseFlow : BIT;
// Combination of Full scale and Volume Pulse setting is invalid
bVolCntPulseSetError : BIT;
// Zero Adjustment is in progress
bZeroAdjActive : BIT;
// 0: Last Zero Adjustment was successful
// 1: Last Zero Adjustment was not successful because sensor was empty or there were too many bubbles in sensor.
bZeroAdjErr : BIT;
// Parameter set mode active. Settings can be written to Hold register
bSetModeActive : BIT;
// EEPROM writing active
bEEPROMWriting : BIT;
// Measured flow is higher than Alarm High level
bFlowAlarmHigh : BIT;
// Measured flow is lower than Alarm Low level
bFlowAlarmLow : BIT;
// Volume Counter is greater than Volume Counter Alarm H limit (Volume Counter Alarm enabled)
bVolCntAlarmH : BIT;
// Volume Counter is greater than Volume Counter Alarm HH limit (Volume Counter Alarm enabled)
bVolCntAlarmHH : BIT;
// Analog or digital test output is active.
bOutputTest : BIT;
// EEPROM error active
bEEPROMErr : BIT;
// Short circuit detected
bShortCircuit : BIT;
// Device is in firmware download mode
bFirmwareUpdateAct : BIT;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<DUT Name="ST_Levi_ChData" Id="{7bd2984e-f05d-431f-83f1-90d65fb7d1bc}">
<Declaration><![CDATA[{attribute 'pack_mode' := '1'}
TYPE ST_Levi_ChData :
STRUCT
stStatus : ST_Levi_ChDStatus;
siCurrFlow : SINT;
dwPulseCounter : DWORD;
END_STRUCT
END_TYPE
]]></Declaration>
</DUT>
</TcPlcObject>

View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
<POU Name="FB_Levi_Flowsensor" Id="{650b4e84-15bc-4cc6-bda5-d5ab333779ee}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_Levi_LFC6IO
VAR_INPUT
// Base station address
byBaseAddr : BYTE;
xReleaseAlarms : BOOL;
xConfirmAlarms : BOOL;
END_VAR
VAR_IN_OUT
// Modbus master comm fb
fbMBMaster : ModbusRtuMasterV2_KL6x22B;
END_VAR
VAR_OUTPUT
// Current flowrate in ml/min
rCurrFlowrate : ARRAY[0..5] OF REAL;
xBusy : BOOL;
xDone : BOOL;
xError : BOOL;
END_VAR
VAR
// Full scale value of all channels in ml/min
_dwFullScale : DWORD;
// Channel data
_astChannelData : ARRAY[0..5] OF ST_Levi_ChData;
// Temporary data storage for writing settings
_awTempData : ARRAY[0..1] OF WORD;
// State machine state
_iState : INT;
// Internal commands
_eCmd : (NONE, SET_FULL_SCALE, );
END_VAR
VAR CONSTANT
REG_SET_MODE : WORD := 0;
REG_FULL_SCALE : WORD := 2;
REG_CH_DATA : WORD := 0;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[CASE _iState OF
// Wait for Modbus master to be ready
0:
IF (NOT fbMBMaster.BUSY)THEN
// Get full scale value for later flow calculation
fbMBMaster.UnitID := byBaseAddr;
fbMBMaster.MBAddr := REG_FULL_SCALE;
fbMBMaster.Quantity := 2;
fbMBMaster.cbLength := SIZEOF(_dwFullScale);
fbMBMaster.pMemoryAddr := ADR(_dwFullScale);
_iState := 10;
END_IF
// Wait for reading done
10:
fbMBMaster.ReadRegs();
IF (NOT fbMBMaster.BUSY) THEN
_iState := 20;
END_IF
IF fbMBMaster.Error THEN
_iState := 900;
END_IF
// Poll channel data
20:
IF THEN
fbMBMaster.UnitID := byBaseAddr;
fbMBMaster.MBAddr := REG_CH_DATA;
fbMBMaster.Quantity := 24;
fbMBMaster.cbLength := SIZEOF(_astChannelData);
fbMBMaster.pMemoryAddr := ADR(_astChannelData);
// Call once to set MB Master to busy
fbMBMaster.ReadInputRegs();
_iState := 21;
ELSIF (_eCmd <> NONE) AND (NOT fbMBMaster.BUSY) THEN
_iState := 30;
END_IF
// Wait for response
21:
fbMBMaster.ReadInputRegs();
IF (NOT fbMBMaster.BUSY) THEN
// Calculate output flowrates
M_CalcOutputData();
_iState := 20;
END_IF
IF fbMBMaster.Error THEN
_iState := 900;
END_IF
// Command handling -> Set mode
30:
// Set bit 1 to 1 for Set Mode
_awTempData[0] := 2#10;
fbMBMaster.UnitID := byBaseAddr;
fbMBMaster.MBAddr := REG_SET_MODE;
fbMBMaster.Quantity := 1;
fbMBMaster.cbLength := 2;
fbMBMaster.pMemoryAddr := ADR(_awTempData[0]);
// Error state
900:
IF xConfirmAlarms THEN
xError := FALSE;
_iState := 0;
END_IF
END_CASE]]></ST>
</Implementation>
<Method Name="M_CalcOutputData" Id="{5b932e1a-1437-4032-adb5-2eae4b98574c}">
<Declaration><![CDATA[METHOD PRIVATE M_CalcOutputData
VAR
i : int;
END_VAR]]></Declaration>
<Implementation>
<ST><![CDATA[FOR i := 0 TO 5 DO
// Current flowrate from channel data is in % of full scale (-30.000 ~ 30.000 => (-300 ~ +300) %
// _Full scale is in l/min but scaling is as followed: 10 ~ 500000 => (0.010 ~ 500) l/min => (10 ~ 500.000) ml/min
// Result is in ml/min
rCurrFlowrate[i] := (SINT_TO_REAL(_astChannelData[i].siCurrFlow) * DWORD_TO_REAL(_dwFullScale)) / 10_000;
END_FOR
]]></ST>
</Implementation>
</Method>
<Method Name="M_ReadSensorData" Id="{2cd73435-5e66-4519-8bdc-106eb3093539}">
<Declaration><![CDATA[METHOD M_ReadSensorData : BOOL
VAR_INPUT
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[IF xBusy THEN
M_ReadSensorData := FALSE;
RETURN;
END_IF
]]></ST>
</Implementation>
</Method>
<Method Name="M_SetFullScale" Id="{ee32b999-71fe-4e16-ab8c-09e92e1f41ef}">
<Declaration><![CDATA[METHOD M_SetFullScale : BOOL
VAR_INPUT
// Full scale in ml/min
siFullScale : SINT;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[]]></ST>
</Implementation>
</Method>
</POU>
</TcPlcObject>

View File

@@ -33,6 +33,9 @@ VAR
// (9) Filmetch
_fbTankFilmetch : FB_Tank;
// Flowsensors
END_VAR
]]></Declaration>