En plus de l'ESP32 et d'une carte SD, nous aurons besoin d'un lecteur de carte SD du genre illustré ci-dessous, qui communique avec le microcontrôleur au moyen du protocole SPI (en fait, il est possible de lier directement les broches de l'ESP32 aux connecteurs de la carte, mais ça ne me semble pas très pratique).
Nous programmerons l'ESP32 au moyen de l'IDE Arduino donc, au besoin, vous pouvez vous référer à ce précédent tutoriel pour obtenir des informations supplémentaires.
Connexions
Puisque le module communique par SPI, les connexions sont les suivantes:
- GND du module SD --- GND de l'ESP32
- +3.3 du module SD --- 3V3 de l'ESP32
- +5 du module SD --- pas branché
- CS du module SD --- GPIO 5 de l'ESP32*
- MOSI du module SD --- GPIO 23 de l'ESP32
- SCK du module SD --- GPIO 18 de l'ESP32
- MISO du module SD --- GPIO 19 de l'ESP32
* La broche CS peut être modifiée dans le sketch, si désiré.
Exemple SD_Test
En général, une fois le module SD connecté, je recommande l'essai du sketch d'exemple CardInfo fourni avec l'IDE Arduino. Mais bien que ce sketch fonctionne correctement avec les cartes Arduino conventionnelles, avec l'ESP8266 et avec les cartes à base de STM32, il est incompatible avec l'ESP 32.
Nous allons plutôt nous rabattre sur le sketch SD_Test, accessible par le menu Fichier - Exemples - SD(esp32) - SD_Test.
Ce sketch comporte 8 routines utiles pour interagir avec une carte SD:
- listDir: retourne la liste du contenu d'un répertoire
- createDir: créé un répertoire
- removeDir: efface un répertoire
- readFile: lit le contenu d'un fichier
- writeFile: remplace le contenu d'un fichier
- appendFile: ajoute du contenu supplémentaire dans un fichier
- renameFile: renomme un fichier
- deleteFile: efface un fichier
Si vous exécutez ce sketch pendant qu'une carte SD se trouve dans le lecteur branché à l'ESP32, le moniteur série devrait vous présenter des informations concernant le contenu de votre carte, ce qui démontrera que vos branchements sont corrects. Si le module SD est mal branché, le moniteur série se contentera d'afficher le message "Card Mount Failed".
Exemple 1: écrire des valeurs mesurées dans un fichier texte
Dans le sketch ci-dessous, je mesure toutes les 5 secondes la valeur analogique de la broche GPIO 36, et j'enregistre le résultat dans un fichier nommé "Valeurs.txt" sur la carte SD (le fichier est créé s'il n'existe pas déjà).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Toutes les 5 secondes, le signal analogique | |
de la broche GPIO 36 est consigné dans un fichier | |
sur une carte SD. | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/08/ecrire-sur-une-carte-sd-avec-lesp32.html | |
*/ | |
// inclusion des bibliothèques utiles | |
#include "FS.h" | |
#include "SD.h" | |
#include "SPI.h" | |
const int broche_CS = 5; // broche CS du module SD branché à GPIO 5 | |
const int broche_lecture = 36; // un potentiomètre est branché à GPIO 36 | |
unsigned long tempsPrecedent; | |
void appendFile(fs::FS &fs, const char * path, const char * message) { | |
Serial.printf("Ecriture dans le fichier: %s\n", path); | |
File file = fs.open(path, FILE_APPEND); | |
if (!file) { | |
Serial.println("Echec d'ouverture du fichier"); | |
return; | |
} | |
if (file.print(message)) { | |
Serial.println("Fichier modifié avec succes."); | |
} else { | |
Serial.println("Echec de la modification du fichier."); | |
} | |
file.close(); | |
} | |
void setup() { | |
Serial.begin(115200); | |
if (!SD.begin(broche_CS)) { | |
Serial.println("Carte SD introuvable"); | |
return; | |
} | |
else{ | |
Serial.println("Carte SD détectée"); | |
} | |
tempsPrecedent = millis(); | |
} | |
void loop() { | |
char message[10]; | |
if ((millis() - tempsPrecedent) >= 5000) { | |
int mesure = analogRead(broche_lecture); | |
// conversion de la valeur numérique en chaîne de caractères | |
sprintf(message,"%d \n", mesure); | |
Serial.print("Valeur mesurée: "); | |
Serial.print(message); | |
appendFile(SD, "/Valeurs.txt", message); | |
tempsPrecedent = millis(); | |
} | |
} |
Exemple 2: data logger consultable par WiFi
Ce deuxième sketch est la version ESP32 d'un projet préalablement réalisé sur l'ESP8266: en plus d'enregistrer la valeur mesurée (ainsi que la date et l'heure de la mesure) sur une carte SD, l'ESP32 produit une page web permettant de consulter les 10 mesures les plus récentes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************** | |
Data Logger ESP32 | |
Les mesures d'un capteur sont enregistrées à intervalle | |
régulier sur une carte SD. | |
On peut consulter, par l'entremise d'une page web, les | |
10 plus récentes valeurs enregistrées. | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/08/ecrire-sur-une-carte-sd-avec-lesp32.html | |
*****************************************************************/ | |
// bibliothèques pour le lecteur de carte SD: | |
#include "FS.h" | |
#include <SPI.h> | |
#include <SD.h> | |
// bibliothèques pour la communication WiFi | |
#include "WiFi.h" | |
#include <WebServer.h> | |
// bibliothèque pour l'affichage de la date et de l'heure | |
#include <time.h> | |
// adresse et mot de passe du réseau wifi | |
const char* ssid = "**********"; | |
const char* password = "**********"; | |
// constantes | |
const int delaiEnMinutes = 1; // combien de minutes entre deux mesures consécutives? | |
const int stockage_max = 10; //nombre maximal de données stockées en mémoire, pour affichage dans la page web | |
const int decalage = -5; // la valeur dépend de votre fuseau horaire. Essayez 2 pour la France. | |
// variables globales | |
unsigned long derniereMesure; // derniere fois qu'une mesure a été prise | |
int mesures[stockage_max]; // tableau contenant les plus récentes mesures | |
time_t date_et_heure[stockage_max]; // tableau contenant le moment des plus récents scans | |
int nb_de_donnees = 0; // nombre de données déjà stockées dans mesures et dateHeure | |
File monFichier; // fichier sur la carte SD | |
WebServer serveur(80); | |
// Construction de la page web | |
void handle_root() { | |
struct tm * timeinfo; | |
String contenu; | |
for (int i = 0; i < stockage_max; i++) { | |
if (date_et_heure[i] != 0) | |
{ | |
timeinfo = localtime(&date_et_heure[i]); | |
contenu.concat( "<p> Date et heure: " + String(timeinfo->tm_mday) + "-" + String(timeinfo->tm_mon + 1) + "-" + String(timeinfo->tm_year + 1900) + " " + String(timeinfo->tm_hour) + ":" + String(timeinfo->tm_min) + ":" + String(timeinfo->tm_sec) + " Mesure: " + String(mesures[i]) + "</p>"); | |
} | |
} | |
serveur.send(200, "html", "<head> <title>Data logger ESP8266</title> <meta http-equiv=Refresh content=30></head> " | |
"<body><H1>" + String(stockage_max) + " plus récentes mesures enregistrées</H1>" | |
"<p>" + contenu + "<p>" | |
"<p>Visitez <a href=http://electroniqueamateur.blogspot.com/>Électronique en Amateur</a>!</p></body>"); | |
delay(100); | |
} | |
// ajout d'information sur le fichier dans la carte SD | |
void appendFile(fs::FS &fs, const char * path, const char * message) { | |
Serial.printf("Ecriture dans le fichier: %s\n", path); | |
File file = fs.open(path, FILE_APPEND); | |
if (!file) { | |
Serial.println("Echec de l'ouverture du fichier"); | |
return; | |
} | |
if (file.print(message)) { | |
Serial.println("Modification du fichier reussie."); | |
} else { | |
Serial.println("Echec de la modification du fichier"); | |
} | |
file.close(); | |
} | |
// Initialisation du WiFi et de la carte SD. Les progrès s'affichent dans le moniteur série | |
// pour faciliter le débogage, et pour connaître l'adresse IP de l'ESP8266. | |
void setup() | |
{ | |
Serial.begin(9600); | |
Serial.println(); | |
WiFi.begin(ssid, password); | |
Serial.print("Connexion au reseau WiFi"); | |
while (WiFi.status() != WL_CONNECTED) { | |
Serial.print("."); | |
delay(1000); | |
} | |
Serial.println(); | |
Serial.print("Nom du reseau WiFi: "); | |
Serial.println(ssid); | |
Serial.print("Adresse IP de la page web: "); | |
Serial.println(WiFi.localIP()); | |
serveur.on("/", handle_root); | |
serveur.begin(); | |
Serial.println("Le serveur web est en fonction."); | |
configTime(decalage * 3600, 0, "ca.pool.ntp.org"); //serveurs NTP canadiens | |
// en Europe, essayez europe.pool.ntp.org ou fr.pool.ntp.org | |
Serial.print("Attente date et heure"); | |
while (time(nullptr) <= 100000) { | |
Serial.print("."); | |
delay(1000); | |
} | |
Serial.println(); | |
Serial.println("Pret."); | |
Serial.print("Initialisation de la carte SD..."); | |
if (!SD.begin(5)) { // CS du lecteur de carte branché à GPIO5 | |
Serial.println("echec!"); | |
while (1); | |
} | |
Serial.println("reussie"); | |
} | |
void loop() | |
{ | |
int nouvelleMesure; | |
time_t heureMesure; | |
struct tm * timeinfo; | |
if ((millis() - derniereMesure) >= delaiEnMinutes * 60000) // c'est le moment de prendre une nouvelle mesure | |
{ | |
nouvelleMesure = analogRead(A0); // mesure de l'entrée analogique A0 | |
time(&heureMesure); // quelle heure est-il? | |
timeinfo = localtime(&heureMesure); | |
// mise à jour du fichier sur la carte SD | |
char message[100]; | |
sprintf (message, "Date et heure: %d-%d-%d %d:%d:%d Mesure: %d \n", timeinfo->tm_mday, timeinfo->tm_mon + 1, timeinfo->tm_year + 1900, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, nouvelleMesure); | |
appendFile(SD, "/Mesures.txt", message); | |
// Stockage des 10 mesures les plus récentes pour affichage dans la page web | |
if (nb_de_donnees < stockage_max) { // il y a encore des lignes vides pour l'affichage web | |
mesures[nb_de_donnees] = nouvelleMesure; | |
date_et_heure[nb_de_donnees] = heureMesure; // quelle heure est-il? | |
nb_de_donnees++; | |
} | |
else // la page web est pleine | |
{ | |
for (int i = 0; i < stockage_max - 1; i++) { // on décale toutes les infos d'une ligne | |
mesures[i] = mesures[i + 1]; | |
date_et_heure[i] = date_et_heure[i + 1]; | |
} | |
mesures[stockage_max - 1] = nouvelleMesure; // on enregistre la nouvelle info | |
date_et_heure[stockage_max - 1] = heureMesure; // quelle heure est-il? | |
} | |
derniereMesure = millis(); | |
} | |
serveur.handleClient(); | |
} |
Articles similaires
- D'autres tutoriels comportant un module lecteur de carte SD ont été publiés sur ce blog: carte SD et Arduino Uno, carte SD et ESP8266, carte SD et Bluepill, carte SD et STM32 Nucleo, carte SD et MSP430 Launchpad...
- Vous pouvez également lire et enregistrer des fichiers dans la mémoire flash de l'ESP32
- Vous pouvez également consulter la liste des articles concernant l'ESP32.
Yves Pelletier (Twitter, Facebook)