Comprehensive PLC Utility Library for CoDeSys
Sergey A Glukhov
true
Anjlab
ACB
Comprehensive PLC Utility Library for CoDeSys
reStructuredText
888cbf53-45a1-47de-9a2a-fbd72e84a196
false
qualified-access-only
AnjlabCodeBox
AnjLabCodeBox
false
https://github.com/anjlab/codebox
false
Anjlab Code Box
3.5.17.3
Raw input signal (may contain noise)
Delay time for rising edge (TON)
Delay time for falling edge (TOF)
Filtered (debounced) output
On-delay timer (for rising edge)
Off-delay timer (for falling edge)
This function block implements a digital input debouncer using both TON and TOF timers. It filters signal noise on both rising and falling edges.
// Rising edge handling start TON if input is TRUE and output is still FALSE
tonTimer(IN := inputSignal AND NOT debouncedOutput, PT := onDelay);
// Falling edge handling start TOF if input is FALSE and output is still TRUE
tofTimer(IN := NOT inputSignal AND debouncedOutput, PT := offDelay);
// Update the output based on timers
IF tonTimer.IN AND tonTimer.Q THEN
debouncedOutput := TRUE;
ELSIF tofTimer.IN AND tofTimer.Q THEN
debouncedOutput := FALSE;
END_IF;
2d459604-36a3-45db-9800-9a6a611bcd75
Raw input signal (may contain noise)
Debounce time for rising edge
Debounce time for falling edge
Debounced output signal
One-cycle pulse on rising edge
One-cycle pulse on falling edge
Previous state of debounced output
Uses DebounceBool internally to filter input signal, then detects rising and falling edges on filtered signal.
fbDebounceBool(
inputSignal := inputSignal,
onDelay := onDelay,
offDelay := offDelay,
debouncedOutput => debouncedOutput
);
// Edge detection on filtered output
risingEdge := NOT prevDebounced AND debouncedOutput;
fallingEdge := prevDebounced AND NOT debouncedOutput;
prevDebounced := debouncedOutput;
f0c530e2-1802-4730-a7c1-9e5a02094132
Input signal
Output signal
Start time
Signal memory
Double click
output := FALSE;
IF input AND NOT memory THEN
IF (TIME() - startTime) > T#100MS AND (TIME() - startTime) < T#800MS THEN
output := TRUE;
END_IF;
startTime := TIME();
END_IF;
memory := input;
d6c942b4-f877-45a8-aaa5-9840654778e6
Input value
Buffer size minimum 32 maximum 1000
Reset buffer
Filtered output value
This function stores up to 32 values in a buffer and outputs the average value. You can decide how many values to store to get the average number.
// limit the bufferSize
bufferSize := MIN(MAX(bufferSize, 1000), 32);
// Initialize
IF NOT isInitialized OR reset THEN
isInitialized := TRUE;
FOR counter := 1 TO bufferSize DO
buffer[counter] := input;
END_FOR;
sum := input * bufferSize;
output := input;
RETURN;
END_IF;
IF counter > bufferSize THEN
counter := 1;
ELSE
counter := counter + 1;
END_IF
sum := sum + input - buffer[counter];
{warning disable C0195}
output := DWORD_TO_WORD(sum / bufferSize);
buffer[counter] := input;
216bb070-e763-45f3-ab22-f6ba447a7047
Input value
Time interval
Filtered output value
Returns the average value over a specified time interval.
currentTime := TIME_TO_DWORD(TIME());
// Initialize
IF NOT isInitialized OR timeInterval = T#0s THEN
isInitialized := TRUE;
lastTime := currentTime;
output := input;
ELSIF output = input THEN
lastTime := currentTime;
ELSE
tempTime := WORD_TO_DWORD(input - output) * (currentTime - lastTime) /
TIME_TO_DWORD(timeInterval);
IF tempTime <> 0 THEN
output := DINT_TO_WORD(WORD_TO_DINT(output) + DWORD_TO_DINT(tempTime));
lastTime := currentTime;
END_IF;
END_IF;
b0642b57-9ee0-4a1f-a6aa-0d37014eaf0e
Input signal
Press time
Output signal
Start time
Signal memory
Long click
IF input AND NOT memory THEN
startTime := TIME();
END_IF;
memory := input;
output := input AND ((TIME() - startTime) > pressTime);
65c343db-a1af-4db4-a7ad-a65316c3f838
Start generator
Pulse time
Output signal
Creates pulses with specified frequency
PwmClock(enable := TRUE, pulseTime := T#5ms); Output Q will have one PLC cycle pulse every 5 milliseconds.
currentTime := TIME();
IF enable AND NOT memory THEN
lastTime := currentTime - pulseTime;
END_IF;
memory := enable;
output := currentTime - lastTime >= pulseTime;
IF output THEN lastTime := currentTime; END_IF;
69fbfb76-4eb7-4d83-95c1-21404034352c
Frequency, times per second (Hz)
Duty cycle
Output signal
creates a PWM signal of a given frequency with a ratio offset. Each PWM cycle can be divided into 2 phases: Ton (on time) and Toff (off time).
The ratio is set from 0 to 1. For example, a ratio of 0.5 will divide the time of each PWM cycle into 50% for Ton and 50% for Toff .
// If the PWM frequency is 0, then we interrupt the execution of the FB
IF frequency <= 0.0 THEN
output := FALSE;
RETURN;
END_IF;
// Calculate how many milliseconds are required for one PWM cycle
tempValue := 1000.0 / frequency;
// Launching the signal generator
clock(pulseTime := REAL_TO_TIME(tempValue));
// We create a sustained pulse for the required time
pulseTimer(in := clock.output, pt := REAL_TO_TIME(tempValue * dutyCycle), Q => output);
b2c549b4-3ee4-4a60-a429-c5dda1b73017
Frequency (Hz)
Ton phase length
Output signal
Creates PWM signal with specified Ton phase time. For example, we can set F to 100 times per second. This means one PWM cycle will be 10
milliseconds. Now if we set PW to T#5ms, we'll get equal time for Ton and Toff phases at 50%.
// If the PWM frequency is 0 or the time of one cycle is less than the time for the Ton phase, then we interrupt the execution of the FB
IF frequency <= 0.0 OR (REAL_TO_TIME(1000.0 / frequency) < pulseWidth) THEN
output := FALSE;
RETURN;
END_IF;
// Start the signal generator
clock(pulseTime := REAL_TO_TIME(1000.0 / frequency));
// We create a sustained pulse for the required time
pulseTimer(in := clock.output, PT := pulseWidth, Q => output);
453b2034-7959-4690-aff2-788eefb570f6
Latitude
Longitude
Current time in UTC
Degree above horizon at which sunset or sunrise is considered to occur
Time of solar noon
Sunrise time
Sunset time
Sun declination angle at solar noon in degrees
Sun declination angle at noon in radians
Time delta from solar noon to sunrise or sunset
Calculate sunset and sunrise times
midday := SunMidday(longitude, currentTime);
b := latitude * 0.0174532925199433;
dk := 0.40954 * SIN(0.0172 * (UINT_TO_REAL(DayOfYear(currentTime)) - 79.35));
sunDeclination := RadiansToDegrees(DK);
IF sunDeclination > 180.0 THEN
sunDeclination := sunDeclination - 360.0;
END_IF;
sunDeclination := 90.0 - latitude + sunDeclination;
delta := HourToTime(REAL_TO_INT(ACOS((SIN(DegreesToRadians(h)) - SIN(b) * SIN(dk)) / (COS(b) * COS(dk))) * 3.819718632));
sunRise := midday - delta;
sunSet := midday + delta;
cce3bab6-f4dc-4237-acc9-88ac792521e0
IP address as string, for example, '192.168.1.2'
Convert IP address to hexadecimal format
position := FIND(ipStr, '.');
WHILE position > 0 DO
IpDecode := SHL(IpDecode, 8) OR
STRING_TO_DWORD(LEFT(ipStr, position - 1));
ipStr := DELETE(ipStr, position, 1);
position := FIND(ipStr, '.');
END_WHILE;
IpDecode := SHL(IpDecode, 8) OR STRING_TO_DWORD(ipStr);
5816bee8-262c-48e8-8b13-2203371ffb56
Input value
Rounds the value and returns the nearest integer value that is greater than or equal to X.
ceil(3.14) = 4 ceil(-3.14) = -3
Ceil := REAL_TO_DINT(input);
IF DINT_TO_REAL(Ceil) < input THEN
Ceil := Ceil + 1;
END_IF;
78816cb7-8bcc-46ba-a32f-d1dc6fe2b231
Input value
Calculates with base 10 to the power.
exp10(2) = 100 exp10(3) = 1000
Exp10 := EXP(input * 2.30258509299405);
e1550196-3879-4553-be95-49fb41feaf24
Input value
Power
Raising to power X^N. Although there is an EXPT operator in the standard, according to OSCAT, this algorithm works 30 times faster in CoDeSys.
sign := power.15;
power := ABS(power);
IF power.0 THEN ExpN := input; ELSE ExpN := 1.0; END_IF;
power := SHR(power, 1);
WHILE power > 0 DO
input := input * input;
IF power.0 THEN ExpN := ExpN * input; END_IF;
power := SHR(power, 1);
END_WHILE;
IF sign THEN ExpN := 1.0 / ExpN; END_IF;
640185c1-fa56-49b9-b0b7-33d16ee89099
Input value
Rounds the value and returns the nearest integer value that is less than or equal to X.
floor(3.14) = 3 floor(-3.14) = -4
FLOOR := REAL_TO_DINT(input);
IF DINT_TO_REAL(FLOOR) > input THEN
FLOOR := FLOOR - 1;
END_IF;
c7ae1511-9cb2-442c-a360-58c1601418fa
Input value
This function returns the fractional part of a floating point number.
fract(3.14) = 0.14
IF ABS(input) < 2.0E9 THEN
Fract := ABS(input - DINT_TO_REAL(DTrunc(input)));
ELSE
Fract := 0.0;
END_IF;
2c97c99b-94d7-41a3-88c0-c889ffc9588e
Dividend
Divisor
Calculates the remainder of division for floating point numbers.
modr(5.5, 2.5) = 0.5
IF divisor = 0.0 THEN
MODR := 0.0;
ELSE
MODR := input - DINT_TO_REAL(FLOOR(input / divisor)) * divisor;
END_IF;
66e67209-b916-448f-8711-f1d9c196c26f
Number to process
Decimal precision
Rounds a floating point number to the specified number of decimal places.
round(12.23456789, 2) = 12.23
Round := DINT_TO_REAL(REAL_TO_DINT(input * Exp10(precision))) / Exp10(precision);
9884e99a-0864-45b3-bf27-509db44c9794
Input value in Celsius
Convert temperature from Celsius to Fahrenheit
CelciusToFarenheit := celsiusTemp * 1.8 + 32.0;
7f5d796b-74a9-421e-bd0e-9267c5a44f76
Input value in Celsius
0C in Kelvin
Convert temperature from Celsius to Kelvin
CelciusToKelvin := celsiusTemp + physT0;
0b238ef8-5d8d-4a08-8ea0-8b0731782a91
Input value in degrees
Converts degrees to radians
DegreesToRadians := ModR(0.0174532925199433 * degrees, pi*2);
ed9aeab3-29c3-4d5f-9dc8-19fc9c76a64e
Input value in Fahrenheit
Convert temperature from Fahrenheit to Celsius
FarenheitToCelcius := (fahrenheitTemp - 32.0) * 0.5555555555555;
e90ecedc-3b3d-421c-9673-f59db8c05ffd
Input value in Kelvin
0C in Kelvin
Convert temperature from Kelvin to Celsius
KelvinToCelcius := kelvinTemp - phys_T0;
e1affddf-5581-4627-8d61-7f2a7f2ca0fa
Input value in Km/h
Speed conversion: km/h to m/s
KmhToMs := kmhSpeed * 0.2777777777777;
b26a4465-6ef2-4dc8-b751-aebd4e2f2f56
Input value in m/s
Speed conversion: m/s to km/h
MsToKmh := msSpeed * 3.6;
21dd1116-5d67-493d-8c77-5b380095d556
Input value in radians
Converts radians to degrees.
RadiansToDegrees := ModR(57.29577951308232 * radians, 360.0);
4981396d-e136-4432-9a71-2aaee415565e
Input value
Input minimum
Input maximum
Output minimum
Output maximum
Linear scaling of floating point number
IF inputLow = inputHigh THEN
ScaleReal := outputLow;
ELSE
ScaleReal := (outputHigh - outputLow) / (inputHigh - inputLow) * (LIMIT(inputLow, input, inputHigh) - inputLow) + outputLow;
END_IF;
f4fd53a7-9957-4944-8a08-2f548df9fcb7
Input byte
0C in Kelvin
Convert single byte to single char string for use cases when CHR is not available (yes, it is possible)
ByteToString := asciiMap[input];
41b7c2b8-3d46-475d-9e89-51e83fe1b7f1
Input string
Character to count
Character pointer
String length
Current string position for iteration
Counts how many specified characters are contained in the string
charPointer := ADR(inputString);
length := LEN(inputString);
CountChar := 0;
FOR position := 1 TO length DO
IF charPointer^ = charToCount THEN
CountChar := CountChar + 1;
END_IF;
charPointer := charPointer + 1;
END_FOR;
18cffb02-7390-4f3a-8476-2dd880b25886
Input string
What to remove
For temporary string
Position of found character
Remove character from string
tempString := inputString;
REPEAT
position := FIND(tempString, substringToRemove);
IF position <> 0 THEN
tempString := DELETE(tempString, LEN(substringToRemove), position);
END_IF;
UNTIL (position = 0)
END_REPEAT;
RemoveSubString := tempString;
e7c3cd13-9368-413f-9843-50ef6ed48f53
Input string
The setup.EXTENDED_ASCII value can be either TRUE or FALSE, depending on the character table supported by the PLC. Usually set to TRUE.
Change character case from lowercase to uppercase. Works only with ASCII Latin letters.
IF input > 96 AND input < 123 THEN
ToUpper := input AND 16#DF;
ELSIF input > 223 AND input <> 247 AND input <> 255 AND useExtendedASCII THEN
ToUpper := input AND 16#DF;
ELSE
ToUpper := input;
END_IF;
de50b872-7179-478a-a52a-b958ef69123e
Input date
Year
Month
Day
Extract year, month and day from DATE
// Get date as a string like 'D#2000-01-01'
sTemp := DATE_TO_STRING(input);
year := STRING_TO_UINT(MID(sTemp, 4, 3));
month := STRING_TO_UINT(MID(sTemp, 2, 8));
day := STRING_TO_UINT(MID(sTemp, 2, 11));
DateToElements := true;
b5fd857c-57e3-429c-980c-20e9ef873113
Input date
Determine day of week
NumOfDay := DATE_TO_DWORD(input) / 86400;
NumOfDay := NumOfDay + 3;
DayOfWeek := DWORD_TO_UINT(NumOfDay MOD 7);
0f2fb405-5be6-4edf-872f-e9cea8ac3b5e
Input date in DATE format
Calculate ordinal day of the year
DateToElements(input := input, year => year, month => month, day => day);
DayOfYear := DaysTillMonth(year, month) + day;
6eb00276-4ffa-4774-80ef-fed385777b26
Year
Calculate number of days in year
IF IsLeapYear(year) THEN
DaysInYear := 366;
ELSE
DaysInYear := 365;
END_IF;
8f928eba-c868-493d-b5ff-8dfedd52641d
Year
Month
Function to determine the number of days from the beginning of the year to the specified month.
DaysTillMonth := days[month] + BOOL_TO_UINT(month > 2 AND IsLeapYear(year));
67d1a212-1ccc-457e-b16d-c7e461443b5b
Year
Calculates how many days have passed until the specified year
// We get the number of years that have passed since 1970
year := year - 1970;
DaysTillYear := year * 365 + ((year + 1) / 4) - ((year + 69) / 100) + ((year + 369) / 400);
d2e816a0-7963-4686-bc41-d296bea22188
Hours
Minutes
Seconds
Milliseconds
Pack hours, minutes, seconds and milliseconds into TIME
HmsToTime := DWORD_TO_TIME(((h * 60 + m) * 60 + sec) * 1000 + ms);
49706012-99ad-4259-bff9-b7c36e2caa74
Hours (0..23)
Convert hours (as INT) to TIME
HourToTime := T#1H * hours;
93ab5816-722a-4fe0-b453-14d79d5f96e7
Hours (0..23)
Convert number of hours (INT) to TOD (time of day) value.
HourToTod := TOD#00:00:00 + T#1h * (hours MOD 24);
ddb9ff52-023f-4749-8919-0e0538454a46
Year
Determine if it's a leap year
IsLeapYear := year MOD 400 = 0 OR year MOD 4 = 0 AND year MOD 100 <> 0;
5dbd663d-5e78-4699-b583-409a46d62852
Longitude
Date in UTC
Solar noon time at the specified longitude at the given time
t := UINT_TO_REAL(DayOfYear(dateInUtc));
offset := -0.1752 * SIN(0.033430 * T + 0.5474) - 0.1340 * SIN(0.018234 * T - 0.1939);
SunMidday := HourToTod(REAL_TO_INT(12.0 - offset - longitude * 0.0666666666666));
68fe546b-72e8-4b0d-8ac1-742a18fb08fa
Input time
Extract hours, minutes, seconds and milliseconds from TIME
// Now the time is a string like 'TOD#12:12:59.001'
sTemp := TOD_TO_STRING(TIME_TO_TOD(input));
h := STRING_TO_UINT(MID(sTemp, 2, 5));
m := STRING_TO_UINT(MID(sTemp, 2, 8));
sec := STRING_TO_UINT(MID(sTemp, 2, 11));
ms := STRING_TO_UINT(MID(sTemp, 3, 14));
TimeToElements := TRUE;
3fe5a2e5-d274-4bd2-9993-e23e460bb606
Current time
Lower bound
Upper bound
Function to determine if the current time is between two TIME_OF_DAY timestamps.
TodBetween := (
fromTOD > toTOD AND (currentTOD > fromTOD OR currentTOD < toTOD)
) OR (fromTOD < toTOD AND (currentTOD < toTOD AND currentTOD > fromTOD)
);
b1ee0c3e-f6b4-4799-81ae-1ae2e0d1a89a
Lower bound
Upped bound
Calculate the difference between TODs in TIME
IF toTOD < fromTOD THEN
TodDiff := T#24H - TOD_TO_TIME(fromTOD) + TOD_TO_TIME(toTOD);
ELSE
TodDiff := toTOD - fromTOD;
END_IF
50bec7af-ee3d-4afd-9afc-57186a003144
Current date
Calculate the week number in the year
WeekOfYear := (DayOfYear(currentDate) + 6) / 7;
IF DayOfWeek(currentDate) < DayOfWeek(YearStarts(currentDate)) THEN
WeekOfYear := WeekOfYear + 1;
END_IF;
67cc401c-9be4-42bc-a783-173878e795d0
Current date
Temporary string
Calculate which day of the week the year starts on
str := DATE_TO_STRING(currentDate);
YearStarts := STRING_TO_DATE(CONCAT(CONCAT('D#', MID(str, 4, 3)), '-01-01'));
f574b5de-a869-4215-82cd-296f58bbc331
Year
Month
Day
Hour
Minutes
Seconds
Milliseconds
Pack year, month, day, hours, minutes, seconds and milliseconds into DT
sTemp := CONCAT('DT#', UINT_TO_STRING(y));
sTemp := CONCAT(sTemp, '-');
sTemp := CONCAT(sTemp, UINT_TO_STRING(mn));
sTemp := CONCAT(sTemp, '-');
sTemp := CONCAT(sTemp, UINT_TO_STRING(d));
sTemp := CONCAT(sTemp, '-');
sTemp := CONCAT(sTemp, UINT_TO_STRING(h));
sTemp := CONCAT(sTemp, ':');
sTemp := CONCAT(sTemp, UINT_TO_STRING(m));
sTemp := CONCAT(sTemp, ':');
sTemp := CONCAT(sTemp, UINT_TO_STRING(sec));
sTemp := CONCAT(sTemp, '.');
sTemp := CONCAT(sTemp, UINT_TO_STRING(ms));
YmdhmsTodt := STRING_TO_DT(sTemp);
e464deaa-a738-4c18-a017-c8f42f407d70
Year
Month
Day
Convert year, month and day to DATE format.
sTemp := CONCAT('D#', UINT_TO_STRING(y));
sTemp := CONCAT(sTemp, '-');
sTemp := CONCAT(sTemp, UINT_TO_STRING(m));
sTemp := CONCAT(sTemp, '-');
sTemp := CONCAT(sTemp, UINT_TO_STRING(d));
YmdToDate := STRING_TO_DATE(sTemp);
9484e7aa-ff92-4a2c-99de-34c83c939af8
Input number
Counts the number of bits equal to TRUE in DWORD.
Example: bit_count(3) = 2 Because 2 bits are 1, and the rest are 0.
WHILE input > 0 DO
IF input.0 THEN
BitCount := BitCount + 1;
END_IF
input := SHR(input, 1);
END_WHILE;
26a551da-bc5b-40c3-a89e-69f572829323
Byte to perform the operation on
Value to assign
Which bit number (zero-padded)
Loads one bit value into a byte.
bit_load_b(2#0000_0000, 1, 3) = 2#0000_1000
IF value THEN
BitLoadB := input OR SHL(dat, position);
ELSE
BitLoadB := input AND (NOT SHL(dat, position));
END_IF;
1e2df803-f917-4f56-b377-8bdb29e7bcae
Input number
Bit number
Calculate the value of a single bit in a DWORD variable.
BitOfDword := (SHR(input, n) AND 16#00000001) > 0;
858995be-7211-4c94-bcd7-bfdd7b773597
Input value
Bit number to invert
Invert the specified bit in DWORD
BitToggleDword := SHL(DWORD#1, position) XOR input;
7f6f260e-6f32-4728-9857-76f270e6e081
Input value X
Input value Y
Number of digits to compare
Compares 2 input numeric variables to check if their first digits match.
Number of digits to compare is passed in parameter N.
cmp(3.141516, 3.141517, 6) will return TRUE.
tmp := ABS(x);
IF tmp > 0.0 THEN
tmp := Exp10(DINT_TO_REAL(FLOOR(LOG(tmp))-n+1));
ELSE
tmp := Exp10(tmp);
END_IF;
CompareReals := ABS(x - y) < tmp;
aa970595-45f5-4899-b174-d076f52d2dc1
Input value
Truncates a floating-point number to a DINT integer. 1.5 becomes 1, and -1.5 becomes -1.
This function is necessary because REAL_TO_DINT may return different results on different systems.
DTrunc := REAL_TO_DINT(input);
IF input > 0.0 THEN
IF DINT_TO_REAL(DTrunc) > input THEN
DTrunc := DTrunc - 1;
END_IF;
ELSE
IF DINT_TO_REAL(DTrunc) < input THEN
DTrunc := DTrunc + 1;
END_IF;
END_IF;
2e55f2c0-fb39-4d0c-8d80-c5fb77a9a432
Input value
Determines if a number is even or not. Returns TRUE if even, FALSE if odd.
Even := NOT input.0;
9dc50582-69c8-4fd8-8f3b-d2bd8f2816fc
Previous value
Euler's constant
Calculates a pseudo-random number. To generate a number, the function
reads the PLC timer and creates a floating-point number from 0 to 1. To
use this function more than once in a single PLC cycle, you need to
call it with different values of the input parameter last. It's easier to use
the previous number to avoid repetitions.
tn := TIME_TO_DWORD(TIME());
tc := BitCount(tn);
tn.31 := tn.2;
tn.30 := tn.5;
tn.29 := tn.4;
tn.28 := tn.1;
tn.27 := tn.0;
tn.26 := tn.7;
tn.25 := tn.6;
tn.24 := tn.3;
tn := ROL(tn, BitCount(tn)) OR 16#80000001;
tn := tn MOD 71474513 + INT_TO_DWORD(tc + 77);
Random := FRACT(DWORD_TO_REAL(tn) / 10000000.0 *
(E - LIMIT(0.0, last, 1.0))
);
7cb56466-a164-4a81-b192-885e8bbef47b
Input value
Swap bytes in DWORD
SwapDwordBytes := (ROR(input, 8) AND 16#FF00FF00) OR (ROL(input, 8) AND 16#00FF00FF);
bccdf6cd-e09b-4efd-9f7e-4630addfad75
Input value
Swap bytes in WORD
SwapWordBytes := ROL(input, 8);
4ffee399-140a-4885-98aa-fde9c899cd67
8c1463b5-02f2-427d-bd06-9e39d42fcbff