Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Sunday, 1 May 2016

SPI Explained (connecting SD cards to the Microbit!)

Once you've got past the basics of flashing LED's and using the built in i2c devices on a microbit, the next thing you'll probably wonder what else you can connect to it...

Quite a lot of the cool things you might want to connect use "SPI", which is quite a lot like I2C, just different - in the same way that firewire, thunderbolt and USB are all different but do similar kinds of things. Like i2c it can be a bit scary to hook up at first, but its actually pretty easy. 

In addition to power (and earth) an SPI device will typically use 4 wires. Two of these are mysteriously labeled MISO and MOSI, but in fact they're really simple: 

MISO: Master In, Slave Out
MOSI: Master Out, Slave In

For our purposes the Master is the computer (Microbit) and the Slave is the thing we want to connect. MOSI sends data from the computer to the device, and MISO sends data from the device back to the Master. See... it really is easy.

The third wire is Clock, and that just makes sure that the two ends are properly synchronised. On the Microbit pin 13 is clock, 14 is MISO and 15 is MOSI. On Arduino they're on pins 11,12 and 13.

Remember how we said SPI is like USB... well just like USB you can connect lots of things to SPI at the same time. However the microbit needs to know which device its talking to. For that we need the final wire CS - Chip/Circuit Select. You can actually use any pin you like as chip select, but pin 16 is generally going to be the best choice on Microbit, as its sitting there right next to 13,14,15 not doing anything very much!

If you want to connect multiple SPI devices at the same time, then that's totally OK - just share the CLK, MOSI, MISO connections and give each their own CS connection.

So what can we connect? Well one of the most common SPI devices is an SD card reader. In fact there's actually no such thing as an SD card reader - its really not much more than a connector between the card and the computer. SD cards actually know how to talk SPI!

You can get on of these for about £1 on eBay, (search for "arduino sdcard reader"). SD cards operate at 3.3v and actually a lot of the ones sold for Arduino might damage the card when you run them at 5V, but the Microbit is 3.3v so we're good! (for Arduino get something like the Data logging shield or ethernet shield which has a card reader built in, along with the extra circuitry to access it safely, rather than one of these cheap stand alone boards).

There'll be 6 pins: 0v/Gnd, 3V/Vcc, MISO, MOSI, CS, and CLK. Just hook them up as described above and you're good to go!

If you've installed Sniff already, then you'll find the code to access the SD card in examples/Embedded. It's in the Embedded, rather than the Microbit folder because it runs essentially unchanged on Microbit, Arduino, Propeller or anything else you might want to try it on.

make spi device

make sdcard device  D16
make sdcardValid boolean
make fileSystem device
make fileData string
make fileOK boolean

The code begins with some definitions. We need an SPI device to create the SPI bus, then an SD card device. You probably need to change the pin number here to correspond to the Microbit.  On the Arduino SD cards generally use either D10 or D4 as their CS pin, but we want to use D16.

The example code starts with some diagnostics:

when start
.wait 1 secs
.say "starting"
.repeat until sdcardValid
..say "insert card!"
..wait 1 secs
..tell sdcard to "init"
.say "Card OK"
.if not fileOK
..tell fileSystem to "init"
.if not fileOK
..say "File System Failure"
..say "ready"

This tests that the card reader is working, it has a card in, and that there's readable data on the card - you don't need to include any of this in your own code, but its good for testing. If this doesn't work then first check your wiring. If you're confident that its correct then you probably need to format your card. This is actually the hardest part! The Microbit is pretty limited in the complexity of code it can run, so it uses the FAT16 file system. This is OLD, so can only handle cards up to 2Gb. Your desktop machine will read FAT16 fine, but it might not be able to format FAT16. Or if it does format them, it can do a really bad job.  If you've got access to an Arduino, then use this sketch to reformat your cards, and theres a good chance that magically everything will start working.

Once you get past that hurdle, everything is nice and simple. The SDcard demo checks if the file "newFile.txt" exists:

when checkExists
.set fileData to "newFile.txt"
.tell fileSystem to "check file"
.if fileOK
..say "Found File"
..broadcast readTest
..say "File Not There"
..broadcast writeTest

If it's there then it does a read test, and if its not it does a write test. 

when writeTest
.set fileData to "newFile.txt"
.tell fileSystem to "create file"
.tell fileSystem to "start write"
.set fileData to "Hellow World!"
.repeat 3
..tell fileSystem to "write string"
.tell fileSystem to "end write"
.say "End Write OK"

Writing data is really easy. We set fileData to be the filename we want, call "create file" to make it, then "start write" to prepare it for writing. The we loop three times writing "hello world" into the file, before telling it we're done.

when readTest
.set fileData to "newFile.txt"
.tell fileSystem to "start read"
.repeat until not fileOK
..tell fileSystem to "read string"
..if fileOK
...say fileData
...say "EOF"
.tell fileSystem to "end read"
.say "End Read OK"
.set fileData to "newFile.txt"
.tell fileSystem to "delete file"

Reading it back is pretty similar, but this time we "start read". If its there and readable, then fileOK will be true, so we go around the loop, calling "read string". If that works then fileOK is true, and we print out what we got. Keep going until it fails, then "end read" to say you're done.

The read test also deletes the file when its done, so each time you reset the microbit it will alternate between doing a read test and a write test!

An SD card is a really cheap and easy way of adding permanent storage to your project, so you can make something that logs data overnight, or even for weeks. It's really easy to store data in a CSV file, and then just take the SD card to a computer and load it into a spreadsheet.


  1. Hello!

    Thanks for the detailed instructions! I have tried to compile the example with Sniff30Release, but I'm getting this error:

    $cd examples/Embedded
    $mb-sniff sdcard.sniff
    arm-none-eabi-gcc: error: nano.specs: No such file or directory

    It's needed by the linker:

    $find ../.. -type f -exec grep -H nano.specs {} \+
    ../../libexec/mbed-sniff:LD_FLAGS="${CPU} -Wl,--gc-sections -Wl,--sort-common -Wl,--sort-section=alignment --specs=nano.specs -Wl,--wrap,main"

    nano.specs is nowhere in the Sniff30Release package.

    Could you please help me to resolve it?


    1. Hello,

      I have solved it by installing these two packages (this is on Fedora 29)

      dnf install arm-none-eabi-newlib avr-libc

      There was one more problem:

      In file included from /S/Sniff30Release/Sniff30Release/lib/sniffDefs.h:11:0,
      from /S/Sniff30Release/Sniff30Release/lib/runtime.c:1,
      from sdcard.c:3:
      /S/Sniff30Release/Sniff30Release/lib/sniffDefsMBED.h:7:16: error: conflicting types for 'rand'
      #define random rand

      I have solved it by commenting out this line
      #define random rand
      in file /S/Sniff30Release/Sniff30Release/lib/sniffDefsMBED.h