Aller au contenu

Carte SD Arduino : branchement, librairie de base, et exemples de code (pour Arduino Uno, Nano, Mega)

Carte SD arduino raccordement et câblage, avec exemple de code programmation IDE Arduino Uno, Nano ou Mega, et librairie sd

Si vous cherchez un moyen simple et rapide pour stocker un grand nombre d’informations depuis votre Arduino, alors rien de tel qu’une carte SD pour ce faire ! Surtout qu’il existe maintenant des modules « prêts à l’emploi » et peu coûteux pour y arriver ! D’ailleurs, c’est ce que je vous propose de découvrir ici. Ainsi, vous verrez comment raccorder un lecteur de carte micro SD à son Arduino Uno, Nano, ou Mega, et comment effectuer des lectures et écritures dans des fichiers sur une SD card.

Histoire de vous montrer des applications concrètes de tout cela, je vous mettrai plusieurs exemples de code pratiques, avec notamment celui d’un datalogger (programme permettant l’enregistrement de données sur carte SD). Ce dernier vous permettra d’ailleurs de faire un suivi régulier de la température et de l’hygrométrie ambiante, via un capteur DHT22. Ces mesures seront stockées sur carte SD, et nous verrons comment elles peuvent être utilisées pour faire de beaux graphiques, dans un tableur (Excel, OpenOffice Calc, …). Mais pour l’heure, commençons par les bases 😉

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

Si vous constatez la moindre coquille ou avez des questions, n’hésitez pas à m’en faire part en zone de commentaire, en bas de cet article ! Car cela me permettra d’améliorer la qualité du présent tuto, et d’apporter un maximum d’infos au plus grand nombre. Merci !

Survol rapide

Lorsque je parle de carte SD ici, je veux avant tout parler de lecteur de carte SD raccordable à l’Arduino. Prenant souvent la forme d’une mini plaquette PCB avec un support de SD card intégré, ces types de modules disposent la plupart du temps d’une interface de communication idéale pour communiquer avec un Arduino Uno, Nano, Mega, ou tout autre contrôleur disposant d’un port SPI. Physiquement, voici deux exemples de modules, qu’on trouve couramment en vente sur le marché :

Type de carte SDPour Carte SDPour carte Micro SD (SDHC)
Aperçu du module Module carte SD arduino grand format, commande SPI via Arduino Uno, Nano ou Mega, branchement par fils dupont sur PCB Module carte Micro SD arduino, pour lire et enregistrer données sur SDHC card, câblage sur port SPI des Arduino Uno, Nano, ou Mega
CommunicationSPISPI
Tension d’alimentation4,5 à 5,5 (régulateur 5V intégré)4,5 à 5,5 (régulateur 5V intégré)
Tension des signaux de communication3,3 V uniquement3,3 ou 5V (avec convertisseur de niveaux intégré)
Trous de fixation2 x Ø2,2 (pour vis M2)4 x Ø2,2 (pour vis M2)
Courants indicatifsn.c.0,2 mA (min) / 80mA (typique) / 200mA (max)
Lien (description/prix)Lecteur Carte SD Lien externe produit ou composant électronique, pour projet ou montage à faire soi-même, external link passion élec Lecteur Micro SD Lien externe produit ou composant électronique, pour projet ou montage à faire soi-même, external link passion élec

Sans hésiter la moindre seconde, je vous recommande fortement de prendre un modèle intégrant un « convertisseur de niveaux intégré », comme présent dans le module « Micro SD » ci-dessus, si vous débutez. Car ainsi, vous ne prendrez pas le risque d’endommager votre lecteur de SD card, si vous travaillez avec un Arduino communiquant sur des niveaux 0/5V (comme l’arduino Uno, Nano, ou Mega, par exemple). Au passage, faites très attention aux vendeurs qui vous disent que leurs modules sont « tolérant 5V ». Car derrière cela peut se cacher des abus de langage. Et même si en pratique la plupart des lecteurs de carte SD tiennent « le choc » lorsque alimentés en 5V au niveau de leurs broches de communication, leur durée de vie, quant à elle, ne peut être longtemps garantie dans ces conditions. C’est pourquoi je vous recommande plutôt l’achat de lecteur de SD card intégrant un convertisseur 3V3 <-> 5V intégré, afin d’éviter tout soucis. Voici d’ailleurs comment cela peut se formaliser, sur un lecteur de carte Micro SD :

Convertisseur 3,3V à 5V pour module SD card, mise à niveau des tensions de communication sur port SPI, entre lecteur et Arduino Uno, Nano, ou Mega

À regarder de près, vous constaterez d’ailleurs la présence des broches « classiques » utilisées pour de la communication par bus SPI, à savoir : les pins SCK, MISO, MOSI, et CS. C’est donc idéal pour venir se raccorder sur un Arduino. Et c’est d’ailleurs tout aussi facile d’utilisation, avec la librairie SD.h, déjà intégrée à l’IDE Arduino.

À présent, concernant l’alimentation électrique de ces lecteurs de carte SD, ceux-ci peuvent généralement s’alimenter aussi bien en 3,3 volts, qu’en 5 volts (car ils sont la plupart du temps munis de régulateur interne 3V3 pour abaisser la tension 5V fournie sur leur VCC).

Au passage, privilégiez les lecteurs de carte SD dotés de trous de fixation dans les angles. Ainsi, il vous sera plus facile de les fixer « solidement » dans vos montages, sans que ça se balade partout ! Sinon, gare aux cartes SD qui s’abîment prématurément, ou qui se délogent !

