Logging various form of data is common practice and is done in some form in more or less every industrial application. There are various ways to store the data such as databases and different cloud services. However, sometimes you just want to stick to the basics and store data as a file, for instance as comma separated values (CSV). Depending on the amount of data that needs to be written, it’s usually not the best idea to do a lot of writes to the PLCs local drive because of wear and accessibility, but rather to a network drive.
To write single lines of strings to a file in TwinCAT I needed to use a three-stage rocket of different function blocks:
- FB_FileOpen – for opening a file (to append new lines to it)
- FB_FilePuts – for writing single strings to the file
- FB_FileClose – for closing the file
The FB_FileOpen returns a handle that the other two FBs are using as input. However, using FB_FileOpen on a network drive always returned an error with the error identity equal to 1804. Checking the documentation for ADS return codes it says ADSERR_DEVICE_NOTFOUND (not found (files, …)). I tried using it on a local drive, including a microSD card attached to the PLC and everything worked just fine. After spending some time trying to get it to work I quickly checked the documentation for FB_FileOpen and it was very clear with network drives:
sPathName: Path and file name for the file to be opened. The path can only point to the local computer’s file system. Network paths cannot be specified here.
This means we need to find a workaround. Initially I thought of writing a simple C# program running in Windows that simply monitored for new files in a directory and copied them over to the network drive. The problem with that would be that there would have to be some sort of communication with the TwinCAT software in order for the C# program to know when the file is finished. This is not optimal and surely there must be a way to do it entirely from PLC code. After some searching I found this really neat function block NT_StartProcess, which can be used to start a windows application on the local or on a remote PLC. In the old days I used to do all file handling in DOS. DOS as an operating system is long gone, but some remnants of it still remain in the cmd.exe (command prompt). In the command prompt we can use the move or copy commands. This means we could copy/move the file once we are finished with the write to it. The state machine for our application would be:
- Open the file on a local drive of the PLC (FB_FileOpen)
- Write to the file (FB_FilePuts)
- Close the file (FB_FileClose)
- Transfer the file from the PLC to the network drive (NT_StartProcess)
Starting cmd.exe in windows and checking the documentation for copy gives us:
C:\Users\Jakob>help copy Copies one or more files to another location. COPY [/D] [/V] [/N] [/Y | /-Y] [/Z] [/L] [/A | /B ] source [/A | /B] [+ source [/A | /B] [+ ...]] [destination [/A | /B]] source Specifies the file or files to be copied. /A Indicates an ASCII text file. /B Indicates a binary file. /D Allow the destination file to be created decrypted destination Specifies the directory and/or filename for the new file(s). /V Verifies that new files are written correctly. /N Uses short filename, if available, when copying a file with a non-8dot3 name. /Y Suppresses prompting to confirm you want to overwrite an existing destination file. /-Y Causes prompting to confirm you want to overwrite an existing destination file. /Z Copies networked files in restartable mode. /L If the source is a symbolic link, copy the link to the target instead of the actual file the source link points to.
There is a bunch of flags that can help us specify our request even more, but the basic command is “copy <sourcefile> <destinationfile>”. Next let’s look at the NT_StartProcess function block.
The PATHSTR refers to the complete path for the application that we want to run, in our case it is ‘C:\Windows\System32\cmd.exe’. The DIRNAME is the directory from which the application will be launched. This doesn’t matter too much for us, but we can just leave it as ‘C:\Windows\System32‘. Finally, the COMNDLINE input are the parameters that are going to be provided to cmd.exe. We can run commands in cmd.exe by calling cmd.exe with the /C flag, and everything that follows is the command that we want to run. So for instance to do a standard ping we would call cmd.exe /C “ping google.com”. In our case this means that the COMNDLINE input would be ‘/C “copy C:\PathToSourceFile.csv N:\PathToDestinationFile.csv”‘.
An example of a call to this FB might then look like this:
Trigger the execution of the function block with the START input.
Now that we got this to work, what about the wear on the PLCs local drive? This can be circumvented by making sure to enable the UWF (or FBFW), which stands for unified write filter. This redirects all writes to the memory of the PLC rather than to the disk. Although it from the perspective from Windows looks like the data is written to disk, it’s actually written to RAM.
The UWF can only be enabled for local drives, and does thus not affect the network drive which means that once the files are copied from the local drive (RAM) into the network drive, they are safely stored.
And that’s it for this post. Have you ever stored files with TwinCAT on a network drive, and have any good tricks for how this was accomplished? Please write a comment!