mardi 27 août 2019

Écrire sur une carte SD avec l'ESP32

Voyons un peu comment procéder pour écrire ou lire des fichiers sur une carte SD avec un ESP32. Ça peut s'avérer utile pour archiver des valeurs mesurées par des capteurs branchés à l'ESP32, par exemple.

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
L'auteur du sketch n'est pas du genre à abuser des commentaires, mais en lisant la section setup() du sketch, on parvient à avoir une assez bonne idée de la façon d'utiliser ces foncions.

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à).




/*
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.

/**************************************************************
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&eacute;centes mesures enregistr&eacute;es</H1>"
"<p>" + contenu + "<p>"
"<p>Visitez <a href=http://electroniqueamateur.blogspot.com/>&Eacute;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


Yves Pelletier   (TwitterFacebook)

1 commentaire: