Catégories
Algorithme Arduino électronique Interruption Arduino Projets Arduino Projets électroniques Projets microcontrôleurs

Projet Gestion de la mémoire SD avec Arduino

Objectifs du projet

    1. Savoir comment lire les informations d’une carte mémoire
    2. Savoir comment lire un fichier dans la carte SD
    3. Savoir comment créer un nouveau fichier
    4. Savoir comment mettre à jour un fichier de données
    5. Savoir comment formater les données dans un fichier
    6. Savoir dater, et stocker les données dans la carte mémoire
    7. Savoir utiliser une carte mémoire avec Arduino
    8. Savoir développer un Data logger avec Arduino
    9. Etc.

Fonctionnement du projet

L’objectif projet et l’acquisition et stockage des données dans une carte mémoire. On fait la lecture du pin analogique A0 toute les secondes. On utilise un buffer (tableau) pour le stockage local dans la carte Arduino. Après le remplissage du tableau, on transfert ce dernier à la carte mémoire SD. On peut varier la taille du tableau via le paramètre N. Chaque ligne du fichier contient la donnée en format double séparé par des virgules ainsi la date et l’heure d’acquisition de ce dernier à la fin de la ligne. Ci-dessous le format de stockage des données dans les lignes du fichier:

DATA1,…, DATAN, Année, …, Secondes,

DATA1,…, DATAN, Année, …, Secondes,

DATA1,…, DATAN, Année, …, Secondes,

DATA1,…, DATAN, Année, …, Secondes,

….

Capteur et module RTC

Avant d’abordé le projet, je vous recommande de consulter la partie 1 et 2 du projet concernant la partie capteur et le module RTC pour la sauvegarde du temps.

Lecteur de la carte SD

Le lecteur de la carte MicroSD utilise l’interface SPI pour interfacer avec la carte Arduino via les pins SCK (Horloge), MOSI (Master Out Slave  In) pour l’envoie des données vers de la carte Arduino vers le module et MISO (Master In Slave  Out) pour la récupération des données de la carte. L’interface SPI est une interface synchrone, il utilise un signal d’horloge indépendant. Le pin CS est utilisé pour l’activation du module de la carte SD. Il est activé niveau : Il faut positionner le pin à 5V afin d’activer le lecteur. Le pin CS est très important lorsqu’on a besoin de contrôler plusieurs lecteurs avec une seule interface. Par exemple, si on dispose de 3 cartes SD de 34 Go, alors on peut sélectionner une carte à la fois grâce aux pins CS1, CS2 et CS1 !

Le lecteur est alimente par une alimentation de 5V s’il dispose d’un régulateur interne comme le modèle illustré ci-dessous. Dans le cas échéant, la tension d’alimentation est souvent 3.3V ! Il faut bien s’assurer des caractéristiques de son lecteur avant d’effectuer le câblage au risque de détériorer le lecteur ou le sous alimentation de ce dernier. Un lecteur de 5V alimenté par 3.3V risque de ne pas fonctionner!

La module ci-dessous est testé et fonctionnel avec une carte Micro SD de 4 Go et 64 Go (voir la vidéo pour plus des détails).  Avant d’incruster la puce dans le lecteur, assurez-vous bien qu’elle est bien formater avec le système FAT16 ou FAT32. Il est recommandé aussi d’avoir une carte dédiée pour son stockage des données. Ci-dessous un lien vers un mini logiciel pour le formatage en format FAT32. Vous pouvez trouver d’autres logiciels dans le web en cas du besoin.

Télécharger le logiciel FAT32 Format

Lecteur carte SD

C’est  quoi la différence entre FAT16 et FAT32 ?

FAT16 (File Allocation Table), soit « table d’allocation de fichiers, ce de divise l’espace disponible sur un disque dur ou mémoire SD en 65536 blocs (ou clusters)  portant chacun un numéro propre. Si le nombre de blocs reste constant, leur taille varie en fonction de la taille du périphérique de stockage. À partir de Windows 95 OSR2, l’utilisateur a le choix entre FAT16 et FAT32. Ce système de fichiers est également utilisé sur les cartes SD. FAT16 permet d’avoir des partitions, selon la taille des clusters, occupant jusqu’à 2 Go, et exceptionnellement 4 Go. Concrètement, les spécifications  de FAT16 sont les suivantes :

  • Taille maximale d’un fichier : 4 Go – 1 octet (soit exactement 4 294 967 295 octets) ;
  • Nombre maximal de fichiers par partition : 65 524 ;
  • La racine du disque est de plus limitée à 512 entrées ; une entrée est un fichier ou un dossier, lire la suite…
  • Taille maximale de la mémoire 2 To !

