• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_certifying

Got it!

Last updated on 7/13/17

Découvrez le shield ethernet et le lecteur de carte SD

Log in or subscribe for free to enjoy all this course has to offer!

Cette partie a pour objectif de vous permettre de connecter votre carte Arduino sur le réseau : d'abord votre réseau domestique (intranet), puis le réseau mondial (internet).

Ceci n'est possible que si vous avez le shield ethernet qui permet à l'Arduino de communiquer avec votre réseau local ou avec le réseau mondial.

Vous aborderez ensuite quelques mots de vocabulaire liés au réseau.

Puis nous apprendrons à utiliser la communication avec la carte mémoire qui est proposée avec ce shield. Vous apprendrez donc à utiliser la bibliothèque d'utilisation d'une carte SD.

Vous construirez enfin un programme pour stocker des informations sur une carte.

Je commence par l'utilisation de la carte SD, tout simplement parce que l'Arduino va très vite être dépassé en mémoire pour la communication Internet. Il est donc important de maîtriser cette possibilité de stockage avant d'aller plus loin.

La connexion au réseau local puis mondial sera abordée dans les chapitres suivants.

Le shield Ethernet

Vous avez déjà eu l'occasion d'utiliser le shield moteur. Et bien le principe du shield internet est le même !

C'est une carte qui vient s'enficher (se connecter sur toutes les bornes de l'Arduino) au dessus de l'Arduino pour lui permettre d’utiliser ses fonctionnalités.

Le shield que je vous propose d'utiliser est le shield Ethernet Arduino.

Arduino shield ethernet vu de dessus (à gauche) et de dessous (à droite)
Le shield Ethernet Arduino  vu de dessus (à gauche) et de dessous (à droite)

Vous pouvez l'acquérir dans un magasin spécialisé d'électronique, sur le site d'Arduino ou sur les sites de vente en ligne cités en début de ce cours. J'ai acquis le mien sur Amazon (les deux modèles, avec ou sans POE, sont utilisables), comme quoi...

Son prix peut varier entre 25€ et 30€ en fonction des distributeurs, auxquels il faut parfois ajouter des frais d'envoi.

Ce shield, outre le fait de venir se connecter sur l'Arduino, vous propose un lecteur de carte SD. Nous allons donc pouvoir étudier à la fois la programmation du shield côté réseau, et la programmation du stockage d'information sur la carte SD.

Il existe un autre shield Ethernet qui permet à l'Arduino de s'alimenter directement par le câble réseau (si le réseau est prévu pour) : l'Arduino Ethernet shield POE.

Arduino shield Ethernet avec POE (http://www.digikey.com)
Arduino shield Ethernet avec POE (http://www.digikey.com)

Il n'y a aucune différence sauf pour le principe d'alimentation (qui utilise la prise de connexion au réseau pour alimenter l'Arduino). Tous les réseaux ne proposent pas une alimentation en même temps que la transmission de données. Donc ce second shield n'est pas du tout une obligation.

Comment fonctionne un shield Internet ?

Le shield est basé sur une puce  Wiznet W5100, qui permet de gérer les échanges de données avec le réseau. Elle contient 16 Ko de mémoire interne et permet jusqu'à 4 connexions simultanées (UDP et TCP).

Elle communique avec l'Arduino grâce au bus SPI. 

Pour pouvoir communiquer avec la carte Arduino, la puce Wiznet utilise les pins 10, 11, 12 et 13 de l’Arduino.

 

La prise RJ45 

Ce qu'on appelle la prise RJ45 n'est autre que la prise qui vous permet de connecter un appareil sur le réseau.

La prise RJ45 (http://www.lextronic.fr)
La prise RJ45 (http://www.lextronic.fr)

Elle comporte 8 contacts. Elle peut être montée en mode croisé ou droit. Pour utiliser le shield Internet, je vous conseille un câble monté en mode droit.

UDP et TCP 

L'un comme l'autre sont des protocoles, c'est-à-dire des règles pour communiquer. En effet, s'il n'y a pas de règles qui expliquent comment envoyer et recevoir l'information, cette information ne nous parviendrait pas correctement. Les règles d'échange comprennent souvent les codes de début de communication, de type d'information échangée, de mode d'échange, de codage de l'information et de fin de communication.

(Lukas, vous pourriez au moins faire semblant de suivre, même si tout ceci vous paraît... pesant.)

Donc en plus de toute communication sur Internet, il faut savoir qu'on doit envoyer avant (parfois pendant) et après l'échange, d'autres informations que le seul message prévu. C'est ce qui est appelé l'encapsulation des données.

Voyons un peu la différence entre ces deux protocoles : 

Le protocole UDP

Prenons un émetteur d'information (ordinateur, Arduino...) et un récepteur (ordinateur, Arduino...). Avec le protocole UDP,  l'émetteur émet une information sans prévenir le récepteur et sans vérifier qu’il a bien reçu cette information. Tout simplement parce que la méthode d'encapsulation ne contient pas d'information sur l'émetteur (sauf son IP). Il ne peut donc pas lui répondre. C'est un peu comme envoyer une carte postale : vous mettez l'adresse du destinataire, il reçoit votre courrier, en lit les informations, mais ne peut pas vous répondre (on sous-entend que vous n’avez pas donné vos coordonnées, ni même signé).

Le protocole TCP

Pour communiquer avec ce protocole, il faut que se crée une liaison fixe entre l'émetteur et le récepteur. Cette liaison permet de communiquer des informations dans les deux sens tant qu'elle est active. C'est un peu comme le téléphone. Vous appelez (numéro du destinataire) et vous échangez des informations tant que la communication est active (aucun ne raccroche). 

Avec protocole TCP, le récepteur peut vérifier si les données qu’il a reçues correspondent à celles que l’émetteur lui a envoyées grâce à une équation mathématique (un peu comme nous l'avons vu avec les échanges avec le moniteur série). Si ce n'est pas le cas, il peut demander à l'émetteur de procéder à nouveau à l'envoi.

De nombreux cas d’échanges de données passent par un protocole de type TCP comme :

  • le protocole HTTP, qui permet d'accéder aux sites Internet (le réseau mondial) ;

  • le protocole FTP, qui permet d'échanger des fichiers entre 2 ordinateurs ;

  • les protocoles POP3 et IMAP qui permettent de lire ses emails ;

  • le protocole SMTP qui permet d'envoyer des emails.

Comme le shield Arduino travaille en TCP comme en UDP, vous imaginez bien que tout ceci est donc possible avec l'Arduino.

L'IP

On entend souvent parler d'IP, ou d'adresse IP. Mais qu'est-ce donc finalement ?

Déjà IP veut dire Internet Protocol, donc protocole Internet. (Lukas j'apprécie votre réponse, mais je vous ai vu, c'est Cunégonde qui vous l'a soufflée !) Le protocole Internet est en fait un stock de règles de communication entre deux machines fabriqué pour être utilisé sur un réseau.

L'adresse IP est donc une adresse créée pour être comprise et repérée par ce protocole Internet.

C'est un code numérique qui est donné (de façon permanente ou provisoire) à un appareil qui se connecte sur un réseau. Grâce à ce code, on peut lui envoyer des données directement, et l'appareil peut lui aussi en envoyer en étant repéré et repérable (pour recevoir une réponse éventuelle).

Les adresses IP locales 

On parle d'adresse locale, car elle est valable uniquement sur votre réseau interne (entreprise, maison...) et ne peut donc pas servir pour communiquer avec le réseau mondial directement. En revanche, tout ordinateur ou machine (imprimante, lecteur média...) connecté sur votre réseau interne est joignable par son adresse IP. Donc deux ordinateurs (ou Arduino/ordinateurs) connectés sur le même réseau interne, vont pouvoir échanger des données.

Bien sûr, les réseaux, même internes, sont organisés pour que l'accès à chaque machine soit sécurisé. Chaque machine fait partie d'un groupe (où l'accès entre les machines du groupe est facilité). La connexion entre chaque groupe est filtrée (cloisonnement) pour éviter l'accès à toutes les informations par tout le monde.

En revanche, lorsqu'il s'agit d'un réseau domestique (chez vous quoi), vous êtes normalement le maître à bord, et il vous est facile de réaliser les communications entre chaque machine.

Les adresses IP mondiales 

Ce sont les fournisseurs d’accès au réseau mondial qui donnent ces adresses IP accessibles par tout le monde sur le réseau mondial. Remarquez, c'est plutôt normal, imaginez sinon le bazar planétaire que ce serait.

Si vous avez Internet chez vous, c'est que vous avez une adresse IP mondiale qui vous est propre. Souvent il s'agit de celle de votre box, que vous fournit votre prestataire téléphonique.

Un ordinateur de votre réseau local n'est pas directement accessible par le réseau mondial. Il faut faire quelques manipulations (pas toujours simples) dont nous parlerons aux chapitres suivants. En revanche, le réseau mondial peut accéder directement à votre box, il suffit d'en connaître l’adresse IP publique bien sûr ;).

 

Bien, je souhaitais faire un survol de ces notions de protocoles et adresses IP pour que nous soyons plus à l'aise pour nous comprendre par la suite. La gestion et la compréhension du réseau est une partie importante de la technologie informatique. Je parle donc de survol, car les concepts et les savoirs en jeu sont bien plus complexes que mes quelques lignes. 

(Lukas, vous pouvez passer à l'infirmerie pour demander un cachet contre les maux de tête. Je vous remercie pour votre effort d'attention...)

Bien, comme prévu, avant de voir la connexion à un réseau, nous allons nous attaquer à l'utilisation de la carte SD qui est comprise avec le shield Ethernet !

Le lecteur de carte micro-SD

Votre chère petite carte Arduino, malgré toutes ses compétences, souffre cruellement de mémoire. De plus, lorsqu'on l'éteint, toute information stockée est perdue.

Lorsque vous utiliserez votre Arduino sur le réseau, vous verrez vite que la place mémoire vous fera défaut !

C'est la raison pour laquelle les constructeurs du shield Ethernet on décidé de lui adjoindre un lecteur de carte mini-SD. Et bien sûr, comme à chaque fois,  ils ont aussi créé une bibliothèque associée pour en faciliter l'accès : la bibliothèque SD !

Il vous faudra tout d'abord vous procurer une carte mémoire de type micro-SD (256 mégas suffisent) :

 

Carte micro SD 256 MB avec adaptateur
Carte micro SD 256 MB avec adaptateur

L'adaptateur permet de connecter votre carte sur un ordinateur (on met la mini-SD dans l'adaptateur, et l’adaptateur au niveau du lecteur de cartes de l’ordinateur).

Il vous faut ensuite la bibliothèque SD. Elle est incluse par défaut dans l'IDE donc pas de souci.

Comme je l'indique plus haut, la connexion entre le lecteur de carte SD et l'Arduino se fait en mode SPI, donc on utilise 4 pins pour communiquer :

  • Pin 11 : MOSI ;

  • Pin 12 : MISO ;

  • Pin 13 : CLK ;

  • Pin 4 : SS (ou CS c'est pareil).

Bien, je vous laisse connecter le shield sur votre Arduino, mettre une carte micro-SD au bon endroit, connecter l'Arduino sur votre ordinateur (inutile pour le moment de vous connecter sur le réseau), et peut-être grignoter un petit quelque chose...

 Programmez la communication entre l’Arduino et la carte micro-SD

Vous pouvez voir rapidement des exemples d'utilisation de la carte avec ceux fournis par l'IDE. Il vous suffit d'aller dans Fichiers/Exemples/SD dans les menus.

Le but ici est de bien comprendre le fonctionnement de cette carte micro-SD avec l'Arduino et de partir du début.

Tout d'abord, il faut inclure à votre programme deux bibliothèques : celle pour la carte et celle pour la communication SPI...

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

 

Contrairement à d'autres bibliothèques, on ne crée pas un objet de type  SD. On va directement utiliser l'objet  SD.

Notre premier programme va consister à écrire toutes les secondes un nombre aléatoire sur la carte, et ce pendant 10 secondes. Puis il devra nous afficher le tableau résultant sur le moniteur série (en récupérant les données de la carte).

Ces données vont être stockées dans un fichier. Pour manipuler facilement les fichiers, nous devons créer un objet de type File. Il gardera le chemin de fichier correspondant tout le temps de son utilisation.

(Une idée de nom ? Oui Lukas  ? Ça me va !)

 

Nous l'appellerons donc "monFichier".

(ha ben non Lukas, monFichier, c'est le vôtre non ? Pas le mien ! Donc ne mettez pas comme nom "votreFichier", mais bien "monFichier", car ce n'est pas mon fichier, mais bien votre fichier. Vous comprenez ? Ça me rappelle un certain dîner tiens...)

 

Écrivez dans un fichier

#include <SPI.h>
#include <SD.h>
File monFichier;

L'objet  monFichier est vide pour le moment, il est juste déclaré en mémoire.

Pour stocker des données de type chiffrées, le plus simple est un fichier texte avec l'extension .txt.

Mais comme nous sommes joueurs, nous n'allons pas utiliser une extension connue, mais une fabriquée, pour l'occasion. Je propose que notre fichier sur la carte SD s'appelle : aleatoire.ard.

Mais avant, il faut débuter la connexion entre la carte SD et l'Arduino.

Dans le  setup(), nous allons donc initialiser la communication avec la carte SD. On utilise le mot-clé  begin() que l'on connaît déjà avec le moniteur série. On va en profiter pour initialiser aussi la communication avec le moniteur :

#include <SPI.h>
#include <SD.h>
File monFichier;

void setup(){
    Serial.begin(9600); //début de communication avec le moniteur série
    SD.begin(4); //début de la communication avec la carte sur le pin 4
}

Le chiffre 4 correspond au pin utilisé par le shield Ethernet pour la carte SD.

Le pin 10 sera utilisé par le shield Ethernet par la suite.

Il est de bon aloi de tester si la connexion est bien établie avec la carte. Un petit  if ne fait donc pas de mal. C'est une bonne habitude à prendre pour éviter d'exécuter du code alors que la communication n'est pas établie.

#include <SPI.h>
#include <SD.h>
File monFichier;

void setup(){
    Serial.begin(9600); //débute la communication avec le moniteur série
    Serial.println("*************\nInitialisation...");
    if (!SD.begin(4)){//teste la communication avec la carte(pin 4)
        Serial.println("Communication impossible");
        return; //stoppe le programme
    }; 
    Serial.println("Communication ok !");
}

void loop() {
}

Vous pouvez déjà tester ce programme ! (J'ai ajouté la boucle de  loop())

Si votre moniteur affiche que la communication est ok, vous pouvez continuer !

Comme nous allons utiliser les nombres aléatoires, nous devons initialiser une graine pour éviter que les nombres soient trop prévisibles (voir le cours d'initiation sur le dé numérique) :

randomSeed(analogRead(A0));

Et nous allons ouvrir notre fichier de la carte SD et en stocker le lien dans l'objet monFichier :

monFichier = SD.open("aleatoire.ard",FILE_WRITE);

Vous remarquez que la fonction  SD.open() prend deux arguments :

  • le nom du fichier sur la carte SD, avec son extension (le tout entre guillemets) ;

  • une option, ici FILE_WRITE, qui permet de placer le fichier en lecture/écriture, avec le curseur en fin de fichier.

Mais le fichier existe déjà ?

Et bien dans notre cas, pas encore. La fonction  SD.open() crée le fichier s'il n'existe pas.

Là encore un petit test de réussite est le bienvenu. Si le fichier existe ou est créé, la fonction retourne un objet de type  File (que l'on va stocker dans notre objet  monFichier), sinon elle renvoie false (ou 0).

 Avant d’aller plus loin, fermons notre fichier. Car si on l’a ouvert, il va bien falloir le fermer. Je place souvent ces deux commandes ensemble et je remplis ensuite entre les deux avec le code de traitement de fichier. Ça évite d'oublier la fermeture qui peut parfois créer des problèmes de sauvegarde. On ferme un fichier avec :

monFichier.close();

Voici donc le code jusqu'à présent :

#include <SPI.h>
#include <SD.h>
File monFichier;

void setup(){
    Serial.begin(9600); //débute la communication avec le moniteur série
    Serial.println("*************\nInitialisation...");
    if (!SD.begin(4)){//teste la communication avec la carte(pin 4)
        Serial.println("Communication impossible");
        return; //stoppe le programme
    }; 
    Serial.println("Communication ok !");
    randomSeed(analogRead(A0)); //initialise nombre aléatoire
    Serial.println("Ouverture du fichier");
    if (!(monFichier = SD.open("aleatoire.ard",FILE_WRITE))){
      Serial.println("Erreur de fichier");
      return; //stoppe le programme
    }
    Serial.println("Fichier ouvert");
    monFichier.close();
    Serial.println("Fichier clos");
}

void loop() {
}

Si vous avez tout fait bien comme il faut... vous devriez avoir une erreur, c'est-à-dire que le moniteur vous indiquera "Erreur de fichier", car votre fichier ne s'ouvrira pas !

En effet, le nom de fichier est trop long ! C'est une habitude que nous avons perdue depuis longtemps, mais à une époque, il fallait faire attention à ce que les noms de fichiers ne dépassent pas 8 caractères avant l'extension. C'est le problème ici : le mot "aleatoire" contient 9 caractères !

Changez donc le nom de fichier en "alea.ard", et là ça devrait fonctionner !

Ce petit exercice vous a montré l'intérêt de placer des affichages moniteur à chaque étape de votre programme, c'est très pratique pour débugger !

Si les messages vous gênent ensuite, il existe un moyen simple de gérer leur affichage : vous créez une fonction d'affichage des message de debug, et en début de programme, une variable qui indique si on les affiche ou non. Voici notre programme corrigé avec cette option en plus :

#include <SPI.h>
#include <SD.h>
File monFichier;
boolean messageOK=0;

void setup(){
    Serial.begin(9600); //débute la communication avec le moniteur série
    Serial.println("*********\nNombres hasardeux\n*********");
    message("Initialisation");
    if (!SD.begin(4)){//teste la communication avec la carte(pin 4)
        message("Communication impossible");
        return; //stoppe le programme
    }; 
    message("Communication ok !");
    randomSeed(analogRead(A0)); //initialise nombre aléatoire
    message("Ouverture du fichier");
    if (!(monFichier = SD.open("alea.ard",FILE_WRITE))){
      message("Erreur de fichier");
      return; //stoppe le programme
    }
    message("Fichier ouvert");
    monFichier.close();
    message("Fichier clos");
}

void loop() {
}
void message(String s){
  if (messageOK){
    Serial.println(s);
  }
}

Si vous passez la variable  messageOK à 0, vous n'aurez plus que les affichages de présentation, sans pour autant modifier votre code.

Bien, la suite, vous allez le voir est plutôt simple. On fait une boucle de 0 à 9 qui pond un nombre aléatoire, on l'écrit dans le fichier à chaque tour de boucle, et on ferme le fichier.

Pour écrire dans un fichier, on utilise la fonction  write(),  print() ou  println() précédés du nom de l'objet  File et d'un point :

  • write() : écrit des données de type byte ou char ou chaîne de caractères.

  • print() : écrit des données de type char, byte, int, long ou string. Un nombre sera décomposé en caractères. Par exemple 314 s'écrira en trois caractères : '3', '1' et '4'. Ce ne sera donc plus un nombre mais une chaîne de caractères. On peut préciser le mode d'envoi du nombre (DEC, HEX, OCT, BIN).

  • println() : agit comme print, mais ajoute au bout le caractère de saut de ligne (\n ou 10) et le retour chariot (\r ou 13).

Nous allons donc utiliser la fonction  print() pour écrire nos nombres aléatoires.

Voici le programme :

#include <SPI.h>
#include <SD.h>
File monFichier;
boolean messageOK=1;

void setup(){
    Serial.begin(9600); //débute la communication avec le moniteur série
    Serial.println("*********\nNombres hasardeux\n*********");
    message("Initialisation");
    if (!SD.begin(4)){//teste la communication avec la carte(pin 4)
        message("Communication impossible");
        return; //stoppe le programme
    }; 
    message("Communication ok !");
    randomSeed(analogRead(A0)); //initialise nombre aléatoire
    message("Ouverture du fichier");
    if (!(monFichier = SD.open("alea.ard",FILE_WRITE))){ //tente d'ouvrir le fichier
      message("Erreur de fichier");
      return; //stoppe le programme
    }
    message("Fichier ouvert");
    for (int n=0;n<10;n++){ // boucle d'écriture
      int nAlea=random(0,10000); //tirage d'un nombre entre 0 et 10000
      message("Ecriture de "+String(nAlea));
      monFichier.print(nAlea); //écriture dans le fichier
    }
    monFichier.close();
    message("Fichier clos");
}

void loop() {
}
void message(String s){
  if (messageOK){
    Serial.println(s);
  }
}

Alors si vous éteignez votre Arduino et éjectez la carte pour la lire sur votre ordinateur (utilisez un éditeur de texte du genre Bloc-notes), vous allez voir une succession de chiffres collés. Impossible de les différencier !

C'est tout le problème des écritures dans un fichier. Il faut donc prévoir un moyen de coder notre information (voir le chapitre sur la communication avec le moniteur série), pour qu'elle soit lisible par la suite.

Nous pourrions utiliser la fonction  println() qui ajoute des sauts de ligne. Mais pour l'exemple, nous allons procéder différemment.

Mais avant cela, si vous avez essayé plusieurs fois le programme, vous avez sûrement vu que le fichier écrit de nouvelles données à chaque fois et que notre fichier grossit d'autant plus à chaque passage. C'est pratique lorsque l'on veut stocker des données qui s'accumulent. Dans notre cas, nous voulons seulement 10 tirages. Il nous faut donc effacer le contenu du fichier à chaque démarrage. Le plus simple est de détruire le fichier s'il existe, avant d'écrire dessus.

Nous allons donc utiliser deux commandes :  SD.exists() et  SD.remove().

Et pour que nos données soient repérables, nous allons ajouter un ":" après chaque nombre.

Voici donc le programme complet qui permet d’écrire 10 nombres aléatoires dans un fichier vide en les séparant par des “:”  :

#include <SPI.h>
#include <SD.h>
File monFichier;
boolean messageOK=1;

void setup(){
    Serial.begin(9600); //débute la communication avec le moniteur série
    Serial.println("*********\nNombres hasardeux\n*********");
    message("Initialisation");
    if (!SD.begin(4)){//teste la communication avec la carte(pin 4)
        message("Communication impossible");
        return; //stoppe le programme
    }; 
    message("Communication ok !");
    message("Teste si fichier existant");
    if (SD.exists("alea.ard")){
      message("Destruction fichier");
      SD.remove("alea.ard");
    }
    randomSeed(analogRead(A0)); //initialise nombre aléatoire
    message("Ouverture du fichier");
    if (!(monFichier = SD.open("alea.ard",FILE_WRITE))){ //tente d'ouvrir le fichier
      message("Erreur de fichier");
      return; //stoppe le programme
    }
    message("Fichier ouvert");
    for (int n=0;n<10;n++){ // boucle d'écriture
      int nAlea=random(0,10000); //tirage du nombre entre 0 et 10000
      message("Ecriture de "+String(nAlea));
      monFichier.print(nAlea); //écriture dans le fichier
      monFichier.write(':');
    }
    monFichier.close();
    message("Fichier clos");
}

void loop() {
}
void message(String s){
  if (messageOK){
    Serial.println(s);
  }
}

Bon je vous le donne aussi en mode épuré, sans les tests d'erreur. Je vous déconseille de faire des programmes sans tests de débuggage, mais je le fais ici juste pour que vous puissiez bien repérer chaque étape du programme.

#include <SPI.h>
#include <SD.h>
File monFichier;

void setup(){
    Serial.begin(9600); //débute de la communication avec le moniteur série
    SD.begin(4)//communication avec la carte(pin 4)
    if (SD.exists("alea.ard")){
      SD.remove("alea.ard");
    }
    randomSeed(analogRead(A0)); //initialisation nombre aléatoire
    monFichier = SD.open("alea.ard",FILE_WRITE)//ouverture du fichier
    for (int n=0;n<10;n++){ // boucle d'écriture
      int nAlea=random(0,10000); //tirage du nombre entre 0 et 10000
      monFichier.print(nAlea); //écriture dans le fichier
    }
    monFichier.close();
}

void loop() {
}

 

Bien, passons à la lecture maintenant !

Lisez un fichier

Et bien c'est un peu comme la communication avec le moniteur. On peut charger l'ensemble du fichier et l'afficher. Mais l'avantage surtout, c'est qu'on peut se déplacer dans le fichier caractère par caractère ! Ce qui est bien pratique pour éviter de surcharger la mémoire de l'Arduino. Nous n'allons donc lire dans notre cas que ce qui nous intéresse.

Voilà comment nous allons procéder :

  • On ouvre le fichier en lecture avec le curseur au début ;

  • On prépare un tableau de caractères (assez grand pour recevoir nos données) ;

  • On lit les caractères un par un en les ajoutant au tableau de caractères, jusqu'à rencontrer les ":" de séparation ;

  • On affiche notre tableau de caractère (en ajoutant le '\0' qui permet de finir la chaîne ;

  • On recommence la lecture ainsi jusqu'à la fin du fichier.

Pour lire, nous utiliserons la fonction  read() (étonnant non ?) qui en plus déplace automatiquement le curseur de 1 caractère dans le fichier.

Voici le programme final :

#include <SPI.h>
#include <SD.h>
File monFichier;
boolean messageOK = 1;

void setup() {
  Serial.begin(9600); //débute la communication avec le moniteur série
  Serial.println("*********\nNombres hasardeux\n*********");
  message("Initialisation");
  if (!SD.begin(4)) { //teste la communication avec la carte(pin 4)
    message("Communication impossible");
    return; //stoppe le programme
  };
  message("Communication ok !");
  message("Teste si fichier existant");
  if (SD.exists("alea.ard")) {
    message("Destruction fichier");
    SD.remove("alea.ard");
  }
  randomSeed(analogRead(A0)); //initialise nombre aléatoire
  message("Ouverture du fichier");
  if (!(monFichier = SD.open("alea.ard", FILE_WRITE))) { //tente d'ouvrir le fichier
    message("Erreur de fichier");
    return; //stoppe le programme
  }
  message("Fichier ouvert");
  
  //phase d'écriture
  for (int n = 0; n < 10; n++) { // boucle d'écriture
    int nAlea = random(0, 10000); //tirage du nombre entre 0 et 10000
    message("Ecriture de " + String(nAlea));
    monFichier.print(nAlea); //écriture dans le fichier
    monFichier.write(':');
  }
  monFichier.close();
  message("Fichier clos");
  
  //phase de lecture
  message("Ouverture du fichier en lecture");
  if (!(monFichier = SD.open("alea.ard", FILE_READ))) { //tente d'ouvrir le fichier
    message("Erreur de fichier");
    return; //stoppe le programme
  }
  message("Fichier ouvert");
  char c = 0; //variable de lecture
  int pos = 0; //position dans la chaîne de caractère
  char tab[6] = {0}; //tableau de la chaîne de caractère
  while (c != -1) {
    c = monFichier.read(); //on lit un caractère
    if (c == ':') { //si : on affiche
      tab[pos] = '\0'; //ajout du caractère de fin de chaîne
      Serial.println(tab); //affichage sur le moniteur
      pos = 0; //remise à zéro de la position
    }
    else { // sinon
      tab[pos] = c; //on ajoute le caractère à la chaîne
      pos++; //on incrémente le curseur dans la chaîne
    }
  }
  monFichier.close(); //on ferme le fichier
  message("Fichier clos");
}

void loop() {
}
void message(String s) {
  if (messageOK) {
    Serial.println(s);
  }
}

Les commentaires et les explications précédentes devraient vous suffire pour comprendre le programme.

J'attire votre attention sur l'ouverture en mode lecture avec la constante  FILE_READ.

Bien, que diriez-vous d'un petit exercice ?

TP : Liste de courses

Je vous propose de réaliser un programme qui va gérer une liste de courses avec quelques fonctionnalités :

  • La liste est saisie à l'aide du moniteur série ;

  • Si on saisit un élément, il s'ajoute à la liste ;

  • Si on tape "liste", la liste s'affiche ;

  • Si on tape "efface", la liste est détruite ;

  • Si on tape "compte", le moniteur affiche le nombre d'éléments de la liste ;

  • Le programme devra prévoir un mode débuggage activé avec la commande "bugON" et désactivé avec "bugOFF".

Vous stockerez la liste sur la carte SD dans un fichier "liste.ard".

Il n'y a rien de bien compliqué puisque vous avez tout en main si vous relisez le chapitre sur le moniteur série et ce chapitre-ci.

Je vous laisse donc chercher (et surtout trouver !) par vous-mêmes !

TP : Correction

Bon j'espère que ça n'a pas été trop difficile. L'important quoiqu'il arrive est de comprendre la correction qui suit. ;)

#include <SPI.h>
#include <SD.h>
File monFichier;
boolean messageOK = 1;

void setup() {
  Serial.begin(9600); //débute la communication avec le moniteur série
  Serial.println("*********\nListe de course\n*********");
  bug(1); //appelle la fonction bug
  initialisation(); //appelle la fonction d'initialisation
}

void loop() {
  if (Serial.available()) { //teste s'il y a une saisie en attente
    analyse(); //lance l'analyse de la saisie
  }
}

//---------------------------* fonctions* ----------------------

//fonction d'initialisation de la communication avec la carte SD
void initialisation() {
  message("Initialisation");
  if (!SD.begin(4)) { //teste la communication avec la carte(pin 4)
    message("Communication impossible");
    return;
  };
  message("Communication ok !");
}

//fonction de modification de l'affichage des messages
void bug(boolean b) {
  messageOK = b;
  if (messageOK) {
    Serial.println("Mode bug ON");
    return;
  }
  Serial.println("Mode bug OFF");
}

//fonction d'analyse de la saisie
void analyse() {
  String chaine = ""; // création d'un String vide
  //lecture de la saisie
  while (Serial.available()) { //tant que caractères en attente.
    delay(10); //petit délai de lecture
    char c = Serial.read(); //on lit le message
    //empêche la saisie d'un #
    if (c == '#') {
      Serial.println("<!> ne pas saisir de #, merci");
      chaine = ""; //on vide la chaine
      while (Serial.available()) //on vide la saisie
        Serial.read();
      return; //on retourne au programme
    }
    if (c != 10 && c != 13) { //nettoyage de la chaine
      chaine += c; //on ajoute le caractère
    }
  }
  //test de la saisie
  if (chaine == "liste") { //si liste demandée
    lister();
    return;
  }
  else if (chaine == "efface") { //si effacement demandé
    effacer();
    return;
  }
  else if (chaine == "compte") { // si comptage demandé
    compter();
    return;
  }
  else if (chaine == "bugON") { // si affichage des message demandé
    bug(1);
    return;
  }
  else if (chaine == "bugOFF") { // si non-affichage des messages demandé
    bug(0);
    return;
  }
  //si aucun code spécial
  ajouter(chaine); // ajout d'un item

}

//fonction d'affichage de la liste
void lister() {
  message("lister");
  if (ouvrir(0)) { //appelle la fonction pour ouvrir
    Serial.println("* liste *");
    char c = 0; //variable de lecture
    int pos = 2; //position dans la chaîne de caractère
    char tab[102] = {0}; //tableau de la chaîne de caractère
    tab[0] = '-'; //mise en page
    tab[1] = ' '; //avec un tiret
    while (c != -1) {
      delay(10);
      c = monFichier.read(); //on lit un caractère
      if (c == '#') { //si # on affiche
        tab[pos] = '\0'; //ajout du caractère de fin de chaîne
        Serial.println(tab); //affichage sur le moniteur
        pos = 2; //remise à zéro de la position
      }
      else { // sinon
        tab[pos] = c; //on ajoute le caractère à la chaîne
        pos++; //on incrémente le curseur dans la chaîne
      }
    }
    monFichier.close(); //on ferme le fichier
    message("Fichier clos");
    return;
  }
}

//fonction d'effacement de la liste
void effacer() {
  message("Effacement de la liste");
  message("Teste si fichier existant");
  if (SD.exists("liste.ard")) {
    message("Destruction fichier");
    SD.remove("liste.ard");
    Serial.println("Effacement OK");
    return;
  }
  message("Fichier inexistant");
}

//fonction de comptage des items
void compter() {
  message("compter");
  if (ouvrir(0)) {
    Serial.print("La liste comporte ");
    int nb = 0;
    char c;
    while (c != -1) {
      c = monFichier.read(); //on lit un caractère
      if (c == '#') {
        nb++;
      }
    }
    Serial.print (nb);
    Serial.print (" item");
    if (nb > 1)
      Serial.print("s");
    Serial.println();
    monFichier.close(); //on ferme le fichier
    message("Fichier clos");
    return;
  }
}

//fonction d'ajout d'un Item
void ajouter(String ch) {
  message ("Ajout de <" + ch + ">");
  if (ouvrir(1)) {
    ch.trim();
    monFichier.print(ch); //écriture dans le fichier
    monFichier.write('#');
    monFichier.close();
    message("Fichier clos");
    Serial.println("Ajout de <" + ch + "> fait");
    return;
  }
}

//fonction d'ouverture de fichier
boolean ouvrir(int mode) {
  message("Ouverture du fichier");
  if (mode)
    monFichier = SD.open("liste.ard", FILE_WRITE);
  else
    monFichier = SD.open("liste.ard", FILE_READ);
  if (!(monFichier)) { //tentative d'ouverture du fichier
    message("Erreur de fichier");
    return 0;
  }
  message("Fichier ouvert");
  return 1;
}

//fonction d'affichage des messages
void message(String s) {
  if (messageOK) {
    Serial.println("\t>"+s);
  }
}

Le programme est conséquent, comme souvent lorsqu'on communique entre plusieurs médias (ici le moniteur série et la carte SD). L'écriture et la fermeture des fichiers nécessite toujours quelques lignes, d'autant qu'ici on a ajouté les lignes de messages.

Vous noterez que j'ai ajouté une fonction pour ouvrir les fichiers. Elle sert à valider l'ouverture sans avoir chaque fois à le répéter dans les codes.

Le programme est conçu de telle sorte que l'on peut, dans la  loop() ajouter d'autres fonctionnalités qui ne sont pas en rapport avec la réception série.

Vous allez me dire, mais tout ceci est-il indispensable ?

Et je vous répondrai : oui. Il faut maîtriser ce genre d'échange pour être à l'aise avec la communication sur le réseau.

Tiens d'ailleurs, si nous passions au chapitre suivant pour en parler ?

Example of certificate of achievement
Example of certificate of achievement