In the previous post (links at bottom), I had used my Xiao Expansion board to play Christmas music stored on the SD card using the SamdAudio player.


I don't have an SD card on my QT Py ornament, but I did add a 2MB GD25Q16 external SPI flash chip to the QT Py.  The music file that I'm using, "we-wish-you-a-merry-christmas.wav", is 1.39MB - so it should fit in the flash memory.


I've previously used the SPI flash chip as external file storage for CircuitPython, so the interface has been tested.  I'm programming the QT Py using the Arduino IDE and Adafruit has "fatfs" libraries for the SPI flash.  The libraries allow for erasing and formatting the memory and creating and reading/writing files from within the Arduino program.  The feature that is missing using the Arduino IDE is the ability to do file transfers between the flash memory and the host computer, so copying files to the flash memory is problematic.  The good news is that the file transfer capability is available in CircuitPython.  The process for using it with Arduino, however, is somewhat convoluted and I haven't tried it before.


The fatfs library setup that is used with the Arduino IDE is not compatible with CircuitPython, so a different library setup is required to be able access files that are transferred with CircuitPython.  Adafruit has provided a CircuitPython example of the fatfs library to use with the Arduino IDE.   After you include the Adafruit SPIFlash library the example is available - SdFat_circuitpython.


File transfer process and program usage

Load CircuitPython

The QT Py uses a special "haxpress" version of CircuitPython.  You can get the latest stable uf2 file at this link:

The current version of the bootloader creates a drive instance that you can use to upload programs simply by dragging and dropping the uf2 file onto the drive instance (this works for any uf2, not just CircuitPython).


If you aren't in bootloader mode, you can enter it by doing a quick double press of the reset button on the QT Py.

Here is what the drive looks like in bootloader mode:

If you want to save the current firmware that is programmed you can just copy the CURRENT.UF2 file.  This allows you to reload that firmware easily after using CircuitPython (this is the internal flash, not the external one).


Transfer files to external flash

When CircuitPython runs it will present the external flash as a drive that you can drag and drop files to and from the host computer.

Here is what my drive looks like: (I have files there from using CircuitPython previously programming the OLED display)



     So, I proceeded to drag and drop the WAV file onto the drive and the transfer started.  Not having done this with a "large" file before I wasn't sure how long it would take.  The copy window popped up immediately.




     After about 5 minutes of the status not changing, I was worried; after 10 minutes really worried.  I guess using this method that this status window stays at 0% until it's done.  After about 15 minutes it finished.





     Verify Arduino program can find the file



     QT_Py_SdFat_circuitpython.ino    derived from SdFat_circuitpython.ino example


// Adafruit QT Py CircuitPython Flash Example
// Original Author: Tony DiCola
// This is an example of reading and writing data from Arduino
// to the QT Py flash filesystem used by CircuitPython.
// You can create, update, and read files on the CircuitPython
// filesystem in an Arduino sketch and then later load CircuitPython
// to interact with the same files.
// Note before you use this sketch you must load CircuitPython
// on your QT Py.  This will create the filesystem and
// initialize it, then you can load this example and read/write
// files on the board.
// Usage:
// - Modify the pins and type of fatfs object in the config
//   section below if necessary (usually not necessary).
// - Upload this sketch to your QT Py board.
// - Open the serial monitor at 115200 baud.  You should see the
//   example start to run and messages printed to the monitor.
//   If you don't see anything close the serial monitor, press
//   the board reset buttton, wait a few seconds, then open the
//   serial monitor again.
#include <SPI.h>
#include <SdFat.h>
#include <Adafruit_SPIFlash.h>

// On-board external flash (QSPI or SPI) macros should already
// defined in your board variant if supported
  Adafruit_FlashTransport_QSPI flashTransport;
  Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);
  #error No QSPI/SPI flash are defined on your board variant.h !

Adafruit_SPIFlash flash(&flashTransport);

// file system object from SdFat
FatFileSystem fatfs;

void setup() {
  // Initialize serial port and wait for it to open before continuing.
  while (!Serial) {
  Serial.println("Adafruit QT Py CircuitPython Flash Example");

  // Initialize flash library and check its chip ID.
  if (!flash.begin()) {
    Serial.println("Error, failed to initialize flash chip!");
  Serial.print("Flash chip JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);

  // First call begin to mount the filesystem.  Check that it returns true
  // to make sure the filesystem was mounted.
  if (!fatfs.begin(&flash)) {
    Serial.println("Failed to mount filesystem!");
    Serial.println("Was CircuitPython loaded on the board first to create the filesystem?");
  Serial.println("Mounted filesystem!");

  // Check if a music file exists:
  if (fatfs.exists("we-wish-you-a-merry-christmas.wav")) {
    Serial.println("Found we-wish-you-a-merry-christmas.wav");
  else {
    Serial.println("No we-wish-you-a-merry-christmas.wav found...");


void loop() {
  // Nothing to do in the loop.


     Program output on Serial Monitor:



Almost done.  I'll do a final post with a demo when everything is integrated and working.



Links to previous blogs

A QT Py Christmas

A QT Py Christmas - add Cap Sense Buttons

A QT Py Christmas - add Sound