A retenir,  au moment de stockage des donnés dans la carte SD,  la taille du fichier ne doit pas dépasse au maximum 4Go. Il faut mettre en place un système de création des fichiers dans la carte SD avec une taille modérée (voir la vidéo et la suite de l’article). En cas de dépassement de la capacité, il peut la remise à zéro du pointeur des lignes du fichier (écraser les données anciennes).

Tout savoir sur le système des fichiers FAT32

Schéma de câblage avec la carte Arduino

Le lecteur de carte SD utilise l’interface SPI matériel. Il faut  utiliser les mêmes pins  en fonction de votre carte Arduino. Le câblage est différent pour la carte Arduino Méga. Voir le tableau et les figures ci-dessous pour plus des détails.

Cablage SD avec Arduino

aRDUINO UNO Projet Data logger Gestion de la mémoire SD avec Arduino

La fonction setup()

void setup() 
{
// Init INT4
InitInt4(PinINT4);

// Initialisation du port série: Affichage locale des données
Serial.begin(9600);

// Initialisation du Module RTC (Heure de l'ordinateur)
InitRTC();

// Initialisation de la carte SD
InitSD(CS);

// Stockage des données dans le fichier (Fichier par défaut)
WriteSD(Data, DateHeure, N, NomFichier);

// Réouverture et Affichage du contenu du fichier (Fichier par défaut)
ReadDisFile(NomFichier);

}

La fonction loop():

Lecture de l’horloge RTC

void loop() 
{
//Lecture de l'horloge
GetDateHeure(DateHeure);
}

La fonction ISR():

Acquisition et stockage des données

ISR(INT4_vect) //ISR(_VECTOR(5))
{
// Compteur des échantillons
static long count=0;

// Désactivation de l'interruption globale
cli();

// Lecture du capteur de luminosité
R_l= GetData(A0);

// Mise à jour du buffer des échantillons
Data[count]=R_l;

// Mise à jour du compteur
count++; count=count%N;

// Mise à jour du nom du fichier
NomFichier= UpdateNomFile(TFile, DateHeure, NomFichier);


// Stockage des données dans la carte Mémoire
if(!count) //count=N: stockage lorsque le buffer est plein
{
// Stockage des données dans la carte SD
WriteSD(Data, DateHeure, N, NomFichier);

// Réouverture et Affichage du contenu du fichier (Pour la vérification)
Serial.println(NomFichier);
ReadDisFile(NomFichier);
}

// Réactivation de l'interruption globale
sei();
}

La fonction ReadDisFile()

Lecture et affichage du contenu d’un fichier dans la carte SD

void ReadDisFile(String NomFich)
{
// 1. Réouverture du fichier
myFile = SD.open(NomFich);

if (myFile)
{
// 2. Lecture & Affichage du contenu du fichier
while (myFile.available()) {
Serial.write(myFile.read());
}

// 3. Fermeture du fichier
myFile.close();
} else
{
Serial.println("Erreur d'ouverture du fichier "+ NomFich);
}
Serial.println("\n\n");
}

La fonction UpdateNomFile()

Génération périodique d’un nom du fichier en fonction de la date, l’heure et la période de création d’un nouveau fichier

String UpdateNomFile(byte TempsFile, word *Horloge, String OldName)
{
String NewNom="";
unsigned long T;
T=Horloge[0]+60*Horloge[1]+60*60*Horloge[2];


if(!(T%TempsFile)) // Changement du nom du fichier toutes les TempsFile
{
for(int i=3;i>=0;i--)
{
NewNom=NewNom+String(Horloge[i]);
}
NewNom="F"+NewNom+".txt";
}
else
NewNom=OldName;

return NewNom;
}

La fonction WriteSD()

Stockage d’un buffer de taille N dans le fichier courant avec la date et l’heure