Ah oui… tant que j’y pense, il y a encore deux choses à savoir, avant d’aller plus loin :

  • Les cartes SD que vous prévoyez d’utiliser doivent au préalable être formatées (au format FAT16 ou FAT32, et de préférence au format FAT16, aussi nommé « FAT » tout court, si elle fait 4 GB ou moins)
  • Les noms de fichiers ou répertoires que vous pourrez créer dessus devront respecter le « format 8.3 », c’est à dire comportant jusqu’à 8 caractères maxi pour le nom, et jusqu’à 3 caractères max pour l’extension (exemple de nom de fichier : 12345678.txt). Pour en savoir plus, n’hésitez pas à jeter un coup d’œil à la convention de nommage de fichier au format 8.3 (https://en.wikipedia.org/wiki/8.3_filename, en anglais), si vous souhaitez creuser la question 😉

Du reste, concernant la capacité des cartes SD utilisables avec un Arduino, je n’ai personnellement pas testé au-delà de 4 Go. Mais on peut bien évidemment aller au-delà, jusqu’à 32 Go, sauf erreur de ma part !

Important : assurez-vous toujours de bien prendre une carte SD compatible avec votre lecteur de SD card. Cela peut sembler évident dit comme ça, mais il faut savoir que, par exemple, un lecteur pour carte Micro SD n’est pas forcément automatiquement compatible avec les Micro SDHC, si non spécifié. Idem pour les tailles de cartes supportées (8 GB, 32 GB, …). C’est un point de détail lorsqu’on débute, mais qui a toute son importance. Donc mieux vaut s’assurer que tout soit bien compatible avec ce que vous prévoyez de mettre en œuvre, afin d’éviter toute déconvenue !

Branchement et câblage d’une micro SD card à un Arduino Uno, Nano, ou Mega (port SPI)

Brancher un lecteur de carte SD sur un Arduino Uno, Nano, ou Mega n’a rien de compliqué en soit. Car si on met de côté l’alimentation, les seuls fils à raccorder sont ceux du port SPI, à savoir : les bornes SCK, MISO, MOSI, et SS.

Bien entendu, suivant si vous êtes sur un Arduino de type Uno, Nano, Mega, ou autre, les bornes du port SPI peuvent varier. Ainsi, voici où raccorder chacune des broches de votre lecteur de carte SD ou Micro SD, sur votre arduino :

Module lecteur SD (spi)Arduino UnoArduino NanoArduino Mega
Broche CSD10D1053
Broche SCKD13D1352
Broche MOSID11D1151
Broche MISOD12D1250
Broche VCC+5V+5V+5V
Broche GNDGNDGNDGND

Schématiquement, voici ce que cela peut donner avec un Arduino Uno (c’est d’ailleurs le montage que j’ai fait de mon côté, pour mes essais) :

Exemple raccordement lecteur Micro SD sur Arduino Uno, câblage port SPI tels que SCK MOSI MISO et CS, avec alimentation 5V pcb

Pour rappel : si jamais votre lecteur de SD CARD n’est pas équipé de convertisseur 3V3 / 5V intégré, il vous faudra vous même faire la conversion des niveaux de signaux, au niveau du port SPI, avec des transistors mosfets ou un circuit spécialisé externe (mais c’est fastidieux, et pas vraiment rentable !). Et si j’insiste autant sur ce point, c’est parce que même si certains lecteurs de carte SD sont « dits » tolérant au 5V, au niveau de leurs broches de communication, cela pourrait à court ou moyen terme conduire à leur destruction. Du coup, faites vraiment attention lorsque vous faites vos achats. Ou comme moi : achetez un modèle qui intègre déjà la puce de conversion de niveau, car cela évite bien des mésaventures !

La librairie SD.h (bibliothèque arduino)

Il existe plusieurs librairies permettant de dialoguer avec un lecteur de carte SD, depuis un arduino. Autant certaines sont simples et facile à utiliser (mais limitées), autant d’autres sont bien plus complexes (et permettent de faire bien plus de choses). Si vous débutez, je vous propose de partir sur la bibliothèque déjà intégrée « de base », dans l’IDE Arduino par défaut. C’est d’ailleurs de celle-ci dont je vais vous parler ici, car c’est un bon compromis entre stabilité, facilité d’utilisation, et fonctionnalités d’usage. D’ailleurs, il n’y aura aucune installation ou importation de librairie à faire, puisque nativement intégrée à l’environnement arduino.

Nous allons voir ici toutes les principales fonctions mises à notre disposition par cette librairie, selon moi, pour lire, écrire, et récupérer des infos sur une carte SD. À noter que tout cela est détaillé sur le site arduino, en anglais, pour ceux qui souhaitent davantage d’informations à ce sujet : https://www.arduino.cc/en/reference/SD.

Remarque : cette librairie est l’idéal pour débuter, selon moi. Par contre, dites-vous que celle-ci n’est pas parfaite, et qu’elle souffre même de quelques lacunes (qui vous gêneront peut-être un peu, si vous cherchez à pousser les choses, un « cran plus loin ». Pour ma part, par exemple, je n’ai pas pu générer de noms de fichiers dynamiquement, sans avoir quelques warnings de compilation en retour. Toutefois, cette bibliothèque reste d’après moi le meilleur compromis qui soit, entre simplicité et performance, pour tous ceux qui débutent avec Arduino ! Bien sûr, si vous souhaitez faire des choses plus pointues ou plus spécifiques, il faudra peut-être utiliser une autre librairie que celle fournie nativement par l’IDE Arduino. Cela étant dit, vous verrez qu’on peut faire quand même pas mal de choses ici, avec la librairie SD.h, fournie dans l’environnement arduino 😉

Initialisation de la librairie SD.h (fonction begin)

Afin de pouvoir utiliser la librairie SD.h, nous allons préalablement devoir appeler deux librairies dans notre code arduino :

  • La librairie SPI.h (inclue de base dans l’IDE Arduino) : permettant d’assurer la communication entre l’arduino et le lecteur de carte SD, via le port SPI.
  • La librairie SD.h (elle aussi nativement installée dans l’IDE Arduino) : permettant de mettre à disposition tout un tas de fonctions simples et efficaces pour lire, écrire, et interroger la carte SD. Pour rappel : il n’y a aucune installation ou importation à faire à ce niveau, car cette bibliothèque est déjà intégrée de base, dans l’environnement arduino.

Pour ce faire, il suffira d’écrire les lignes suivantes, dans l’entête de votre programme :

#include <SPI.h>
#include <SD.h>

Ensuite, il faudra bien évidemment initialiser la classe « SD », afin de pouvoir s’en servir. Au passage, la bibliothèque SD.h nécessite qu’on lui précise sur quelle broche est raccordé le signal CS (ou SS), branché sur votre lecteur de carte SD. Classiquement, celle-ci est câblée sur la pin D10 des arduino uno ou nano, ou pin 53 d’un arduino mega.

Au niveau du code arduino, voici comment cela peut s’écrire, si vous utilisez un Arduino de type Uno, avec la broche SS (CS) de votre lecteur de SD card branchée sur la pin D10 de votre arduino (à mettre dans la fonction ‘setup’) :

const int pinBranchementCS = 10;           // Le « 10 » indiquant ici que la broche CS (SS) de votre lecteur de carte SD est branché sur la pin D10 de votre Arduino
SD.begin(pinBranchementCS);

À noter que cette fonction begin retourne « TRUE » si l’initialisation s’est bien déroulée, ou « FALSE » dans le cas contraire. Vous verrez d’ailleurs comment exploiter ce code erreur en pratique, dans les exemples de codes présentés plus bas, dans cet article.

Ouverture d’un accès en lecture ou écriture, vers un fichier sur la carte SD (fonction open)

Afin de pouvoir lire ou écrire dans un fichier sur la carte SD, il faut au préalable exécuter la fonction « open ». Et oui… cette fonction est commune pour faire ces deux choses là ! En fait :

  • Si vous souhaitez lire un fichier, alors la fonction « open » vous permettra d’ouvrir un accès en lecture vers le fichier voulu
  • Si vous souhaitez écrire dans un fichier, alors la fonction « open » vous permettra :
    • D’ouvrir un accès en écriture vers le fichier voulu, si celui-ci existe déjà
    • Ou de créer le fichier puis ouvrir un accès en écriture vers le fichier voulu, si celui-ci n’existait pas sur la carte SD

Comme vous l’aurez compris, la fonction « open » ne fait qu’ouvrir un canal vers le fichier visé. D’ailleurs, suivant si vous essayez de lire ou écrire dans un fichier, la fonction « open » ne renverra pas forcément un code erreur suivant si le fichier ciblé existe ou pas. En effet :

  • Si vous essayez d’ouvrir un fichier en lecture, tout en sachant que celui-ci n’existe pas sur la carte SD, alors la fonction « open » vous renverra une erreur (retournera « FALSE », en fait)
  • Mais si vous essayez d’ouvrir un fichier en écriture, tout en sachant que celui-ci n’existe pas, alors vous n’aurez aucun message d’erreur à proprement parler. Car un nouveau fichier « vide » sera simplement créé, avec le nom que vous aurez choisi. Et c’est seulement si jamais il était impossible au programme de créer ce nouveau fichier ou d’ouvrir le fichier existant, qu’un code erreur serait alors retourné.

J’espère que cette subtilité ne vous semblera pas trop obscure, car je ne saurais vous l’expliquer plus simplement !

Au final, l’appel de la fonction « open » vous retournera :

  • Soit un objet de type File, pointant vers le fichier ouvert
  • Soit une valeur booléenne égale à « FALSE », vous indiquant qu’il y a eu un problème lors de l’appel de cette fonction

Enfin, il faut savoir que la fonction « open » accepte un paramètre optionnel, permettant de préciser le « mode de fonctionnement ». Ce paramètre peut prendre 2 valeurs prédéfinies, qui sont :

  • FILE_READ, qui permet d’indiquer qu’on souhaite ouvrir le fichier en lecture seule, en le lisant « depuis le début » (évident, me direz-vous !)
  • FILE_WRITE, qui permet d’indiquer qu’on souhaite ouvrir le fichier cible (en lecture ou écriture), mais « depuis la fin », cette fois-ci

Là encore, si ce n’est pas très clair, ne vous inquiétez pas. Car au final, voici les 2 lignes de code généralement utilisées en pratique :

  • Si vous souhaitez ouvrir un fichier en lecture : SD.open(nomDuFichier, FILE_READ), ce qui vous permettra de lire le contenu de ce fichier depuis le début
  • Si vous souhaitez ouvrir un fichier en écriture : SD.open(nomDuFichier, FILE_WRITE), ce qui vous permettra d’écrire dans ce fichier, à la suite des lignes déjà enregistrées dedans (car oui : cette fonction n’efface pas le contenu d’un fichier existant, mais permet en fait d’écrire « à la suite » des données déjà enregistrées dans ce fichier)

Le saviez-vous ? Il est possible d’entrer un nom de répertoire en même temps qu’un nom de fichier, afin de lire ou écrire dedans. Mais bien évidemment, ce répertoire doit déjà exister sur la carte SD. Car autant la fonction « open » peut créer un fichier si jamais celui-ci n’existe pas sur la SD card, autant cette fonction est incapable de faire de même avec un répertoire. Mais rassurez-vous ! Car il existe une solution pour cela, en appelant la fonction « mkdir », que nous verrons un peu plus loin !

Fermer un fichier précédemment ouvert (fonction close)

Comme vous vous en doutez… une fois qu’on a ouvert un fichier… il faut le refermer ! Ou tout du moins, une fois qu’on en a fini avec lui ! Mais trait d’humour mis à part, n’oubliez JAMAIS de fermer vos fichiers, une fois que vous en avez plus l’utilité. Enfin… surtout lorsque ceux-ci sont ouverts en écriture, et nous allons voir pourquoi.

En effet, c’est seulement au moment de l’appel de la fonction close (ou flush), que toutes les données sont enregistrées dans le fichier cible. Par conséquent, ne pensez pas, en écrivant dans un fichier, que tout sera enregistré à la volée. D’ailleurs, histoire d’être parfaitement clair : dites-vous que si vous écrivez dans un fichier logé sur une carte SD, depuis un Arduino, et que vous n’exécutez pas la fonction « close », alors JAMAIS la moindre donnée ne sera réellement écrite ! Du coup : pensez toujours à fermer vos fichiers ouverts, une fois fini !

Au niveau du code, une ligne suffit, d’ailleurs :

SD.close();

Vous constaterez que cette fonction ne retourne rien. Il n’est donc pas possible, à ce niveau, de savoir si le fichier a bien pu être fermé, ni si toutes les données ont bien pu être enregistrées (dans le cas d’un fichier ouvert en écriture, j’entends). Mais bon, rien n’est parfait 😉

Vérifier si un fichier existe ou pas, ou un répertoire (fonction exists)

C’est vrai que je n’en ai pas parlé jusqu’à présent, mais il existe une fonction très intéressante, nommée « exists », permettant de tester si un fichier ou un répertoire existe sur la carte SD.

Cette fonction s’utilise très simplement, en lui donnant en paramètre :

  • Soit le nom de fichier, dont on cherche à vérifier l’existence
  • Soit le nom du répertoire, dont on cherche à vérifier l’existence
  • Soit le nom d’un fichier incluant son ou ses répertoires, délimités par un slash (par exemple : « monrep1/monfic.txt »)

En retour, on obtient :

  • « TRUE » si le fichier ou répertoire existe (est présent) sur la SD card
  • « FALSE » dans le cas contraire

Ce genre de fonction est particulièrement intéressante :

  • lorsqu’on cherche à effectuer un test d’existence avant toute ouverture de fichier en mode lecture (ça évite les erreurs d’accès en lecture, au beau milieu d’un programme informatique, par exemple !)
  • lorsqu’on souhaite écrire dans un nouveau fichier, en effaçant l’ancien au préalable s’il existe déjà, par exemple (car pour rappel : la fonction open, lorsqu’appelée en écriture, n’efface rien d’un fichier existant, mais se place simplement à la fin de celui-ci, pour y rajouter les données à enregistrer)

Effacer un fichier, présent dans une SD card (fonction remove)

Puisqu’on vient d’en parler, autant détailler la fonction « remove » sans plus attendre !

Car vous souhaiterez sûrement un jour pouvoir effacer un fichier, ne serait-ce que pour en faire disparaitre tout son contenu, et y stocker des données plus récentes à l’intérieur, par exemple. Pour ce faire, rien de plus simple ! Car une seule ligne de code :

SD.remove(nomDuFichierAsupprimer);          // Pour supprimer un fichier sur la carte SD

À noter que le nom de fichier à supprimer peut également contenir le chemin qui mène à lui, c’est-à-dire le ou les répertoires qui permettent d’y accéder. Dans ce cas, il vous suffira d’ajouter chacun des répertoires, avec un slash de délimitation entre chacun d’eux (par exemple : monrep1/sousrep2/monfic.txt).

Cette fonction « remove » retourne :

  • la valeur « TRUE » si le fichier a bien pu être effacé
  • la valeur « FALSE », si le fichier n’a pas pu être effacé
  • ou aucune valeur, si le fichier n’existe pas

Remarque : il est très important de toujours vérifier la présence d’un fichier avant toute tentative d’effacement de celui-ci. D’ailleurs, on le voit bien ici, car la fonction « remove » ne retournera aucune valeur si le fichier n’existe pas sur la SD card. Du coup, en l’absence de retour d’une valeur égale à TRUE par cette fonction, vous ne sauriez dire si le problème vient d’un fichier absent, mal orthographié, ou d’un problème d’effacement sur la carte SD. D’où l’intérêt de toujours tout bien tester, avant de faire quoi que ce soit !

Lire le contenu d’un fichier (fonction read)

Pour lire le contenu d’un fichier présent sur une carte SD, vous aurez besoin d’utiliser la fonction « read ». Mais vous aurez également besoin de réfléchir à comment vous souhaitez vous y prendre pour lire votre fichier. En effet, chaque fichier peut se lire d’octet en octet, tout comme ensemble d’octets après ensemble d’octets.

À savoir que :

  • un pointeur permet de savoir à chaque instant à quel endroit on se situe dans le fichier (la position de celui-ci peut d’ailleurs être retournée par la fonction « position », si besoin)
  • et qu’on peut parfaitement lire des octets depuis n’importe quel endroit d’un fichier, sans avoir à forcément tout lire depuis le début

Mais généralement, on lit chaque fichier depuis le début, octet après octet, jusqu’à arriver à la fin. Pour cela, on utilise :

  • la fonction « read », donc, qui permet de lire 1 à plusieurs octets à la fois (et qui décale le pointeur d’autant d’octets lus, pour que la lecture suivante se fasse bien sur les octets suivants).
  • la fonction « available », qui permet de savoir s’il reste ou non des octets à lire (sinon, cela signifie que soit le fichier était vide, soit on l’a lu jusqu’au bout)

Au niveau code de programmation, voici un exemple de ce qu’il est possible d’écrire, pour lire l’intégralité d’un fichier, du début à la fin :

File monFichierAlire = SD.open("monfichier.txt");    // Ouverture d’un accès en lecture seule

if (monFichierAlire) {                               // Si l’ouverture s’est bien passé, alors :
  while (dataFile.available()) {                     // Tant qu’il reste des octets à lire
      Serial.write(dataFile.read());                 // … on en lit un octet, puis on l’affiche sur le port série
  }                                                  // (en sachant que le pointeur passe automatiquement sur l’octet suivant à chaque lancement de "read", pour la lecture suivante)
  dataFile.close();
} else {
  Serial.println("Erreur lors de la tentative d’ouverture du fichier monfichier.txt");
}

En bref, c’est super simple ! Bien sûr, on peut pousser les choses un peu plus loin ici, et coupler cette fonction « read » avec d’autres fonctions, telles que :

  • la fonction « size », qui permet de connaître la taille exacte d’un fichier (c’est-à-dire le nombre total d’octets à lire)
  • la fonction « position », qui permet de donner la position courante du pointeur à l’intérieur du fichier (c’est-à-dire l’endroit où nous nous trouvons actuellement dans le fichier, qui, pour rappel, avance d’un cran après chaque lecture de type « read »)
  • la fonction « seek », qui permet de « sauter » à un endroit précis à l’intérieur d’un fichier (cela permet de lire un ou plusieurs octets à un endroit précis, sans avoir à tout parcourir, depuis le début)
  • et la fonction « peek » (à ne pas confondre avec « seek » !), qui elle permet de lire un octet dans un fichier (comme la fonction « read »), mais SANS faire avancer le pointeur de lecture, sur l’octet suivant à lire

Écrire dans un fichier (fonction write)

Pour écrire dans un fichier sur une carte SD, il n’y a rien de vraiment bien compliqué ! Car une seule ligne permet de le faire, la plupart du temps. Au passage, vous pourrez écrire un octet (caractère) à la fois, tout comme plusieurs à la fois. En fait, il vous suffit d’écrire :

  • SD.write(octetAecrire), pour écrire un octet dans le fichier préalablement ouvert, avec la fonction open
  • SD.write(ensembleDoctets, longueur), pour écrire un ensemble d’octets (de type « byte array », ou « char array »)

En retour, la fonction « write » renvoie le nombre d’octet qu’elle a pu écrire. Par contre, ne vous faites pas avoir ! Car, pour rappel, ces données ne sont réellement enregistrées qu’à l’appel de la fonction « close » (fermant le fichier), ou de la fonction « flush » (qui permet d’enregistrer tout ce qui était en attente d’enregistrement). Du coup, connaître le nombre d’octets écrits avec la fonction « write » n’est pas forcément quelque chose de pertinent, au sens où cela signifie simplement combien d’octets ont été écrits en mémoire tampon, en attente d’enregistrement « définitif ».

Sachez également qu’on retrouve 2 autres fonctions, semblables à la fonction « write » :

  • La fonction « print », qui va plus loin que la fonction write, en permettant d’enregistrer des chiffres ou nombres au format texte ; d’ailleurs, on peut également préciser le format des nombres à enregistrer, suivant si celui-ci est au format binaire (BIN / base 2), octal (OCT, base 8), décimal (DEC, base 10), ou hexadécimal (HEX, base 16).
  • La fonction « println », qui elle fait exactement la même chose que la fonction « print », mais en rajoutant un retour et saut à la ligne suivante en plus !

Remarque : la fonction « println » est très utile pour insérer en toute simplicité des sauts de lignes dans un fichier. Pour cela, il suffit d’écrire la commande suivante, à chaque fois que souhaité : « SD.println(); » (sans aucun argument, donc).

Créer un répertoire (fonction mkdir)

Vous aurez peut-être également besoin de créer un répertoire, afin de classifier tous vos différents fichiers sur carte SD. Pour cela, il existe une fonction nommée « mkdir », qui permet de créer un répertoire avec le nom voulu (mais attention : toujours en respectant le format 8.3, vu au début).

Cette fonction est particulièrement simple d’utilisation. Car là encore, une ligne suffit. Qui plus est, elle permet même de créer plusieurs sous-répertoires en même temps, si souhaité. Voici d’ailleurs deux exemples de syntaxe d’écriture, vous montrant comment créer un ou plusieurs répertoires à la fois :

  • Pour créer un répertoire du nom de « monrep », il suffit par exemple d’écrire : SD.mkdir(« monrep »);
  • Pour créer un répertoire « rep1 », avec un sous-répertoire « sousrepA » dedans, qui lui-même devra contenir un répertoire nommé « alpha », alors il suffit par exemple d’écrire : SD.mkdir(« rep1/sousrepA/alpha »); (cela va créer les 3 répertoires d’affilé)

En retour, la fonction « mkdir » renvoie :

  • TRUE si la création du ou des répertoires s’est bien déroulée
  • Ou FALSE, s’il y a eu un problème lors de la création du ou des répertoires

Supprimer un répertoire (fonction rmdir)

À l’inverse de la fonction précédente, « rmdir » permet de supprimer un répertoire ciblé, sur la carte SD. Mais ATTENTION, car ce répertoire doit au préalable être vidé de tout son contenu (c’est à dire qu’il ne doit contenir ni fichier, ni répertoire, avant d’essayer de l’effacer). Sinon, cela ne fonctionnera pas.

Au niveau du code, cette fonction s’utilise en toute simplicité. Voici deux exemples d’appel :

  • SD.rmdir(« monrep ») permet d’effacer un répertoire nommé « monrep »
  • SD.rmdir(« rep1/sousrepA/alpha ») permet d’effacer un répertoire nommé « alpha », et uniquement lui (les autres répertoires devront être supprimés séparément, un à un, si souhaité)

Comme pour la fonction « mkdir », cette fonction retourne TRUE ou FALSE, selon si l’opération a pu se faire ou pas.

Voilà pour cette partie « librairie » ! À présent, nous allons passer à une partie bien plus fun, à savoir : la pratique ! Au programme : 4 exemples concrets, vous montrant « tout » ce qu’il est possible de faire avec cette bibliothèque, afin que vous puissiez faire vos premiers pas avec ! Après, il ne tiendra qu’à vous de vous exercer, de vous l’approprier, et d’approfondir tout ça 😉

Exemple de code #1 : récupérer les infos d’une carte SD (taille, format, …)

Pour commencer, je vous propose de découvrir un exemple un peu en marge du reste, et qui n’attrait pas de suite à la manipulation de fichiers sur carte SD. En fait, ce programme est juste une petite aparté, pour apprendre à récupérer des infos de votre SD card depuis un Arduino.

Comme vous vous en doutez, avant de se lancer dans le cœur du programme, il faut tout d’abord qu’on aborde la partie câblage. Mais rassurez-vous, car ici, c’est vraiment rudimentaire ! En fait, il suffit d’alimenter votre lecteur de carte SD en +5V, par exemple, et de raccorder les broches de communication à votre Arduino, sur son port SPI (pins SCK, MISO, MOSI, et CS). En bref : comme nous avons déjà vu plus haut, dans la partie « raccordement à l’arduino ».

Pour ma part, utilisant un arduino uno, voici le schéma de raccordement que j’ai réalisé, pour câbler mon lecteur de carte Micro SD à mon Arduino Uno :

Schéma raccordement micro sd card sur arduino uno, câblage du port SPI et branchement de l'alimentation 5V, avec exemple de code card info

Une fois fait, voici le code que j’ai écrit, afin de pouvoir récupérer les infos de ma carte SD :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       RecupInfosCarteSD.ino
  
  Description :   Ce programme permet de récupérer tout un tas d'infos sur la carte SD raccordée à l'Arduino,
                  et d'afficher toutes ces données sur le Moniteur Série de l'IDE Arduino

  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       07.06.2021

*/
#include <SD.h>

// Variables SD Card
Sd2Card CarteSD;
SdVolume VolumeCarteSD;
uint32_t volumesize;

// Lecteur SD card branché sur les pins 10 (CS), 11 (MOSI), 12 (MISO), et 13 (SCK). Les 3 dernières étant le traditionnel bus SPI, il faut juste préciser la pin raccordée au chip select du lecteur de SD card
#define sdCardSelect 10

// ========================
// Initialisation programme
// ========================
void setup () {
  
  // Initialisation de la liaison série (pour retourner les infos au moniteur série de l'ordi)
  Serial.begin(9600);
  Serial.println(F("Affichage des informations de la carte SD raccordée à l'Arduino"));
  Serial.println(F("==============================================================="));
  Serial.println();

  // Étape 1 : test la présence d'une carte raccordée ou non
  if (!CarteSD.init(SPI_HALF_SPEED, sdCardSelect)) {
    Serial.println(F("Échec lors de l'initialisation. Essayez de jeter un coup d'oeil à : "));
    Serial.println(F("- est-ce que la carte SD est bien insérée dans le lecteur ?"));
    Serial.println(F("- est-ce que vos branchements électriques sont corrects ?"));
    Serial.println(F("- est-ce que le 'chipSelect' choisi dans le programme correspond bien à celui branché sur l'arduino ? (pin D10, par défaut)"));
    Serial.println();
    Serial.println(F("Appuyez sur RESET pour relancer le programme, au besoin."));
    while (1);      // Boucle infinie, arrêt du programme
  } else {
    Serial.println(F("Câblage correct, carte SD trouvée."));
    Serial.println();
  }

  // Étape 2 : déterminer le type de carte SD insérée dans le lecteur
  Serial.print(F("Type de carte SD insérée : "));
  switch (CarteSD.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println(F("SD1"));
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println(F("SD2"));
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println(F("SDHC"));
      break;
    default:
      Serial.println(F("Inconnu"));
  }
  Serial.println();

  // Étape 3 : tentative d'ouverture du volume/de la partition (qui doit être en FAT16, ou FAT32), et affichage du format
  if (!VolumeCarteSD.init(CarteSD)) {
    Serial.println(F("Aucune partition FAT16/FAT32 trouvée."));
    Serial.println(F("Vérifiez si votre carte SD est bien formatée !"));
    while (1);      // Boucle infinie, arrêt du programme
  } else {
    Serial.print(F("Format du volume : FAT"));
    Serial.println(VolumeCarteSD.fatType(), DEC);
  }

  // Étape 4 : affichage du nombre de cluster, et de blocs
  Serial.print(F("Nombre de clusters : ")); 
  Serial.println(VolumeCarteSD.clusterCount());
  Serial.print(F("Nombre de blocs par cluster : "));
  Serial.println(VolumeCarteSD.blocksPerCluster());
  Serial.print(F("Nombre total de blocs : "));
  Serial.println(VolumeCarteSD.blocksPerCluster() * VolumeCarteSD.clusterCount());
  Serial.println();

  // Étape 5 : affichage de la taille du volume, en KB/MB/GB
  volumesize = VolumeCarteSD.clusterCount() * VolumeCarteSD.blocksPerCluster();
  volumesize = volumesize / 2;         // Nota : les blocs d'une carte SD font toujours 512 octets (il faut donc 2 blocs pour faire 1KB)
  Serial.print(F("Taille du volume (en KB): ")); Serial.println(volumesize);
  Serial.print(F("Taille du volume (en MB): ")); Serial.println(volumesize / 1024);
  Serial.print(F("Taille du volume (en GB): ")); Serial.println(volumesize / 1024 / 1024.0);
  Serial.println();

  Serial.println(F("Fin !"));

}
 
// =================
// Boucle principale
// =================
void loop () {
  // Vide (tout se passe dans la fonction "setup" !)
}

Vous noterez l’utilisation de fonctions « F » un peu partout dans le code, au niveau des « Serial.print », afin d’économiser un maximum la mémoire RAM. Sans quoi… le programme pourrait devenir instable, du fait de manque d’espace suffisant.

Si tout est bien câblé, et le programme bien uploadé, vous devriez obtenir en réponse, sur le moniteur série de votre IDE Arduino, un écran semblable au mien (mais avec des valeurs propres à votre SD card, bien entendu !) :

Affichage moniteur série des infos carte SD raccordée à un Arduino Uno, pour afficher le type, format, et informations volume d'une sd card via port SPI

Nota : c’est vrai que je n’en ai pas parlé jusqu’à présent, mais il faut bien évidemment que votre carte SD soit formatée, afin de pouvoir vous en servir (et attention, car l’acheter neuve ne garantit pas qu’elle le soit déjà). Les formats courants pour une carte SD arduino sont le FAT16 ou FAT32, avec une préférence pour le FAT16 lorsque c’est possible (détails disponibles ici en anglais, sur le site arduino : https://www.arduino.cc/en/Reference/SDCardNotes).

Exemple de code #2 : lire et écrire un fichier dans une SD card (read/write/dump)

Maintenant que nous avons fait nos premiers pas avec la librairie SD.h, nous allons voir ici comment lire et écrire dans un fichier, sur une SD card. Pour ce faire, je vous propose de réaliser le même montage que précédemment (cf. exemple 1), avec un lecteur de carte Micro SD branché sur un Arduino Uno. De mon côté, voici d’ailleurs un aperçu du montage réalisé :

Branchement lecteur carte SD sur Arduino Uno avec fils dupont, câblage du port SPI sur le module sd card, avec raccordement de l'alim 5 volts et masse

Ici, nous allons réaliser une suite d’opérations, permettant de mettre en œuvre la plupart des fonctions les plus couramment utilisées. Ainsi, vous verrez comment se passent :

  1. L’initialisation de la communication avec un lecteur de SD card
  2. La suppression d’un fichier sur celle-ci, si déjà préexistant
  3. La création d’un fichier test, avec inscription d’une chaîne de caractère de test à l’intérieur
  4. Et la lecture de ce fichier (dump), avec l’affichage à l’écran de son contenu

En bref, de quoi faire un tour d’horizon complet sur les opérations de lecture, écriture, et suppression de fichier sur carte SD, depuis un Arduino. Pour y arriver, voici le programme que j’ai mis sur pied, pour réaliser cette série de tâches :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       LectureEcritureCarteSD.ino
  
  Description :   Ce programme permet de réaliser des opérations de lecture/écriture dans un fichier, sur une carte SD
                  branchée sur un Arduino Uno (les résultats s'afficheront sur le moniteur série de l'IDE arduino)

  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       09.06.2021

*/
#include <SD.h>

// Variables utilisées pour la SD Card
#define sdCardPinChipSelect   10                                  // Le lecteur de SD card sera branché sur les pins 10 (CS), 11 (MOSI), 12 (MISO), et 13 (SCK)
#define nomDuFichier          "fic-test.txt"                      // Format 8.3 (c'est à dire 8 lettres maximum pour le nom, et optionnellement 3 pour l'extension)
#define texteDeTest           "Blabla écrit dans un fichier logé sur la carte SD, via un Arduino !"
File monFichier;

// ========================
// Initialisation programme
// ========================
void setup () {
  
  // Initialisation de la liaison série (pour retourner les infos au moniteur série de l'ordi)
  Serial.begin(9600);
  Serial.println(F("Programe de test de lecture/écriture de fichier sur Carte SD, reliée à un Arduino Uno"));
  Serial.println(F("====================================================================================="));
  Serial.println();

  // ------------------------------------------------------------------
  // Étape 1 : test si la carte SD est bien accessible depuis l'arduino
  // ------------------------------------------------------------------
  Serial.println(F("Étape 1 : Initialisation de la carte SD :"));
  if (!SD.begin(sdCardPinChipSelect)) {
    Serial.println(F("Échec de l'initialization !"));
    while (1);    // Boucle infinie (stoppage du programme)
  }
  Serial.println(F("Initialisation terminée."));
  Serial.println();

  // ------------------------------------------------------------------
  // Étape 2 : suppression du fichier 'nomDuFichier' si existe déjà
  // ------------------------------------------------------------------
  Serial.println(F("Étape 2 : suppression du fichier '" nomDuFichier "', si déjà existant :"));
  if (!SD.exists(nomDuFichier)) {
    Serial.println(F("Fichier '" nomDuFichier "' inexistant."));
  }
  else {
    Serial.println(F("Fichier '" nomDuFichier "' déjà existant, donc lancement suppression."));    

    // Suppression du fichier déjà existant
    if(!SD.remove(nomDuFichier)) {
      Serial.println(F("Échec de suppression du fichier '" nomDuFichier "' présent sur la SD card !"));
      while (1);    // Boucle infinie (arrêt du programme)
    }
    else {
      Serial.println(F("Suppression du fichier existant réussie."));
    }
  }
  Serial.println();

  // ------------------------------------------------------------------
  // Étape 3 : écriture de données dans le fichier 'nomDuFichier'
  // ------------------------------------------------------------------
  Serial.println(F("Étape 3 : écriture de données dans le fichier '" nomDuFichier "' :"));
  monFichier = SD.open(nomDuFichier, FILE_WRITE);
  if (monFichier) {
    Serial.println(F("Écriture de la chaîne suivante, dans le fichier '" nomDuFichier "' : « " texteDeTest " »"));
    monFichier.println(texteDeTest);              // Écriture dans le fichier
    monFichier.close();                           // Fermeture du fichier
    Serial.println("Écriture terminée.");
  }
  else {
    Serial.println(F("Échec d'ouverture en écriture, pour le fichier '" nomDuFichier "', sur la carte SD !"));
    while (1);    // Boucle infinie (arrêt du programme)
  }
  Serial.println();

  // ------------------------------------------------------------------
  // Étape 4 : lecture des données précédemment enregistrées
  // ------------------------------------------------------------------
  Serial.println(F("Étape 4 : lecture des données précédemment enregistrées dans le fichier '" nomDuFichier "' :"));
  monFichier = SD.open(nomDuFichier, FILE_READ);
  if (monFichier) {
    Serial.println(F("Affichage du contenu du fichier '" nomDuFichier "', ci-après."));
    Serial.write("-> Texte présent dans le fichier = ");
    while (monFichier.available()) {              // Lecture, jusqu'à ce qu'il n'y ait plus rien à lire
      Serial.write(monFichier.read());            // ... et affichage sur le moniteur série !
    }
    monFichier.close();                           // Fermeture du fichier
    Serial.println(F("Lecture terminée."));
  }
  else {
    Serial.println(F("Échec lors de l'ouverture en lecture du fichier '" nomDuFichier "' sur la carte SD !"));
    while (1);    // Boucle infinie (arrêt du programme)
  }
  Serial.println();
  
  // ------------------------------------------------------------------
  // Fin du programme de test !
  // ------------------------------------------------------------------
  Serial.println(F("Fin !"));

}
 
// =================
// Boucle principale
// =================
void loop () {
  // Vide, car tout se passe dans la fonction "setup" !
}

À noter que vous aurez une variante d’affichage entre la 1ère exécution de ce programme et les suivantes, du fait que le fichier de test sera absent la première fois, et présent les fois d’après. D’ailleurs, le programme vous indiquera clairement à l’écran si le fichier de test est déjà présent sur la carte SD ou pas, et l’effacera le cas échéant. Pour illustrer cela, voici deux aperçus écran que j’ai pu faire de mon côté, vous montrant la première puis la seconde exécution du même programme.

La première exécution :

Opérations de suppression de fichier sd card depuis Arduino Uno, avec exemple de code pour manipulation de fichiers et inscription de données

Et la seconde fois (et fois d’après) :

Lecture écriture de fichier sur carte SD avec arduino uno, affichage moniteur série des données, avec test fonctions read, write, et remove, plus exemple codage

Voilà ! À présent, nous allons corser un peu plus les choses, en intégrant un capteur de température et hygrométrie, pour faire des mesures à intervalles réguliers, tout en enregistrant celles-ci sur carte SD. Un Arduino Uno restera aux commandes, pour piloter tout ce beau monde ! Alors en avant !

Exemple de code #3 : enregistrer des mesures de température et hygrométrie avec un DHT22 (data logger)

Que diriez-vous si nous fabriquions un datalogger ? C’est-à-dire un petit dispositif électronique, permettant d’enregistrer la température et le taux d’humidité ambiant, à intervalle régulier, tout en sauvegardant ces mesures au fur et à mesure, dans un fichier sur carte SD ! Avec un Arduino à la commande, comme toujours !

Pour ce faire, nous allons partir du montage précédent, et rajouter un capteur de t°/hygrométrie de type DHT22 sur la broche D8 de notre Arduino (une autre broche digitale aurait pu convenir). Si besoin, au passage, n’hésitez à lire le tutoriel sur le DHT22 que j’ai publié récemment, pour en apprendre plus sur ce capteur, à la fois simple, efficace, et pas cher 😉

Mais avant de nous jeter sur la partie codage, voyons ensemble le montage à réaliser :

Schéma câblage carte SD sur arduino uno avec dht22, pour réalisation datalogger permettant mesure et enregistrement température plus hygrométrie

Et pour donner vie à ce datalogger, voici le programme que j’ai écrit de mon côté :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       DataloggerSdDht22.ino
  
  Description :   Ce programme permet de faire des mesures de température et d'hygrométrie avec un DHT22, et de stocker
                  ces infos sur une carte SD, dans un fichier au format CVS (importable sous Excel)

  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       11.06.2021

*/
#include <SD.h>
#include <DHT.h>

// Variables utilisées pour la carte SD
const int   sdCardPinChipSelect     = 10;               // Le lecteur de carte SD sera branché sur la pin 10 pour le CS (chip select), 11/12/13 pour le MOSI/MISO/SCK du bus SPI
const char* nomDuFichier            = "donnees.csv";    // Format 8.3 (c'est à dire 8 lettres maximum pour le nom, et optionnellement 3 pour l'extension)
File monFichier;

// Variables utilisées pour le DHT22
const int brocheDeBranchementDHT    = 8;                // Le bus de communication du DHT22 sera branché sur la pin D8 de l'Arduino (mais libre à vous de le mettre ailleurs !)
const int typeDeDHT                 = DHT22;            // Ici, le type de DHT utilisé est un DHT22
DHT dht(brocheDeBranchementDHT, typeDeDHT);

// Autres variables
const long delaiIntervalleDeMesures = 2000;             // Intervalle de mesure/enregistrement (exprimé en millis secondes)
                                                        // ==> Nota : ne pas descendre en dessous de 2 secondes, car le DHT22 ne peut aller si vite

// ========================
// Initialisation programme
// ========================
void setup () {
  
  // Initialisation de la liaison série (pour retourner les infos au moniteur série de l'ordi)
  Serial.begin(9600);
  Serial.println(F("Programe DATALOGGER (enregistrement t°/hygro à partir d'un DHT22, et stokage des infos sur carte SD"));
  Serial.println(F("==================================================================================================="));
  Serial.println();

  // ----------------------------------------------------------------------------
  // Vérification : est-ce que la carte SD est bien accessible depuis l'arduino ?
  // ----------------------------------------------------------------------------
  Serial.print(F("Initialisation de la carte SD..."));
  if (!SD.begin(sdCardPinChipSelect)) {
    Serial.println();
    Serial.println();
    Serial.println(F("Échec de l'initialisation du lecteur de SD card. Vérifiez :"));
    Serial.println(F("1. que la carte SD soit bien insérée"));
    Serial.println(F("2. que votre câblage soit bon"));
    Serial.println(F("3. que la variable 'sdCardPinChipSelect' corresponde bien au branchement de la pin CS de votre carte SD sur l'Arduino"));
    Serial.println(F("Et appuyez sur le bouton RESET de votre Arduino une fois le pb résolu, pour redémarrer ce programme !"));
    while (true);
  }
  Serial.println(F(" réussie !"));
  Serial.println();

  // ----------------------------------------------------------------------------
  // Initialisation du DHT22
  // ----------------------------------------------------------------------------
  dht.begin();
}

// =================
// Boucle principale
// =================
void loop () {
   
  // Lecture des données
  float tauxHumidite = dht.readHumidity();        // Lecture du taux d'humidité, exprimé en %
  float temperature = dht.readTemperature();      // Lecture de la température ambiante, exprimée en degrés celsius
 
  // Vérification si données bien reçues
  if (isnan(tauxHumidite) || isnan(temperature)) {
    Serial.println(F("Aucune valeur retournée par le DHT22. Est-il bien branché ?"));
    delay(2000);
    return;         // Si aucune valeur n'a été reçue par l'Arduino, on attend 2 secondes, puis on redémarre la fonction loop()
  }

  // Mise en forme de ces données (un seul chiffre après la virgule)
  String tauxHumiditeArrondi = String(tauxHumidite,1);    // Affichage d'une seule décimale (car précision de 0,1 % ici)
  String temperatureArrondie = String(temperature,1);     // Affichage d'une seule décimale (car précision de 0,1 °C ici)
  tauxHumiditeArrondi.replace(".", ",");                  // et on remplace les séparateurs de décimale, initialement en "." pour devenir ","
  temperatureArrondie.replace(".", ",");                  // (permet par exemple d'affichier 12,76 au lieu de 12.76 (ce qui facilite l'interprétation dans Excel ou tout autre tableur)
 
  // Affichage des valeurs sur le moniteur série de l'IDE Arduino
  Serial.print("Humidité = "); Serial.print(tauxHumiditeArrondi); Serial.print(" % - ");
  Serial.print("Température = "); Serial.print(temperatureArrondie); Serial.println(" °C");

  // Enregistrement de ces données sur la carte SD
  monFichier = SD.open(nomDuFichier, FILE_WRITE);
  if (monFichier) {    
    monFichier.print(tauxHumiditeArrondi);
    monFichier.print(";");                  // Délimiteur du fichier CSV
    monFichier.println(temperatureArrondie);
    monFichier.close();                     // L'enregistrement des données se fait au moment de la clôture du fichier
    Serial.println(F("Enregistrement réussi en carte SD"));
  }
  else {
    Serial.println(F("Erreur lors de la tentative d'ouverture du fichier en écriture, sur la carte SD"));
  }

  // Temporisation de X secondes (2 sec min, pour que le DHT22 ait le temps de faire ses mesures)
  Serial.println();
  delay(delaiIntervalleDeMesures);
}

J’en profite d’ailleurs pour vous simuler différents cas possibles, suivant :

  • si vous rencontrez un problème sur votre lecteur de carte SD
  • si vous rencontrez un problème sur votre capteur de température et hygrométrie DHT22
  • ou… si tout se passe bien !

Exemple de retour si problème d’initialisation de la carte SD :

Datalogger avec problème carte SD branchée sur Arduino Uno, branchement port SPI avec MISO, MOSI, SCK, et SS, puis initialisation sd card par code

Exemple de retour si problème au niveau du capteur DHT22 :

Datalogger avec problème capteur température et hygrométrie DHT22, branché sur Arduino Uno, affichage erreur de lecture moniteur série de l'IDE

Et si tout se passe bien, voici un exemple de retour sur le moniteur série de l’IDE Arduino :

Datalogger enregistrement mesures DHT22 sur carte SD avec Arduino Uno, température et taux d'humidité mesurés air ambiant, affichage moniteur série IDE Arduino

Parallèlement, vous pourrez retrouver toutes ces valeurs inscrites dans un fichier, sur votre carte SD. Ce fichier porte le nom de « DONNEES.CSV » (tout simplement !), et toutes les mesures prises y seront stockées. Celles-ci seront formatées de la manière suivante : TAUX D’HUMIDITÉ ; TEMPÉRATURE (avec un point-virgule de séparation entre ces deux valeurs, et un saut/retour à la ligne pour passer aux valeurs suivantes). Voici d’ailleurs un extrait du fichier présent sur ma carte SD, après exécution du programme (à noter qu’il n’y a pas le moindre texte, mais seulement des données numériques à 1 décimale, et séparées entre-elles par un point-virgule) :

65,3;25,7
65,3;25,6
65,3;25,6
65,3;25,6
65,2;25,6
65,2;25,6
65,2;25,6
65,2;25,6
65,3;25,6
65,6;25,6
66,0;25,7
66,5;25,7
66,6;25,7
66,6;25,7
66,6;25,7
66,7;25,7
66,8;25,7
66,7;25,7
66,5;25,7
66,3;25,6
66,1;25,7

Ensuite, comme moi, vous pouvez importer ces valeurs dans un tableur (de type Excel ou OpenOffice Calc), pour générer de beaux graphiques ! D’ailleurs, voici ce que j’ai obtenu de mon côté, sur la base de données collectées au bout d’une petite heure de fonctionnement (à raison d’un échantillon de mesure toutes les deux secondes, ici) :

Courbes température hygrométrie dans tableur, mesures DHT22 enregistrées sur carte SD depuis Arduino Uno, fichier CSV données capteur électronique

Bien sûr, il pourrait être bien plus sympa de faire des relevés sur une journée complète (24 heures, donc), avec un intervalle de mesure toutes les minutes, par exemple !

Remarque : au niveau du code de programmation présenté un peu plus haut, j’ai fait exprès ici de changer le format des différentes type de constantes. En effet, dans l’exemple 2, j’avais utilisé des « #define » pour définir le numéro de la pin CS et le nom du fichier test. Ici, dans l’exemple 3, j’ai utilisé les formats « const int » pour la pin CS, et « const char* » pour le nom de fichier. Ceci est juste fait pour vous montrer qu’il y a différentes façons pour arriver au même résultat… mais encore faut-il les connaître 😉

Exemple de code #4 : génération d’un nom de fichier aléatoire, et enregistrement sur carte SD

Dernier exemple que j’ai tenu à vous partager ici : un code permettant de générer un nom de fichier aléatoire, avec enregistrement sur carte SD. Et si j’ai tenu à vous faire cela, c’est avant tout pour mettre en avant un « petit défaut » de la librairie SD.h, utilisée ici. En fait, ce défaut se situe au niveau du nom de fichier que l’on renseigne en utilisant la fonction « SD.open ». En effet :

  • Si vous utilisez une constante (type #define ou const char*) pour définir votre nom de fichier, alors vous n’aurez aucun souci
  • Mais si utilisez une variable pour définir votre nom de fichier, alors vous aurez un petit message au moment de la compilation, sous la forme d’un « warning » (c’est-à-dire qu’un message écrit en rouge, mais qui n’empêchera pas la finalisation de la compilation, puis l’upload du programme dans votre Arduino)

Si je vous parle de ça, c’est parce que vous voudrez certainement utiliser un nom de fichier personnalisable, suivant ce que vous cherchez à enregistrer. C’est par exemple le cas lorsqu’on nomme un fichier en y intégrant la date du jour où les données sont enregistrées. Dans ce cas, votre fichier pourrait par exemple s’écrire sous la forme « AAAAMMJJ.TXT », avec AAAA pour l’année, MM pour le mois, et JJ pour le jour.

Au passage, si tout ce que je vous explique ici dans ce dernier exemple ne vous semble pas très clair, ne vous inquiétez pas. Car ce n’est pas vital en soit, mais juste intéressant à connaître. Qui plus est, cela vous permettra de vous y référer, si jamais vous obtenez le même message d’erreur que moi.

Alors… pour ce 4ème exemple, je vous propose de refaire le montage que nous avons fait au début, à l’occasion du 1er exemple. Ensuite, au niveau programmation, voici le programme que je vous propose d’exécuter, permettant de générer un nom de fichier aléatoire (composé de 1 à 8 chiffres en fait), avec une extension « .txt » :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       NomDeFichierDynamique.ino
  
  Description :   Ce programme montre comment générer des noms de fichiers au hasard, afin de
                  pouvoir nommer des fichiers de manière personnalisée, sur la carte SD

  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       08.07.2021

*/
#include <SD.h>

// Variables utilées pour la SD Card
#define sdCardPinChipSelect   10                   // Le lecteur de SD card sera branché sur les pins 10 (CS), 11 (MOSI), 12 (MISO), et 13 (SCK)
char nomDuFichier[13];
File monFichier;

// ========================
// Initialisation programme
// ========================
void setup () {
  
  // Initialisation de la liaison sérielle (pour recupérer les infos en retour, sur le moniteur série de l'IDE Arduino)
  Serial.begin(9600);
  Serial.println(F("Programe de test de génération de noms dynamiques, à enregistrer sur carte SD"));
  Serial.println(F("============================================================================="));
  Serial.println();

  randomSeed(analogRead(0));      // Préparation de la génération d'un nombre aléatoire ; servira à la fonction random(), utilisée plus loin

  // -----------------------------------------------------------------
  // Étape 1 : on teste si la carte SD est accessible depuis l'arduino
  // -----------------------------------------------------------------
  Serial.println(F("Étape 1 : Initialisation de la carte SD :"));
  if (!SD.begin(sdCardPinChipSelect)) {
    Serial.println(F("Échec de l'initialization !"));
    while (1);    // Boucle infinie (arrêt du programme)
  }
  Serial.println(F("Initialisation réussie."));
  Serial.println();

  // --------------------------------------------------
  // Étape 2 : génération d'un nom de fichier dynamique
  // --------------------------------------------------
  Serial.println(F("Étape 2 : génération d'un nom de fichier aléatoire (de 1 à 8 chiffres) :"));
  long nombre8chifres = random(99999999);         // Génére un nombre aléatoire compris entre 0 et 9999 9999
  char baseFichier[8];
  ltoa(nombre8chifres, baseFichier, 10);          // Stocke le nombre décimal (base 10) au format "long", dans une variable au format "char[]"
  
  strcat(nomDuFichier, baseFichier);              // Créer la base du nom de fichier (8 caractères max, pour rappel)
  strcat(nomDuFichier, ".txt");                   // Puis ajoute une extension à ce nom (3 caractères maximum)
  
  Serial.print(F("Nom du fichier à créer = "));   // Et affiche ce nom sur le moniteur série de l'IDE Arduino
  Serial.println(nomDuFichier);
  Serial.println();

  // ---------------------------------------------
  // Étape 3 : création du fichier sur la carte SD
  // ---------------------------------------------
  Serial.println(F("Étape 3 : enregistrement d'une chaîne de caractères dans ce fichier :"));
  monFichier = SD.open(nomDuFichier, FILE_WRITE);         // Créé le fichier (à noter qu'on ne vérifie pas ici si le fichier existe déjà, pour ce programme servant uniquement de test)
  
  if (monFichier) {
    Serial.print(F("Écriture d'une chaîne de caractères dans le fichier '"));
    Serial.print(nomDuFichier);
    Serial.println(F("'"));
    monFichier.println("Ligne de test !");                // On écrit "Ligne de test !" dans le fichier qu'on vient de créer, puis on le referme
    monFichier.close();
  } else {
    Serial.print(F("-> Erreur lors de la tentative de création du fichier '"));
    Serial.print(nomDuFichier);
    Serial.println(F("'"));
  }
  Serial.println();
  
  // ------------------------------------------------------------------
  // Fin du programme de test !
  // ------------------------------------------------------------------
  Serial.println(F("Fin !"));

}
 
// =================
// Boucle principale
// =================
void loop () {
  // Vide ici, car tout se passe dans la fonction "setup" !
}

Et comme je vous disais, voici ce que vous devriez voir apparaître en bas de votre IDE Arduino, après compilation du programme (des lignes en rouge, dans la partie basse de l’écran) :

Erreur compilation librairie SD.h arduino avec message erreur pathidx, après utilisation avec nom de fichier variable dynamique dans code exemple

Pour être plus précis, voici ce message d’erreur qui y figure, au format texte :

C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.49.0_x86__mdqgnx93n4wtt\hardware\arduino\avr\cores\arduino\main.cpp: In function 'main':
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.49.0_x86__mdqgnx93n4wtt\libraries\SD\src\SD.cpp:462:14: warning: 'pathidx' may be used uninitialized in this function [-Wmaybe-uninitialized]
     filepath += pathidx;
              ^
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.49.0_x86__mdqgnx93n4wtt\libraries\SD\src\SD.cpp:456:9: note: 'pathidx' was declared here
     int pathidx;
         ^

Bien sûr, ce petit « bug » de librairie pourrait être corrigé à même la librairie, mais en cas de mise à jour de cette dernière, toutes ces modifications pourraient être perdues. Du coup, pour ma part, je laisse les choses telles quel, sachant qu’elles n’empêchent en rien la bonne exécution du programme ! Pour preuve : voici ce que j’obtiens sur mon moniteur série (sachant que le nom du fichier généré change à chaque exécution, bien sûr, comme prévu !).

Génération nom de fichier variable sur carte SD depuis Arduino Uno, exemple de code programmation automatique avec librairie SD.h

Bien évidemment, on aurait pu utiliser une autre librairie arduino ici, pour « contourner » ces warning à la compilation. Mais j’ai préféré rester sur des choses simples et fonctionnelles, pour les débutants, tout en sachant qu’on pourra ensuite pousser les choses un peu plus, une fois les bases maîtrisées !

Carte SD Arduino : conclusion !

Nous voici au terme de cet article sur comment brancher un lecteur de carte SD sur un Arduino ! J’espère que tout ceci vous aura plu, et que cela vous donnera envie d’intégrer des cartes SD à vos futurs montages ! J’espère également que tous les exemples que je vous ai concocté seront suffisamment clairs pour vous, afin de bien comprendre les fonctions de base pour piloter une SD card depuis un Arduino. Du reste, il ne s’agit évidemment là que d’une introduction aux SD card, et une base d’apprentissage pour tous ceux qui débutent. Il faudra bien évidement compléter tout cela, et creuser au niveau d’autres librairies arduino plus poussées, si vous souhaitez aller bien plus loin !

Bon… quant à moi, il ne me reste plus qu’à vous souhaiter une bonne journée, et à vous dire : à la prochaine 😉

Jérôme.

À découvrir aussi : comment lire ou écrire dans la mémoire EEPROM d’un Arduino ?

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

(*) Mis à jour le 11/07/2021

13 commentaires sur “Carte SD Arduino : branchement, librairie de base, et exemples de code (pour Arduino Uno, Nano, Mega)”

  1. J’ai déjà utilisé des cartes SD dans des applications, mais merci pour cet article parfaitement exposé, comme tout d’ailleurs !
    Des tutos à garder précieusement pour les applications futures.
    Bonne journée Jérôme !

  2. Site passionelectronique.fr
    Bénédiction LIKANGA

    Bonjour Jérôme.
    Ça a été un plaisir de lire cet article.
    Je vous souhaite une bonne continuation. Plus ces tutoriels nous serons sûrement bénéfique.
    Bien à vous,
    Bénédiction LIKANGA.

  3. Bonjour,

    Merci pour ce Tuto explicatif très bien fait.

    Il manque juste une chose, c’est comment mettre les valeurs lue sur la carte SD dans une variable pour l’utiliser dans le programme.
    Les afficher sur le moniteur série est très simple, mais les stocker dans une variable, c’est autre chose.

    Perso je sèche la dessus.

    Très amicalement,
    Christian

    1. Bonsoir Christian !

      Hum… je vois un moyen « simple » y arriver, en contournant ton problème. Mais je ne sais pas si cette solution te conviendra 😉

      Tu pourrais par exemple enregistrer tes variables dans un ficher au format texte, sur ta carte SD, à raison d’un contenu de variable par ligne (avec un code spécifique à chaque début de ligne, te permettant d’identifier chaque variable par la suite). Ainsi, au moment de la lecture, tu pourrais lire octet par octet ce fichier, jusqu’à reconstituer des lignes « complètes », avec des codes identifiant tes variables à chaque début de ligne. Et donc, en faisant au passage des conversions de format à la volée (type « string to int » si besoin, par exemple), tu pourrais « restocker » des valeurs issues d’un fichier sur carte SD, dans tes variables.

      En espérant que cette solution soit assez claire, et qu’elle puisse te convenir ou t’inspirer !

      @+
      Jérôme.

  4. Bonjour je découvre arduino et je suis en train de faire un petit montage pour enregistrer les paramètres (température, humidité, éclairage, etc) de ma serre. Cet article m’est bien utile, merci.

  5. Site passionelectronique.fr
    Ivan-joel Tefeguim

    Salut Jerome.

    J’ai un soucis lors que je veux enregistrer les données de mon capteur sur ma carte sd utilisant arduino nano. Voici le code que je reçois :

    Arduino : 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Carte : "Arduino Nano, ATmega328P (Old Bootloader)"
    
    Capteur_test:47:2: error: 'monFichier' does not name a type
    
    exit status 1
    'monFichier' does not name a type

    Ce rapport pourrait être plus détaillé avec l’option « Afficher les résultats détaillés de la compilation » activée dans Fichier -> Préférences.

    1. Salut à toi !

      En fait, il faut que tu précises de quel type est ta variable « monFichier ». Ainsi, l’erreur devrait disparaître.
      Tu peux faire cela tout simplement, comme je l’ai fait dans l’exemple de code #4, en mettant une ligne du style « File monFichier; » en entête, pour faire cela.

      @+, et bon courage à toi 😉
      Jérôme.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Symbole danger point d'exclamation site PSE, triangle jaune avec contour noir, texte noir alerte au milieu Afin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard.