Skip to content

Coffee filesystem guide

joshvoigts edited this page Aug 29, 2013 · 2 revisions

Coffee is a very simple, relatively small and easy to use file system that you are most likely going to be very familiar with if you have done any C file access in the past. The notion is the same as on a normal PC: you open a file, read and write to it and close it. Contiki will take care of the underlying flash memory, giving you more time to focus on the real issues. This guide is written for the TmoteSky, though the lessons learned are directly applicable to any other mote for which the cfs filesystem exists.

Table of Contents

Creating an Initial Program (Step 1)

So lets start at the beginning. Go to your projects directory (or find a convenient location to create the tutorial application) and create a new directory for our example file system program, naming the directory coffee-example. Our example program will store a couple of string messages that begin with "#" in the filesystem and later retrieve them again.

So lets get started with a simple program that simply copies a message and prints it out. 1. open an editor of your choice (gedit, vi, emacs..), 2. copy the following initial program into a newly created file and 3. save this file as coffee-example.c:

 PROCESS(coffee_test_process, "Coffee test process");
 AUTOSTART_PROCESSES(&coffee_test_process);
 
 PROCESS_THREAD(coffee_test_process, ev, data)
 {
   PROCESS_BEGIN();
     
   /* step 1 */
   char message[32];
   char buf[100];
     
   strcpy(message,"#1.hello world.");
   strcpy(buf,message);
   printf("step 1: %s\n", buf );
     
   /* End Step 1. We will add more code below this comment later */    
        
   PROCESS_END();
 }

Open cooja and create a new simulation in which you load the coffee-example.c file onto a mote (see Steps 1-6 in Develop your first application if you have forgotten how to do so). Now run the simulation. If everything goes according to plan, you should see the Contiki bootup sequence followed by a printout of the message string.

Writing to the Filesystem (Step 2)

After having simply printed our first message, we will now write the message to the coffee filesystem and retrieve it again to ensure it is saved correctly. Now add the cfs header file. Do this by typing #include "cfs/cfs.h" at the top of your coffee-example.c file. Add the following code below the final comment of Step 1 (just above PROCESS_END()):

 fd_write = cfs_open(filename, CFS_WRITE);
 if(fd_write != -1) {
   n = cfs_write(fd_write, message, sizeof(message));
   cfs_close(fd_write);
   printf("step 2: successfully written to cfs. wrote %i bytes\n", n);
 } else {
   printf("ERROR: could not write to memory in step 2.\n");
 }

A brief outline of the code:

  • The first three lines below the comments are the name of the file as well as the file descriptions (fd) respectively.
  • We then continue to open the file with the CFS_WRITE attribute and then check whether it was opened correctly. If it returns -1, something went wrong.
  • Inside the if clause we write the message to the filesystem, and then close the file.

Reading from the Filesystem (Step 3)

After having written to memory, we will now retrieve the message from the filesystem and print it out. Copy and paste the following below your code from step 2:

 /* step 3 */
 /* reading from cfs */
 strcpy(buf,"empty string");
 fd_read = cfs_open(filename, CFS_READ);
 if(fd_read!=-1) {
   cfs_read(fd_read, buf, sizeof(message));
   printf("step 3: %s\n", buf);
   cfs_close(fd_read);
 } else {
   printf("ERROR: could not read from memory in step 3.\n");
 }

Note how we overwrite the buffer "buf" with a new message, just to ensure the read is accurate. Now execute rerun the test by reloading the code. If everything is fine, the first message should be displayed correctly in the log.

Appending More Data (Step 4)

We now open the file and append another message to it. That means that both the first and the new message should be stored in the file system. Append the following code below your code from step 3:

 fd_write = cfs_open(filename, CFS_WRITE | CFS_APPEND);
 if(fd_write != -1) {
   n = cfs_write(fd_write, message, sizeof(message));
   cfs_close(fd_write);
   printf("step 4: successfully appended data to cfs. wrote %i bytes\n",n);
 } else {
   printf("ERROR: could not write to memory in step 4.\n");
 }

This code is exactly the same as when we were writing the first message to the filesystem, however we now open the file using both "CFS_WRITE" and "CFS_APPEND" file modes. Once again, rerun the code and ensure the correct message is printed to the log.

Seeking Specific Data (Step 5)

We will now read both messages individual from the filesystem by seeking them individually. Copy the following code below your code from step 4:

   cfs_seek(fd_read, sizeof(message), CFS_SEEK_SET);
   cfs_read(fd_read, buf, sizeof(message));
   printf("step 5: #2 - %s\n", buf);
   cfs_close(fd_read);
 } else {
   printf("ERROR: could not read from memory in step 5.\n");
 }

The important line to watch out for is the line using the "cfs_seek(..)" function. The second parameter describes the offset or how far you want to advance the point from which the file is read (in our case the size of the message). The final parameter describes the mode with which we want to seek the data within the file. There are three ways of seeking data:

  • CFS_SEEK_SET: You will seek the absolute position within the file starting from the beginning. If we wanted to seek a presumed third message in the file, we would have to change the second parameter to "2*sizeof(message)".
  • CFS_SEEK_END: The offset from the end of the file.
  • CFS_SEEK_CUR: The offset relative to the last position within the file.
When you run the program, you should see both messages being printed out individually.

Removing a File (Step 6)

Finally, we want to remove our messages from the file system. This is done by simply using the cfs_remove(..) function. In the example we will later cross-check that the file does not exist any more, simply to demonstrate that it has been removed.

 /* step 6 */
 /* remove the file from cfs */
 cfs_remove(filename);
 fd_read = cfs_open(filename, CFS_READ);
 if(fd_read == -1) {
   printf("Successfully removed file\n");
 } else {
   printf("ERROR: could read from memory in step 6.\n");
 }

coffee-example.c

All the steps have now been incorporated. Your program should look like this:

 #include "cfs/cfs.h" 
 #include "contiki.h"
 #include <stdio.h>
 /**/
 PROCESS(coffee_test_process, "Coffee test process");
 AUTOSTART_PROCESSES(&coffee_test_process);
 PROCESS_THREAD(coffee_test_process, ev, data)
 /**/
 {
  PROCESS_BEGIN();
  /*        */
  /* step 1 */
  /*        */
  char message[32];
  char buf[100];
  strcpy(message,"#1.hello world.");
  strcpy(buf,message);
  printf("step 1: %s\n", buf );
  /* End Step 1. We will add more code below this comment later */    
  /*        */
  /* step 2 */
  /*        */
  /* writing to cfs */
  char *filename = "msg_file";
  int fd_write, fd_read;
  int n;
  fd_write = cfs_open(filename, CFS_WRITE);
  if(fd_write != -1) {
    n = cfs_write(fd_write, message, sizeof(message));
    cfs_close(fd_write);
    printf("step 2: successfully written to cfs. wrote %i bytes\n", n);
  } else {
    printf("ERROR: could not write to memory in step 2.\n");
  } 
  /*        */
  /* step 3 */
  /*        */
  /* reading from cfs */
  strcpy(buf,"empty string");
  fd_read = cfs_open(filename, CFS_READ);
  if(fd_read!=-1) {
    cfs_read(fd_read, buf, sizeof(message));
    printf("step 3: %s\n", buf);
    cfs_close(fd_read);
  } else {
    printf("ERROR: could not read from memory in step 3.\n");
  }
  /*        */
  /* step 4 */
  /*        */
  /* adding more data to cfs */
  strcpy(buf,"empty string");
  strcpy(message,"#2.contiki is amazing!");
  fd_write = cfs_open(filename, CFS_WRITE | CFS_APPEND);
  if(fd_write != -1) {
    n = cfs_write(fd_write, message, sizeof(message));
    cfs_close(fd_write);
    printf("step 4: successfully appended data to cfs. wrote %i bytes  \n",n);
  } else {
    printf("ERROR: could not write to memory in step 4.\n");
  }
  /*        */
  /* step 5 */
  /*        */
  /* seeking specific data from cfs */
  strcpy(buf,"empty string");
  fd_read = cfs_open(filename, CFS_READ);
  if(fd_read != -1) {
    cfs_read(fd_read, buf, sizeof(message));
    printf("step 5: #1 - %s\n", buf);
    cfs_seek(fd_read, sizeof(message), CFS_SEEK_SET);
    cfs_read(fd_read, buf, sizeof(message));
    printf("step 5: #2 - %s\n", buf);
    cfs_close(fd_read);
  } else {
    printf("ERROR: could not read from memory in step 5.\n");
  }
  /*        */
  /* step 6 */
  /*        */
  /* remove the file from cfs */
  cfs_remove(filename);
  fd_read = cfs_open(filename, CFS_READ);
  if(fd_read == -1) {
    printf("Successfully removed file\n");
  } else {
    printf("ERROR: could read from memory in step 6.\n");
  }
  /**/
  PROCESS_END();
 }

Simulation result

Lessons Learned

You now know the basics of using the cfs filesystem, namely reading, writing, seeking and deleting data. Cfs also allows you to create independent directories in which you can store data. Check out the cfs source code documentation for more details.

Tips

  • The PERL script /tools/makefsdata can create c source code for a pre-initialized coffee system, taking as input all the files and subfolders in a given directory. $/tools/makefsdata --help gives more information.
  • /examples/sky/test-coffee.c has a routine you can lift for a comprehensive test of your platform implementation.
  • Configuring the complement storage option increases flash lifetime, since page erases to all "1" don't require a subsequent rewrite with all "0".
Clone this wiki locally