This simple example makes use of Coesys 3.5 Libraries, SysFile, SysTypes and SysTimeRtc to datalog every second to a CSV file on an SD Card. This will create a new file when the xRunning variable becomes TRUE. The format of the file used is: Recipe_Name_YYYY-MM-DD_HH-MM-SS.csv
PROGRAM Datalog
VAR
xRunning : BOOL;
tTimer : TON; // Timer for 1-second interval
hFile : RTS_IEC_HANDLE; // File handle
sFileName : STRING(255); // Dynamic file path
bFileOpen : BOOL := FALSE; //File open status
bWriteHeader : BOOL := TRUE; //Flag to write CSV header
bSetFileName : BOOL := TRUE; //Flag to set filename on xRunning rising edge
dtTimestamp : DATE_AND_TIME; //RTC timestamp
sBuffer : STRING(1024); //Buffer for CSV line
iResultOpen : DINT; //RTS_IEC_RESULT
iResultWrite : DINT; //RTS_IEC_RESULT
iResultClose : DINT; //RTS_IEC_RESULT
// Timestamp variables
diRtcSec : DWORD;
Year : STRING;
Month : STRING;
Day : STRING;
Hour : STRING;
Minute : STRING;
Second : STRING;
dtStr : STRING(30);
pos : INT;
// Example Process Variables
sRecipe_Name : STRING := 'MyTest';
wRPM : WORD := 250;
rDegreesF : REAL := 23.4;
rPressurePSI : REAL := 100.4;
// Install SysFile (3.5.17.0+), SysTypes (3.5.17.0+) & SysTimeRtc (3.5.17.0+)
// in Library Manager.
// Tested on CC100 FW 28 Codesys 3.5.17.0 on Oct 1 2025
END_VAR
(* Initialize timer *)
tTimer(IN := xRunning AND NOT tTimer.Q , PT := T#1S);
(* Detect rising edge of xRunning to set filename *)
IF xRunning AND bSetFileName THEN
IF sRecipe_Name <> '' THEN
sFileName := CONCAT('/media/sd/', sRecipe_Name);
sFileName := CONCAT(sFileName, '_');
ELSE
sFileName := CONCAT('/media/sd/', 'NoRecipe_');
END_IF
diRtcSec := SysTimeRtcGet(pResult := Result);
dtTimestamp := UDINT_TO_DT(diRtcSec);
dtStr := DT_TO_STRING(dtTimestamp); // Convert DT to string
pos := FIND(dtStr, '#') + 1; // Position after 'DT#': 4
Year := MID(dtStr,4, pos); // 'DT#2025-10-01-16:48:00'
Month := MID(dtStr,2, pos + 5); // '10'
Day := MID(dtStr,2, pos + 8); // '01'
pos := FIND(dtStr, '-') + 1;// Extract (HH:MM:SS part, after the '-')
Hour := MID(dtStr,2, pos); // '16'
Minute := MID(dtStr,2, pos + 3); // '48'
Second := MID(dtStr,2, pos + 6); // '00'
sFileName := CONCAT(sFileName, Year);
sFileName := CONCAT(sFileName, '-');
sFileName := CONCAT(sFileName, Month);
sFileName := CONCAT(sFileName, '-');
sFileName := CONCAT(sFileName, Day);
sFileName := CONCAT(sFileName, '_');
sFileName := CONCAT(sFileName, Hour);
sFileName := CONCAT(sFileName, '-');
sFileName := CONCAT(sFileName, Minute);
sFileName := CONCAT(sFileName, '-');
sFileName := CONCAT(sFileName, Second);
sFileName := CONCAT(sFileName, '.csv');
// Format filename: /media/sd/RecipeName_YYYY-MM-DD_HH-MM-SS.csv
bSetFileName := FALSE; // Prevent resetting filename
END_IF
(* Reset filename flag when xRunning goes FALSE *)
IF NOT xRunning THEN
bSetFileName := TRUE;
bWriteHeader := TRUE;
END_IF
IF xRunning AND tTimer.Q THEN
(* Open file if not already open *)
IF NOT bFileOpen THEN
hFile := SysFileOpen(sFileName, 1, ADR(iResultOpen));
IF iResultOpen = 0 THEN
bFileOpen := TRUE;
END_IF
END_IF
IF bFileOpen THEN
(* Write header if needed *)
IF bWriteHeader THEN
sBuffer := 'Timestamp,RPM,Degrees,Pressure'; // EDIT THIS SECTION
sBuffer := CONCAT(sBuffer, '$R$L'); // CR LF
SysFileWrite(hFile, ADR(sBuffer), LEN(sBuffer), ADR(iResultWrite));
bWriteHeader := FALSE;
END_IF
(* Format data row *)
sBuffer := CONCAT(DT_TO_STRING(dtTimestamp), ','); // EDIT THIS SECTION
sBuffer := CONCAT(sBuffer, WORD_TO_STRING(wRPM));
sBuffer := CONCAT(sBuffer, ',');
sBuffer := CONCAT(sBuffer, REAL_TO_STRING(rDegreesF));
sBuffer := CONCAT(sBuffer, ',');
sBuffer := CONCAT(sBuffer, REAL_TO_STRING(rPressurePSI));
sBuffer := CONCAT(sBuffer, '$R$L'); // CR LF
(* Write to file *)
SysFileWrite(hFile, ADR(sBuffer), LEN(sBuffer), ADR(iResultWrite));
END_IF
END_IF
(* Close file when not running )
IF NOT xRunning AND bFileOpen THEN
SysFileClose(hFile);
bFileOpen := FALSE;
bWriteHeader := TRUE; // Reset header flag for next file open
END_IF