void WriteSD(double *DataIn, word *Horloge, int Taille, String NomFich)
{
String StrDATA="";

// 1. Ouverture du fichier en écriture
File myFile = SD.open(NomFich, FILE_WRITE);

// 2-3. Conversion et Stockage des données dans la carte mémoire
if (myFile)
{
// Données
for(int i=0;i<Taille; i++)
{
// 2. Conversion des données en chaine de caractaire
StrDATA=String(DataIn[i])+",";

// 3. Stockage dans la carte SD
myFile.print(StrDATA);
}

// Date & Heure
for(int i=0;i<6; i++)
{
// 2. Conversion des données en chaîne de caractère
StrDATA=String(Horloge[i])+",";

// 3. Stockage dans la carte SD
myFile.print(StrDATA);
}

myFile.print("\n"); // Retour à la ligne

// 4. Fermeture du fichier
myFile.close();
}
else
{
Serial.println("Erreur d'ouverture du fichier "+NomFichier);
}
}

La fonction GetDateHeure()

Récupération de la date et l’heure dans le module RTC

void GetDateHeure(word *DateHeure)
{
// Lecture du module RTC
DateTime now = rtc.now();

// Récupération de la date
DateHeure[5]=now.year(); // Années
DateHeure[4]=now.month(); // Mois
DateHeure[3]=now.day(); // Jours

// Récupération de l'heure
DateHeure[2]=now.hour();
DateHeure[1]=now.minute();
DateHeure[0]=now.second();
}

La fonction InitRTC()

Initialisation du module RTC avec la génération d’un signal carré de 1Hz. Il sera utilisé pour trigger les acquisitions et le  stockage des données

void InitRTC(void)
{
if (! rtc.begin())
{
Serial.println("Le module RTC non disponible");
while (1); // Attente RESET
}
else
{
Serial.println("Le module RTC est OK");
rtc.writeSqwPinMode(DS1307_SquareWave1HZ);
//DS1307_SquareWave1HZ = 0x10, // 1Hz square wave
//DS1307_SquareWave4kHz = 0x11, // 4kHz square wave
//DS1307_SquareWave8kHz = 0x12, // 8kHz square wave
//DS1307_SquareWave32kHz = 0x13 // 32kHz square wave
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Date du PC

//rtc.adjust(DateTime(2020,1,24,10,10,10)); // Ajustement manuel
// Ex: 10 Janvier 2020 à 10:00:00:
// rtc.adjust(DateTime(2020, 1, 10, 10, 0, 0));
}
}

La fonction InitInt4()

Initialisation de l’interruption externe INT4 (Arduino Mega)

void InitInt4(byte PinINT)
{
// Entrée de synchronisation 1PPS / INT4
pinMode(PinINT, INPUT);

// Activation de l'interruption globale (registre SREG)
SREG|=0x80;

// Validation de l'interruption INT4 (Registre EIMSK)
EIMSK|=0x10; // INT4

// Choix du mode de détection: Front montant dans INT4
EICRB|=0x03;
}

La fonction InitSD()

Initialisation de la carte SD: Vérification de la connexion, affichage des informations de la carte, les fichiers disponibles dans la carte, test lecture et écriture dans la mémoire, etc.

void InitSD(byte ShipSel)
{
// Positionner à "1" le CS: Activation du module
pinMode(ShipSel, OUTPUT);
digitalWrite(ShipSel, HIGH);

// Initialisation
Serial.print("\n Initialisation de la carte SD...");

if (!card.init(SPI_HALF_SPEED, CS)) {
Serial.println("Initialisation échouée");
//return;
} else {
Serial.println("Le câblage est OK et la carte est disponible.");
}

Serial.print("\nType de la carte: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Non reconnue!");
}

if (!volume.init(card)) {
Serial.println("La partition FAT16/FAT32 non dispo.\n Assurez-vous bien de bien formater la carte");
return;
}

uint32_t volumesize;
Serial.print("\nVolume type is FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();

volumesize = volume.blocksPerCluster(); // clusters = somme des blocks
volumesize *= volume.clusterCount();
volumesize *= 512; // les blocs ont souvent la taille de 512 bytes
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);


Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);

root.ls(LS_R | LS_DATE | LS_SIZE);

if (!SD.begin(CS)) {
Serial.println("Initialisation échouée!");
while (1);
}
Serial.println("Initialisation réussie.");
}

La fonction GetData()

Lecture du flux de lumière (la résistance en fonction de la luminosité)

double GetData(byte Analog_pin)
{
word A0_val;
double V0, R_ll;

// Lecture du capteur
A0_val=analogRead(Analog_pin);

// Conversion en volt [0,1023]=>[0, 5V]
V0=(double)A0_val*V/1023.00;

// Calcul de la résistance en Ohm
R_ll=((V/V0)-1.0)*R;

// Renvoie de la valeur
return R_ll;
}

Le programme complet


/*

* Projet Data logger: Stockage des données dans la carte SD 3/4
* wwww.Electronique-Mixte.fr

Objectifs
1. Savoir utiliser le lecteur de la carte SD
2. Savoir lire / écrire les données dans la carte SD
3. Savoir ajuster la période de mise à jour du fichier
4. Savoir ajuster la période de création d’un nouveau fichier
5. Les erreurs fatales à éviter (Gagnez quelques heures ou jours du travail!)
6. Etc.
*/

#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
#define CS 10
#define V 5.00
#define R 10000.0
#define N 36 // 60*20=1200 - Taille SRAM
#define PinINT4 2 // PE4 - PIN 2
#define TFile 3600 // Création d'un nouveau fichier toutes

RTC_DS1307 rtc;
word DateHeure[6]={0,0,0,0,0,0};
double R_l=0.0, Data[N];


Sd2Card card;
SdVolume volume;
SdFile root;

File myFile;
String StrDATA="";
String NomFichier="F1234"; // Fichier Initial (A ne pas tenir en compte)

void setup()
{
// Init INT4
InitInt4(PinINT4);

// Initialisation du port série: Affichage locale des données
Serial.begin(9600);

// Initialisation du Module RTC (Heure de l'ordinateur)
InitRTC();

// Initialisation de la carte SD
InitSD(CS);

// Stockage des données dans le fichier (Fichier par défaut)
WriteSD(Data, DateHeure, N, NomFichier);

// Réouverture et Affichage du contenu du fichier (Fichier par défaut)
ReadDisFile(NomFichier);

}




void loop()
{
//Lecture de l'horloge
GetDateHeure(DateHeure);
}


ISR(INT4_vect) //ISR(_VECTOR(5))
{
// Compteur des échantillons
static long count=0;

// Désactivation de l'interruption globale
cli();

// Lecture du capteur de luminosité
R_l= GetData(A0);

// Mise à jour du buffer des échantillons
Data[count]=R_l;

// Mise à jour du compteur
count++; count=count%N;

// Mise à jour du nom du fichier
NomFichier= UpdateNomFile(TFile, DateHeure, NomFichier);


// Stockage des données dans la carte Mémoire
if(!count) //count=N: stockage lorsque le buffer est plein
{
// Stockage des données dans la carte SD
WriteSD(Data, DateHeure, N, NomFichier);

// Réouverture et Affichage du contenu du fichier (Pour la vérification)
Serial.println(NomFichier);
ReadDisFile(NomFichier);
}

// Réactivation de l'interruption globale
sei();
}

void ReadDisFile(String NomFich)
{
// 1. Réouverture du fichier
myFile = SD.open(NomFich);

if (myFile)
{
// 2. Lecture & Affichage du contenu du fichier
while (myFile.available()) {
Serial.write(myFile.read());
}

// 3. Fermeture du fichier
myFile.close();
} else
{
Serial.println("Erreur d'ouverture du fichier "+ NomFich);
}
Serial.println("\n\n");
}

String UpdateNomFile(byte TempsFile, word *Horloge, String OldName)
{
String NewNom="";
unsigned long T;
T=Horloge[0]+60*Horloge[1]+60*60*Horloge[2];


if(!(T%TempsFile)) // Changement du nom du fichier toutes les TempsFile
{
for(int i=3;i>=0;i--)
{
NewNom=NewNom+String(Horloge[i]);
}
NewNom="F"+NewNom+".txt";
}
else
NewNom=OldName;

return NewNom;
}
void WriteSD(double *DataIn, word *Horloge, int Taille, String NomFich)
{
String StrDATA="";

// 1. Ouverture du fichier en écriture
File myFile = SD.open(NomFich, FILE_WRITE);

// 2-3. Conversion et Stockage des données dans la carte mémoire
if (myFile)
{
// Données
for(int i=0;i<Taille; i++)
{
// 2. Conversion des données en chaine de caractaire
StrDATA=String(DataIn[i])+",";

// 3. Stockage dans la carte SD
myFile.print(StrDATA);
}

// Date & Heure
for(int i=0;i<6; i++)
{
// 2. Conversion des données en chaîne de caractère
StrDATA=String(Horloge[i])+",";

// 3. Stockage dans la carte SD
myFile.print(StrDATA);
}

myFile.print("\n"); // Retour à la ligne

// 4. Fermeture du fichier
myFile.close();
}
else
{
Serial.println("Erreur d'ouverture du fichier "+NomFichier);
}
}




void GetDateHeure(word *DateHeure)
{
// Lecture du module RTC
DateTime now = rtc.now();

// Récupération de la date
DateHeure[5]=now.year(); // Années
DateHeure[4]=now.month(); // Mois
DateHeure[3]=now.day(); // Jours

// Récupération de l'heure
DateHeure[2]=now.hour();
DateHeure[1]=now.minute();
DateHeure[0]=now.second();
}

void InitRTC(void)
{
if (! rtc.begin())
{
Serial.println("Le module RTC non disponible");
while (1); // Attente RESET
}
else
{
Serial.println("Le module RTC est OK");
rtc.writeSqwPinMode(DS1307_SquareWave1HZ);
//DS1307_SquareWave1HZ = 0x10, // 1Hz square wave
//DS1307_SquareWave4kHz = 0x11, // 4kHz square wave
//DS1307_SquareWave8kHz = 0x12, // 8kHz square wave
//DS1307_SquareWave32kHz = 0x13 // 32kHz square wave
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Date du PC

//rtc.adjust(DateTime(2020,1,24,10,10,10)); // Ajustement manuel
// Ex: 10 Janvier 2020 à 10:00:00:
// rtc.adjust(DateTime(2020, 1, 10, 10, 0, 0));
}
}


void InitInt4(byte PinINT)
{
// Entrée de synchronisation 1PPS / INT4
pinMode(PinINT, INPUT);

// Activation de l'interruption globale (registre SREG)
SREG|=0x80;

// Validation de l'interruption INT4 (Registre EIMSK)
EIMSK|=0x10; // INT4

// Choix du mode de détection: Front montant dans INT4
EICRB|=0x03;
}


void InitSD(byte ShipSel)
{
// Positionner à "1" le CS: Activation du module
pinMode(ShipSel, OUTPUT);
digitalWrite(ShipSel, HIGH);

// Initialisation
Serial.print("\n Initialisation de la carte SD...");

if (!card.init(SPI_HALF_SPEED, CS)) {
Serial.println("Initialisation échouée");
//return;
} else {
Serial.println("Le câblage est OK et la carte est disponible.");
}

Serial.print("\nType de la carte: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Non reconnue!");
}

if (!volume.init(card)) {
Serial.println("La partition FAT16/FAT32 non dispo.\n Assurez-vous bien de bien formater la carte");
return;
}

uint32_t volumesize;
Serial.print("\nVolume type is FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();

volumesize = volume.blocksPerCluster(); // clusters = somme des blocks
volumesize *= volume.clusterCount();
volumesize *= 512; // les blocs ont souvent la taille de 512 bytes
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);


Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);

root.ls(LS_R | LS_DATE | LS_SIZE);

if (!SD.begin(CS)) {
Serial.println("Initialisation échouée!");
while (1);
}
Serial.println("Initialisation réussie.");
}

double GetData(byte Analog_pin)
{
word A0_val;
double V0, R_ll;

// Lecture du capteur
A0_val=analogRead(Analog_pin);

// Conversion en volt [0,1023]=>[0, 5V]
V0=(double)A0_val*V/1023.00;

// Calcul de la résistance en Ohm
R_ll=((V/V0)-1.0)*R;

// Renvoie de la valeur
return R_ll;
}

Ressources PDF

Accueil des projets avec Arduino

Laisser un commentaire