Write an INT to a file with SysFile in CODESYS

6 min read · Published November 5, 2026

One INT is 2 bytes on disk. Open the file with SysFileOpen and a write mode such as AM_WRITE, call SysFileWrite, and check the returned count is 2. Run SysFileClose on every path, including the error path, and drive the sequence from a rising edge in a 100 ms or slower task so the scan runs it once.

An INT is a 16-bit value, so on disk it is 2 raw bytes. Writing one with the SysFile library takes three calls in a fixed order: SysFileOpen with a write access mode, SysFileWrite of the whole value, and SysFileClose. Two checks decide whether it works on every controller. SysFileWrite must return a written count equal to SIZEOF(iValue). And SysFileClose must run on every path, including the path where the write failed. Drive the sequence from a rising edge in a task with a cycle of 100 ms or slower, so a cyclic scan runs it once per trigger instead of once per pass. Below are the call checks, a state machine you can paste, and the tests that show the bytes reached storage.

What you need

  • The SysFile library in the project's Library Manager, with the system libraries. Add it if the reference is missing. The Library Manager must show no missing-reference warning and the project must compile.
  • The exact signatures for your version. Open SysFileOpen and SysFileWrite from the Library Manager and copy their parameter types from what the signature prints; they vary between library versions.
  • A writable location on the controller. Where a relative name such as Value.bin lands is the runtime's working directory, and the device documentation for your target states that path.
  • A slow task. Put file work in a task with a cycle of 100 ms or slower, or in the lowest-priority task you have. A write to flash storage can block for tens of milliseconds, and inside a 1 ms task that is dozens of overrun cycles. When a stretched task does end in a watchdog exception, stop a CODESYS status task spin watchdog exception covers how to find and stop the spin.

What each call does and returns

Three calls, in this order. The last column is the check that must pass before the program moves on.

Step Call You pass You get back Gate to the next step
1 SysFileOpen the file name and the access mode AM_WRITE a handle used by the next two calls the error result the call reports shows no error
2 SysFileWrite the handle, ADR(iValue), and SIZEOF(iValue) the number of bytes written, of type __XWORD the count equals SIZEOF(iValue)
3 SysFileClose the handle an error result no error, and the handle is spent

The reference page for SysFileWrite in the library documentation states that the return is the number of bytes written and that the file must already be open with AM_WRITE or AM_APPEND. AM_WRITE rewrites from the start of the file, AM_APPEND adds at the end; for a fixed value slot, AM_WRITE replaces the old value.

The file holds raw binary. The INT lands in the target's byte order, and a text editor shows odd characters. To store a number a person can read, convert the INT to a string and write the string's bytes, with the size taken from the string length.

Steps

  1. Declare the working variables.
xStart    : BOOL    := FALSE; // trigger input
xStartOld : BOOL    := FALSE; // xStart from the previous scan
xEdge     : BOOL    := FALSE; // TRUE for one scan per press
xDone     : BOOL    := FALSE; // sequence finished without error
xError    : BOOL    := FALSE; // sequence finished with error
iState    : INT     := 0;     // 0 idle, 10 open, 20 write, 30 close
iValue    : INT     := 1234;  // the value to store
xwCount   : __XWORD := 0;     // bytes written, as returned by SysFileWrite
hFile     : ...               // declare with the type SysFileOpen returns
bOpenOK   : BOOL    := FALSE; // set from the open's error result

Every name here except hFile is this example's own variable. __XWORD is the type SysFileWrite returns. Give hFile the return type printed in SysFileOpen's signature in your Library Manager.

  1. Build the edge trigger outside the CASE.
xEdge     := xStart AND NOT xStartOld;
xStartOld := xStart;

xEdge is TRUE for exactly one scan per press, so holding xStart TRUE still produces one write.

  1. Write the CASE with one call per state.
CASE iState OF

0: // idle
    xDone  := FALSE;
    xError := FALSE;
    IF xEdge THEN
        iState := 10;
    END_IF

10: // open the file for writing
    hFile := SysFileOpen('Value.bin', AM_WRITE);
    IF bOpenOK THEN // TRUE when the open reported no error
        iState := 20;
    ELSE
        xError := TRUE;
        iState := 30;
    END_IF

20: // write iValue
    xwCount := SysFileWrite(hFile, ADR(iValue), SIZEOF(iValue));
    IF xwCount = SIZEOF(iValue) THEN
        iState := 30;
    ELSE
        xError := TRUE;
        iState := 30;
    END_IF

30: // close, on every path
    SysFileClose(hFile);
    iState := 0;
    IF NOT xError THEN
        xDone := TRUE;
    END_IF

END_CASE

