SD en micro SD kaartjes

SD lezers te kust en te keur...
Ale je extra geheugen nodig hebt dan is een SD kaartje erg handig. Er zijn nauwelijks problemen met het gebruik van deze kaartjes en je hebt meteen enorm veel geheugen tot je beschikking. Een goed voorbeeld is het constant bewaren van meetgegevens tijdens een experiment. Of je zou je GPS positie tijdens een autorit kunnen bijhouden. Als je een Arduino robot bouwt dan kun je duizenden zinnen als geluidsbestanden opslaan op het kaartje en die later door de robot laten uitspreken.
Een nadeel zou kunnen zijn dat de kaartjes niet zo snel zijn als intern geheugen, maar er zijn niet zoveel projecten waarbij dat een grote rol speelt. Er is een bijzonder goede Arduino bibliotheek ("SD.h"), die in de nieuwste versie veel handige functies heeft. Er zijn zoveel mogelijkheden dat ik ze hier niet alle kan beschrijven. Onderaan deze pagina vind je twee testprogramma's. Je vindt diverse goede voorbeeldprogramma's bij de officiële site van de SD bibliotheek.
Het maakt voor de software niet uit of je een micro-SD of gewone SD kaartlezer aansluit. Beide heb ik getest en ze doen het gewoon erg goed. Wel kan het gebeuren bij goedkope kaartlezers dat een plaatje losschiet. Gewoon terugduwen en het werkt weer. Ik heb nog oude kaartjes liggen die niet genoeg capaciteit meer hebben voor een modern fototoestel, maar nog erg goed bruikbaar zijn als extra geheugen voor de Arduino. Waarschijnlijk kan iedereen gemakkelijk (bijna) gratis aan dergelijke kaartjes komen. Er zijn geen problemen bij het aansluiten van de kaartjes.
Datalogger met batterij en SD kaart
De kaart moet FAT16 of FAT32 geformatteerd zijn, maar dat is doorgaans al het geval. Je kunt alleen bestandsnamen gebruiken die uit maximaal 8 letters bestaan met een extensie van maximaal 3 letters. Er zijn ook displays met kaartlezers erop. Meestal moet je het kaartleesgedeelte hetzelfde aansluiten als de losse kaartlezers. Je kunt bitmaps die je op het SD kaartje hebt opgeslagen dan op het scherm tonen. Ik heb ook een data logger module getest. Dat is SD lezer, gecombineerd met een Real Time Clock. Ook de aansluiting gaat hetzelfde: SPI voor de SD kaart en I²C voor de RTC. Dit werkt als een zonnetje. De datalogger heeft een plaats voor een knoopcel, zodat de tijd bewaard blijft ook als de Arduino uit is.

Aansluitingen

Aansluiting bij de Leonardo
Met deze bibliotheek gebruik je de standaard SPI aansluitingen. Voor de meeste Arduino's komt dat erop neer dat je (verplicht) de volgende aansluitingen maakt: MOSI - pin 11, MISO - pin 12, CLK (of SCK) - pin 13. Maar voor de Arduino mega is dat MOSI - pin 51, MISO - pin 50, CLK - pin 52. Voor en Leonardo moet je de pinnen van de ICSP header gebruiken (dat zijn de 2 keer 3 pinnen aan de rechterkant). In het plaatje hiernaast zie hoe je dit moet doen.
CS (Chip Select) kun je in principe vrij kiezen, maar sommige shields vereisen dat het een bepaalde pin (bijvoorbeeld 4) is. Welke pin je gekozen hebt geef je in het programma op. De bibliotheek heeft nog iets merkwaardigs: SS (die je waarschijnlijk helemaal niet gebruikt) moet als OUTPUT pin worden geïnitialiseerd anders werkt de bibliotheek niet.
Getest met een Arduino Uno, Leonardo en met een 3,3 volt Arduino Pro Mini.

Voorbeeld programma's

Eerst een test programma, om een kaartje te lezen of schrijven. Let op: je krijgt geen waarschuwing als je een bestaand bestand probeert te overschrijven.
/* Programma voor het lezen en schrijven van een SD kaart
   Zie https://www.arduino.cc/en/Reference/SD voor alle functies van de bibliotheek
   Aansluitingen:
   UNO en meeste andere Arduino's: MOSI - pin 11, MISO - pin 12, CLK (of SCK) - pin 13; pin 10 (SS) moet output zijn
   Mega: MOSI - pin 51, MISO - pin 50, CLK - pin 52; pin 53 (SS) moet output zijn
   Leonardo: zie tekst op mijn site
   CS (Chip Select) kies een vrije pin, liefst 4 omdat dit vereist is bij sommige shields
*/

#include <SD.h> // Deze bibliotheek is standaard aanwezig
#define CS 4
File Bestand;

