Un moniteur série (UART) matériel avec Arduino

Une liaison série UART permet une communication entre deux systèmes électroniques, elle est par exemple utilisée par votre carte Arduino lors de l'envoie du programme ou lors de la communication avec un ordinateur.
Schéma d'une communication UART
Dans le logiciel Arduino IDE, le "moniteur série" permet de savoir ce qu'il se passe sur la liaison série matérielle de l'Arduino. Dans le cas de l'Arduino UNO par exemple, la liaison série matérielle se trouve sur les entrées/sorties 0 (broche de réception) et 1 (broche de transmission), et elle est utilisée notamment pour l'envoie d'un programme au microcontrôleur.

Imaginons que vous êtes en train de réaliser un système électronique, mettant en jeu un microcontrôleur et une puce GSM, ou encore GPS (en effet les modules GSM et GPS utilisés dans le monde Arduino communiquent pour la plupart via liaison série UART). Si vous désirer "écouter" ce qu'il se dit sur cette liaison, il va vous falloir soit programmer le microcontrôleur pour qu'il rapporte via un autre moyen ce qu'il se dit sur la liaison série, soit vous utilisez un autre microcontrôleur ou équipement qui va "espionner" la communication.

Voilà le projet que je veux partager avec vous dans cet article : un moniteur série matériel. Il permet de visualiser facilement et rapidement les données échangées via une liaison série sans la perturber. Il est bien sur possible de régler la vitesse de la communication (en bauds) et on peut distinguer la provenance de chacun des messages (pour savoir de quel côté de la liaison  série viennent-il).

Alors comment écouter sur deux lignes séries (une liaison = deux lignes, TX et RX) en même temps à l'aide d'un Arduino ? L'Arduino UNO ne possèdent qu'une seule liaison série matérielle, donc une seule broche de réception (nommée RX). On pourrait utiliser un port série logiciel (software serial) mais j'ai préféré employer la solution de facilité et simplement utiliser un microcontrôleur disposant de deux ports série matériels : le ATMega644.
Ce microcontrôleur présente d’autres intérêts par rapport au traditionnel ATMega328 équipant l'Arduino UNO :
ATMega328 ATMega644
Flash [ko] 32 64
RAM [ko] 32 64
ROM [ko] 1 2
IO 23 32
ADC 8 8
Les trois types de mémoire ont le double de capacité et il dispose aussi d'un plus grand nombre d'entrée/sortie pour un prix "seulement" deux fois supérieur à l'ATMega328.
Ce microcontrôleur existe dans un boitier DIP facilement utilisable avec une breadboard par exemple mais j'ai préféré faire fabriquer une petite carte pour l'accueillir.
Photo de la carte pour l'ATMEGA644 nue
Cette simple carte accueille l'ATMEGA644 (ou l'ATMEGA1284, ces deux sont compatibles) ainsi qu'une alimentation, un circuit de Reset et un cristal à 16MHz.
Photo de la carte pour l'ATMEGA644 assemblée
J'utilise ensuite un écran LCD de type ILI9341 pour afficher de différentes couleurs les messages venant des deux lignes séries surveillées.
Photo de l'écran d'affichage
J'ai monté tout ça sur une carte à trou avec deux interrupteurs pour désactiver à la demande l'affichage d'une des deux lignes de la communication série (pour n'écouter qu'un seul coté de la liaison par exemple), un bouton permettant de parcourir les différentes vitesse de communication (en bauds) et deux LED pour afficher sur quelle ligne série a eu lieu la dernière communication.
Photo du système
J'ai placé un tampon à base d'amplificateurs opérationnels en montage suiveur sur les deux lignes séries pour qu'il n'y ait aucunes chances qu’elles soient perturbées par "l'espion".
Schéma du double AOP LM358 configuré en deux montages suiveurs
Il me suffit donc d'alimenter ma carte en 5V et je peux ensuite surveiller une liaison série UART sans la perturber.

Il est sans doute largement possible d'améliorer le montage par exemple avec une carte SD pour enregistrer les données de communication etc... Mais ce petit montage m’a suffi pour faire fonctionner le module GSM que je souhaitais.


Voici le programme Arduino utilisé :

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#define TFT_DC 3
#define TFT_CS 4
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

char inchar1;  //caractère entrant
char inchar2;
String message1 = ""; //message entier
String message2 = "";

