cfile-iosd-cardembedded-linuxcan-bus

Avoid SD card corruption in ANSI C


I am currently working on an embedded Linux device for data logging. The Linux device is plugged into a CAN bus and writes the traffic to an SD card.

From time to time, the SD card corrupts and is mounted read-only. This behavior needs to be avoided.

The file system is FAT (the SD card should stay be readable by the Windows systems).

The embedded device can power fail any time, so I need a safe way to write to the SD card from my C program.

As I am not really into C, I rely on a program called "candump" which basically prints the CAN bus messages to standard output in this format:

<0x006> [8] 77 00 00 00 00 00 00 00

My C program basically opens the candump program, reads from standard output, adds a timestamp and removes unnecessary characters:

1345836055.520 6 7700000000000000

The program:

while(running)
{
    if (filename != NULL)
    {
        fp_log = fopen(filename, "a");
        if (!fp_log)
        {
            perror("fopen");
            exit (EXIT_FAILURE);
        }
    }

    fgets(line, sizeof(line)-1, fp);

    /* Reset the row_values so they are always correctly initialized */
    row_identifier = 0;

    if (strchr(line, '<') != NULL)
    {
        /* Creating a buffer char to store values for casting char to int*/
        buffer_ident[0] = line[4];
        buffer_ident[1] = line[5];

        /* Cast buffer, e.g., {'1', '0', '\0'} to int: 10 */
        row_identifier = strtol(buffer_ident, NULL, 10);

        /* Heartbeat of the CAN bus PLC */
        if(row_identifier == 80)
        {
            /* Return pong on identifier 81 to the PLC */
            //system("cansend can0 -i 81 1 > /dev/null");
        }
        else
        {
            gettimeofday(&tv, NULL);
            fprintf(fp_log, "%d.%03d ", tv.tv_sec, tv.tv_usec/1000);
            fprintf(fp_log, "%d ", row_identifier);

            /* rowlength > 11 = data part is not empty */
            row_length = strlen(line);
            if (row_length>11)
            {
                int i = 0;
                for (i=11; i<row_length; i++)
                    /* Remove spaces between the data to save
                       space and copy data into new array */
                    if (isspace(line[i]) == 0)
                        fprintf(fp_log, "%c", line[i]);
                fprintf(fp_log, "\n");
            }
        }
    }
    fclose(fp_log);
}

The code snippet above works fine. It’s just that I get SD card corruption.

Solution

I ended up using ext3 as the file system with standard mount options. There aren't any problems any more.


Solution

  • The corruption probably happens because the OS didn't complete its write operations on the FAT filesystem. As correctly pointed out by J-16 SDiZ you can try to mitigate the problem doing sync from time to time to force the OS to write changes on the filesystem.

    However, you are having this kind of problems because you are not using a journaled filesystem (such as Ext3 or NTFS. Another thing to do, could be to fsck the filesystem every boot and then explicitly forcing a rw remount to keep the mountpoint clean and writeable.