void setup() {
  Serial.begin(9600);
  while (!Serial); // wacht op verbinding (alleen nodig bij gebruik van een Leonardo)
  pinMode(SS, OUTPUT); // Deze pin moet OUTPUT zijn, ook als hij niet wordt gebruikt
  if (SD.begin(CS))
    Serial.println("De kaart is klaar voor gebruik.");
  else {
    Serial.print("Kaart is niet aanwezig of fout in de aansluitingen. \nCorrigeer dit en start dan de Arduino opnieuw op.");
    while (true);
  }
}

void loop()
{
  Serial.println("r of l = lezen, w of s = schrijven");
  while (!Serial.available());
  String c = Serial.readString(); c.toUpperCase();
  switch (c[0]) {
    case 'R': case 'L':
      Serial.println("Geef de naam van het bestand dat u wilt lezen. A = annuleren.");
      while (!Serial.available());
      c = Serial.readString();
      if (c[0] == 'A' || c[0] == 'a') {
        Serial.println("Geannuleerd");
        return;
      }
      c.replace("\n", ""); c.replace("\r", ""); // Haal eventuele CR en LF weg
      Bestand = SD.open(c);
      if (!Bestand) {
        Serial.print(c); Serial.println(" is niet gevonden");
      }
      else {
        while (Bestand.available()) Serial.write(Bestand.read());
        Bestand.close();
      }
      break;
    case 'W': case 'S':
      Serial.println("Geef de naam van het bestand dat u wilt (over)schijven. A = annuleren.");
      while (!Serial.available());
      c = Serial.readString();
      if (c[0] == 'A' || c[0] == 'a') {
        Serial.println("Geannuleerd");
        return;
      }
      c.replace("\n", ""); c.replace("\r", ""); // Haal eventuele CR en LF weg
      Bestand = SD.open(c, FILE_WRITE);
      if (!Bestand) {
        Serial.print(c); Serial.println(" kan ik niet aanmaken");
      }
      else {
        Serial.println("Verzend steeds een regel die opgeslagen moet worden. Begin de regel met @ om te stoppen");
        while (true) { // wacht op @
          while (!Serial.available());
          c = Serial.readString(); Serial.print(c);
          if (c[0] == '@') {
            Bestand.close();
            return;
          }
          Bestand.print(c);
        }
      }
      break;
    default: Serial.println("Ongeldig commando");
  }
}
Uitvoer van het programma
Als je informatie over je kaartje wilt ophalen (zonder er iets op te zetten), dan kun je dat ook heel goed doen met de SD bibliotheek. Je moet dan objecten aanmaken voor het cardtype, het "volume" en de aanwezige bestanden. Zie het voorbeeld hieronder. Je kunt de twee voorbeeldprogramma's uiteraard combineren, maar het is beter om dat niet te doen, want dat kost veel geheugen.
/* Dit programma geeft informatie over de SD kaart
   Zie https://www.arduino.cc/en/Reference/SD voor alle functies
   Aansluitingen:
   UNO en meeste andere Arduino's: MOSI - pin 11, MISO - pin 12, 
        CLK (of SCK)- pin 13; pin 10 (SS) moet output zijn
   Mega: MOSI - pin 51, MISO - pin 50, CLK - pin 52; pin 53 (SS) moet output zijn
   Leonardo: zie tekst op mijn site
   CS (Chip Select): kies een vrije digitale pin (bij voorkeur 4)
*/

#include <SD.h>
#define chipSelect 4
// De volgende variabelen gebruik je om informatie over het kaartje te verkrijgen
// Om bestanden te lezen of te schrijven heb je ze geen van alle nodig
Sd2Card kaartje;
SdVolume volumeInfo;
SdFile bestanden;

void setup() {
  Serial.begin(9600);
  while (!Serial); // wacht op verbinding (alleen nodig voor de Leonardo)
  Serial.println("De SD kaart wordt geinitialiseerd...");
  pinMode(SS, OUTPUT); // Deze pin moet OUTPUT zijn, ook als hij niet wordt gebruikt
  if (!kaartje.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("Kaart is niet aanwezig of fout in de aansluitingen.");
    Serial.print("Corrigeer dit en start daarna de Arduino opnieuw op.");
    while (true);
  }
  Serial.print("De kaart is klaar voor gebruik (dit is een ");
  switch (kaartje.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1 kaart)");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2 kaart)");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC kaart)");
      break;
    default:
      Serial.println("onbekend kaart type)");
  }
  if (! volumeInfo.init(kaartje)) {
    Serial.println("Het kaartje is niet goed geformateerd of onleesbaar.");
    return;
  }
  Serial.print("Bestandsysteem: FAT"); Serial.println(volumeInfo.fatType());
  Serial.print("Beschikbaar geheugen: ");
  Serial.print((float)volumeInfo.blocksPerCluster() * volumeInfo.clusterCount() / 2048.0, 0);
  Serial.println(" MB");
  bestanden.openRoot(volumeInfo);
  Serial.println("Aanwezig op deze SD kaart:");
  bestanden.ls(LS_R | LS_DATE | LS_SIZE);
  Serial.println("--------------------------------------------------");
}

void loop() { }