const byte led1 = 0;
const byte led2 = 2;
const byte act1 = A5;
const byte act2 = A7;
const byte bp = A2;

unsigned int ixe = 11; //position x d'écriture

#include <EEPROM.h>
const byte addrEE = 52;  //on enregistre la vitesse de la communication dans la case 52

byte selbaudrate = 2; //on sélectionne 9600 bauds au début
unsigned long baudrate = 0;

void setup() {

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(act1, INPUT_PULLUP);
  pinMode(act2, INPUT_PULLUP);
  pinMode(bp, INPUT_PULLUP);

  fctchangebaud(); //on initialise la liaison série et l'interface graphique
}

void loop() {

  if (digitalRead(bp) == LOW) {
    delay(5); //anti-rebond
    if (selbaudrate == 7) selbaudrate = 0;
    else selbaudrate++;
    EEPROM.write(addrEE, selbaudrate);
    fctchangebaud();
  }

  if(digitalRead(act1) == LOW) {

    while (Serial.available() && message1.length() < 53) {
      inchar1 = Serial.read();
      message1 += inchar1;
      delay(10);  //le temps que le prochain caractère arrive, IMPORTANT !
    }

    message1.trim();
    if (message1 != "") {
      digitalWrite(led1, LOW);
      digitalWrite(led2, HIGH);

      if (ixe > 230) {
        ixe = 11;
        tft.fillScreen(ILI9341_BLACK);
        tft.setCursor(0, 0);
        tft.setTextColor(ILI9341_WHITE);
        tft.print("SERIAL MONITOR - ");
        tft.print(baudrate);
        tft.print(" bauds");
        tft.drawLine(0, 9, 320, 9, ILI9341_WHITE);
      }

      tft.setCursor(1, ixe);
      tft.setTextColor(ILI9341_GREEN);
      tft.print(message1);
      ixe = ixe + 10;
    }
  }
  else while(Serial.available()) Serial.read();  //on vide quand même le buffer de l'autre serial

  if(digitalRead(act2) == LOW) {

    while (Serial1.available() && message2.length() < 53) {
      inchar2 = Serial1.read();
      message2 += inchar2;
      delay(10);  //le temps que le prochain caractère arrive, IMPORTANT !
    }

    message2.trim();
    if (message2 != "") {
      digitalWrite(led2, LOW);
      digitalWrite(led1, HIGH);

      if (ixe > 230) {
        ixe = 11;
        tft.fillScreen(ILI9341_BLACK);
        tft.setCursor(0, 0);
        tft.setTextColor(ILI9341_WHITE);
        tft.print("SERIAL MONITOR - ");
        tft.print(baudrate);
        tft.print(" bauds");
        tft.drawLine(0, 9, 320, 9, ILI9341_WHITE);
      }

      tft.setCursor(1, ixe);
      tft.setTextColor(ILI9341_RED);
      tft.print(message2);
      ixe = ixe + 10;

    }
  }
  else while(Serial1.available()) Serial1.read();  //on vide quand même le buffer de l'autre serial

  message1 = ""; //Vide la variable "message"
  message2 = "";
}

void fctchangebaud()  {

  Serial.end();
  Serial1.end();

  switch (selbaudrate) {
  case 0:
    baudrate = 2400;
    break;
  case 1:
    baudrate = 4800;
    break;
  case 2:
    baudrate = 9600;
    break;
  case 3:
    baudrate = 19200;
    break;
  case 4:
    baudrate = 28800;
    break;
  case 5:
    baudrate = 38400;
    break;
  case 6:
    baudrate = 57600;
    break;
  case 7:
    baudrate = 115200;
    break;
  }

  Serial.begin(baudrate);
  Serial1.begin(baudrate);

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.print("SERIAL MONITOR - ");
  tft.print(baudrate);
  tft.print(" bauds");
  tft.drawLine(0, 9, 320, 9, ILI9341_WHITE);

  ixe = 11;

}

Et voici le schéma électrique complet du système (cliquez pour agrandir) :
Schéma éléctrique du moniteur série matériel


A bientôt !

Liens externes :
Bibliothèque Sanguino : https://github.com/Lauszus/Sanguino
Bibliothèque ILI9341 : https://github.com/adafruit/Adafruit_ILI9341
ATMega644 : http://www.atmel.com/devices/ATMEGA644.aspx

0 commentaires:

Enregistrer un commentaire