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 0°C 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 0°C 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 0°C 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