How to log data to CSV on CC100 SD Card

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 
3 Likes

I wanted to test this program on my PFC 200. But I get an error message on this line:

diRtcSec := SysTimeRtcGet(pResult := Result);

Apparently Result is not defined. What needs to be done?

Hi,

Not tested, but try:

add to declaration part

iResultTime : DINT; //RTS_IEC_RESULT

change to in programm part

diRtcSec := SysTimeRtcGet(pResult := adr(iResultTime));

But also check WagoAppDatalogger library. There is also an example available.

I don`t fully understand what this command is doing? I have changed according to your instructions, but got the following error:
VAR_IN_OUT- beziehungsweise REFERENCE-Parameter ‘pResult’ von ‘SysTimeRtcGet’ benötigt eine Variable mit Schreibzugriff als Eingang

OK, pResult is differently handled here.than in the sysfile function.

udiResultTime : RTS_IEC_RESULT;
diRtcSec := SysTimeRtcGet(pResult := udiResultTime);