bOpenOK marks the line where you read the error result SysFileOpen reports; the exact output name comes from the signature in your library version. Everything else in the machine is complete as written.

  1. Route every failure through state 30. The open failure and the short write both set xError and land in the close state, so the handle is released on every path. A handle left open holds the file and consumes one of the runtime's file handles. Enough leaked handles and later opens fail until the runtime restarts.

  2. Download, pulse xStart once, and watch iState in the online view. It must step 0, 10, 20, 30, 0 and stop there. At a 100 ms cycle the sequence spans about 300 ms: open on the first scan, write on the second, close on the third.

Example: store a part count of 1234 across a restart

A cell keeps its part count in iValue and stores it when a batch ends, so a runtime restart does not lose it. The count comes from counter logic; PLC up down counter: scan timing and direction logic covers the scan moment when a counter value is safe to take. iValue holds 1234 and the task cycle is 100 ms.

  • Scan 1, state 10: SysFileOpen('Value.bin', AM_WRITE) returns a handle and reports no error.
  • Scan 2, state 20: SysFileWrite returns 2, the INT's full size. 1234 = 0x04D2, because 4 x 256 + 210 = 1234. On the little-endian targets most controllers use, Value.bin now holds the two bytes D2 04. A big-endian target writes 04 D2.
  • Scan 3, state 30: SysFileClose runs, iState returns to 0, xDone goes TRUE.

The result: after 3 scans spanning about 300 ms at the 100 ms cycle, Value.bin is exactly 2 bytes and holds the INT. After a runtime restart, the read-back sequence opens the file, reads the whole value with the returned count checked, closes, and iValue shows 1234 again. The count survived because it reached the storage medium.

If the same count also feeds a screen on the wall, the guide to andon boards covers where a small screen fits and where it does not.

How to check it worked

  1. Pulse xStart once. xDone goes TRUE, xError stays FALSE and iState is back at 0.
  2. Find the file. It exists where the device documentation puts the runtime's working directory, and its size equals SIZEOF(iValue). The programming system's online file functions or the device's file browser both show the size.
  3. Read it back after a restart. Change iValue, run the write, restart the runtime, then use the library's matching read call to pull the bytes into an INT and check the returned count. A value that matches after the restart shows the bytes reached storage.
  4. Repeat the write 100 times. Every run must end at state 0 with xError FALSE. If any path skipped the close, the leak would show up as an open that fails on a later trigger.

If it stalls or fails

What you see What it points to What to do
First open returns an invalid handle the path is missing or not writable on the target read the error result, then check the path, permissions and free space
Open works once, fails on later triggers an earlier sequence never closed make state 30 run on every path, including the write-failure path
SysFileWrite returns a short count storage full, or the medium is write-protected free space, and treat the short count as an error
The file changes on every scan the calls sit outside the CASE, or the trigger is a level move all calls into the CASE states and drive them from xEdge
Works in simulation, fails on the controller the controller's working directory or permissions differ use the path convention printed in the device documentation

Read each error result against the error constants the library defines; the Library Manager's data-type list prints them. The invalid handle on the first trigger is the one to check first, and a walkthrough of SysFileOpen returning RTS_INVALID_HANDLE on a runtime works that case in detail.

Raw calls or a function-block library

Recommendation: for one or two stored values, call SysFileOpen, SysFileWrite and SysFileClose directly from the three-state machine above, on a task cycle of 100 ms or slower, and treat any returned write count below SIZEOF(iValue) as an error that still closes the file. When several values share the file, or several program parts write to it, move to a function-block file handler such as the OSCat file handler. The discipline is the same: one rising edge on the execute input starts the sequence, the returned count is checked, and the file is closed at the end, failed or not.

Questions

Can all three SysFile calls run in the same scan?

Yes. The calls are synchronous, so open, write and close can follow one another inside one scan, as long as each return value is checked in order and a failure still reaches the close. The one-call-per-state machine on this page is easier to diagnose because the online view shows exactly which step failed, and it keeps a slow write out of the middle of a fast scan.

Why does the second write fail when the first one worked?

The first sequence never reached its close. A handle left open keeps the file locked and consumes one of the runtime's file handles, so the next open of that same file fails first; with more leaks, every open fails until the runtime restarts. The online view shows the tell: iState jumps from 10 straight to 30 with xError TRUE. Put state 30 on every path.

How do I write the INT so a text editor can read it?

Convert the INT to a string first, then write the string's bytes with the size taken from the string length. The value 1234 converts to 4 characters and produces 4 bytes, so the returned count to check becomes 4 instead of the INT's 2. A text editor then shows 1234. Keep the raw binary write when another program reads the file as binary.

Does the value survive a power cut?

A SysFileWrite return equal to SIZEOF(iValue) means the file system accepted the whole value; how well it survives an unexpected power cut depends on the target's file system and storage medium. Test it on your hardware: write the value, cut controller power, restart, and read the bytes back. A matching value after that test is the result you need.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote