Using SPI Flash

One of the best features of the M0 express board is a small SPI flash memory chip built into the board.  This memory can be used for almost any purpose like storing data files, Python code, and more.  Think of it like a little SD card that is always connected to the board, and in fact with Arduino you can access the memory using a library that is very similar to the Arduino SD card library.  You can even read and write files that CircuitPython stores on the flash chip!

To use the flash memory with Arduino you'll need to install the Adafruit SPI Flash Memory library in the Arduino IDE.  Click the button below to download the source for this library, open the zip file, and then copy it into an Adafruit_SPIFlash folder (remove the -master GitHub adds to the downloaded zip and folder) in the Arduino library folder on your computer:

Once the library is installed open the Arduino IDE and look for the following examples in the library:

  • fatfs_circuitpython
  • fatfs_datalogging
  • fatfs_format
  • fatfs_full_usage
  • fatfs_print_file
  • flash_erase

These examples allow you to format the flash memory with a FAT filesystem (the same kind of filesystem used on SD cards) and read and write files to it just like a SD card.

Read & Write CircuitPython Files

The fatfs_circuitpython example shows how to read and write files on the flash chip so that they're accessible from CircuitPython.  This means you can run a CircuitPython program on your board and have it store data, then run an Arduino sketch that uses this library to interact with the same data.

Note that before you use the fatfs_circuitpython example you must have loaded CircuitPython on your board. Load the latest version of CircuitPython as explained in this guide first to ensure a CircuitPython filesystem is initialized and written to the flash chip.  Once you've loaded CircuitPython then you can run the fatfs_circuitpython example sketch.

To run the sketch load it in the Arduino IDE and upload it to the Feather/Metro/ItsyBitsy M0 board.  Then open the serial monitor at 115200 baud.  You should see the serial monitor display messages as it attempts to read files and write to a file on the flash chip.  Specifically the example will look for a boot.py and main.py file (like what CircuitPython runs when it starts) and print out their contents.  Then it will add a line to the end of a data.txt file on the board (creating it if it doesn't exist already).  After running the sketch you can reload CircuitPython on the board and open the data.txt file to read it from CircuitPython!

To understand how to read & write files that are compatible with CircuitPython let's examine the sketch code.  First notice an instance of the Adafruit_M0_Express_CircuitPython class is created and passed an instance of the flash chip class in the last line below:

#define FLASH_SS       SS1                    // Flash chip SS pin.
#define FLASH_SPI_PORT SPI1                   // What SPI port is Flash on?

Adafruit_SPIFlash flash(FLASH_SS, &FLASH_SPI_PORT);     // Use hardware SPI 

// Alternatively you can define and use non-SPI pins!
//Adafruit_SPIFlash flash(SCK1, MISO1, MOSI1, FLASH_SS);

// Finally create an Adafruit_M0_Express_CircuitPython object which gives
// an SD card-like interface to interacting with files stored in CircuitPython's
// flash filesystem.
Adafruit_M0_Express_CircuitPython pythonfs(flash);

By using this Adafruit_M0_Express_CircuitPython class you'll get a filesystem object that is compatible with reading and writing files on a CircuitPython-formatted flash chip.  This is very important for interoperability between CircuitPython and Arduino as CircuitPython has specialized partitioning and flash memory layout that isn't compatible with simpler uses of the library (shown in the other examples).

Once an instance of the Adafruit_M0_Express_CircuitPython class is created (called pythonfs in this sketch) you can go on to interact with it just like if it were the SD card library in Arduino.  You can open files for reading & writing, create directories, delete files and directories and more.  Here's how the sketch checks if a boot.py file exists and prints it out a character at a time:

  // Check if a boot.py exists and print it out.
  if (pythonfs.exists("boot.py")) {
    File bootPy = pythonfs.open("boot.py", FILE_READ);
    Serial.println("Printing boot.py...");
    while (bootPy.available()) {
      char c = bootPy.read();
      Serial.print(c);
    }
    Serial.println();
  }
  else {
    Serial.println("No boot.py found...");
  }

Notice the exists function is called to check if the boot.py file is found, and then the open function is used to open it in read mode.  Once a file is opened you'll get a reference to a File class object which you can read and write from as if it were a Serial device (again just like the SD card library, all of the same File class functions are available).  In this case the available function will return the number of bytes left to read in the file, and the read function will read a character at a time to print it to the serial monitor.

Writing a file is just as easy, here's how the sketch writes to data.txt:

  // Create or append to a data.txt file and add a new line
  // to the end of it.  CircuitPython code can later open and
  // see this file too!
  File data = pythonfs.open("data.txt", FILE_WRITE);
  if (data) {
    // Write a new line to the file:
    data.println("Hello CircuitPython from Arduino!");
    data.close();
    // See the other fatfs examples like fatfs_full_usage and fatfs_datalogging
    // for more examples of interacting with files.
    Serial.println("Wrote a new line to the end of data.txt!");
  }
  else {
    Serial.println("Error, failed to open data file for writing!");
  }

Again the open function is used but this time it's told to open the file for writing.  In this mode the file will be opened for appending (i.e. data added to the end of it) if it exists, or it will be created if it doesn't exist.  Once the file is open print functions like print and println can be used to write data to the file (just like writing to the serial monitor).  Be sure to close the file when finished writing!

That's all there is to basic file reading and writing.  Check out the fatfs_full_usage example for examples of even more functions like creating directories, deleting files & directories, checking the size of files, and more!  Remember though to interact with CircuitPython files you need to use the Adafruit_Feather_M0_CircuitPython class as shown in the fatfs_circuitpython example above!

Format Flash Memory

The fatfs_format example will format the SPI flash with a new blank filesystem.  Be warned this sketch will delete all data on the flash memory, including any Python code or other data you might have stored!  The format sketch is useful if you'd like to wipe everything away and start fresh, or to help get back in a good state if the memory should get corrupted for some reason.

Be aware too the fatfs_format and examples below are not compatible with a CircuitPython-formatted flash chip!  If you need to share data between Arduino & CircuitPython check out the fatfs_circuitpython example above.

To run the format sketch load it in the Arduino IDE and upload it to the M0 board.  Then open the serial monitor at 115200 baud.  You should see the serial monitor display a message asking you to confirm formatting the flash.  If you don't see this message then close the serial monitor, press the board's reset button, and open the serial monitor again.

Type OK and press enter in the serial monitor input to confirm that you'd like to format the flash memory.  You need to enter OK in all capital letters!  

Once confirmed the sketch will format the flash memory.  The format process takes about a minute so be patient as the data is erased and formatted.  You should see a message printed once the format process is complete.  At this point the flash chip will be ready to use with a brand new empty filesystem.

Datalogging Example

One handy use of the SPI flash is to store data, like datalogging sensor readings.  The fatfs_datalogging example shows basic file writing/datalogging.  Open the example in the Arduino IDE and upload it to your Feather M0 board.  Then open the serial monitor at 115200 baud.  You should see a message printed every minute as the sketch writes a new line of data to a file on the flash filesystem.

To understand how to write to a file look in the loop function of the sketch:

  // Open the datalogging file for writing.  The FILE_WRITE mode will open
  // the file for appending, i.e. it will add new data to the end of the file.
  File dataFile = fatfs.open(FILE_NAME, FILE_WRITE);
  // Check that the file opened successfully and write a line to it.
  if (dataFile) {
    // Take a new data reading from a sensor, etc.  For this example just
    // make up a random number.
    int reading = random(0,100);
    // Write a line to the file.  You can use all the same print functions
    // as if you're writing to the serial monitor.  For example to write
    // two CSV (commas separated) values:
    dataFile.print("Sensor #1");
    dataFile.print(",");
    dataFile.print(reading, DEC);
    dataFile.println();
    // Finally close the file when done writing.  This is smart to do to make
    // sure all the data is written to the file.
    dataFile.close();
    Serial.println("Wrote new measurement to data file!");
  }

Just like using the Arduino SD card library you create a File object by calling an open function and pointing it at the name of the file and how you'd like to open it (FILE_WRITE mode, i.e. writing new data to the end of the file).  Notice however instead of calling open on a global SD card object you're calling it on a fatfs object created earlier in the sketch (look at the top after the #define configuration values).

Once the file is opened it's simply a matter of calling print and println functions on the file object to write data inside of it.  This is just like writing data to the serial monitor and you can print out text, numeric, and other types of data.  Be sure to close the file when you're done writing to ensure the data is stored correctly!

Reading and Printing Files

The fatfs_print_file example will open a file (by default the data.csv file created by running the fatfs_datalogging example above) and print all of its contents to the serial monitor.  Open the fatfs_print_file example and load it on your Feather M0 board, then open the serial monitor at 115200 baud.  You should see the sketch print out the contents of data.csv (if you don't have a file called data.csv on the flash look at running the datalogging example above first).

To understand how to read data from a file look in the setup function of the sketch:

  // Open the file for reading and check that it was successfully opened.
  // The FILE_READ mode will open the file for reading.
  File dataFile = fatfs.open(FILE_NAME, FILE_READ);
  if (dataFile) {
    // File was opened, now print out data character by character until at the
    // end of the file.
    Serial.println("Opened file, printing contents below:");
    while (dataFile.available()) {
      // Use the read function to read the next character.
      // You can alternatively use other functions like readUntil, readString, etc.
      // See the fatfs_full_usage example for more details.
      char c = dataFile.read();
      Serial.print(c);
    }
  }

Just like when writing data with the datalogging example you create a File object by calling the open function on a fatfs object.  This time however you pass a file mode of FILE_READ which tells the filesystem you want to read data.

After you open a file for reading you can easily check if data is available by calling the available function on the file, and then read a single character with the read function.  This makes it easy to loop through all of the data in a file by checking if it's available and reading a character at a time.  However there are more advanced read functions you can use too--see the fatfs_full_usage example or even the Arduino SD library documentation (the SPI flash library implements the same functions).

Full Usage Example

For a more complete demonstration of reading and writing files look at the fatfs_full_usage example.  This examples uses every function in the library and demonstrates things like checking for the existence of a file, creating directories, deleting files, deleting directories, and more.

Remember the SPI flash library is built to have the same functions and interface as the Arduino SD library so if you have code or examples that store data on a SD card they should be easy to adapt to use the SPI flash library, just create a fatfs object like in the examples above and use its open function instead of the global SD object's open function.  Once you have a reference to a file all of the functions and usage should be the same between the SPI flash and SD libraries!

Accessing SPI Flash

Arduino doesn't have the ability to show up as a 'mass storage' disk drive. So instead we must use CircuitPython to do that part for us. Here's the full technique:

  • Start the bootloader on the Express board. Drag over the latest circuitpython uf2 file
  • After a moment, you should see a CIRCUITPY drive appear on your hard drive with boot_out.txt on it
  • Now go to Arduino and upload the fatfs_circuitpython example sketch from the Adafruit SPI library. Open the serial console. It will successfully mount the filesystem and write a new line to data.txt
  • Back on your computer, re-start the Express board bootloader, and re-drag circuitpython.uf2 onto the BOOT drive to reinstall circuitpython
  • Check the CIRCUITPY drive, you should now see data.txt which you can open to read!

Once you have your Arduino sketch working well, for datalogging, you can simplify this procedure by dragging CURRENT.UF2 off of the BOOT drive to make a backup of the current program before loading circuitpython on. Then once you've accessed the file you want, re-drag CURRENT.UF2 back onto the BOOT drive to re-install the Arduino sketch!

Last updated on May 27, 2018 Published on May 03, 2017