Pilotage de 5 écrans TFT avec 1 carte SD

Débutant en Arduino, mes connaissances en programmation était surtout le Pascal. (il y a longtemps).
Pour une maquette (salle de contrôle du NCC-1701 série STAR TREK), j'avais besoin d'afficher divers images sur plusieurs écrans. ( 1er série de 5 écrans (sur 12 de la maquette) avec 1 lecteur SD et une carte NANO, pour la puissance 2 alim 5v et 3.3 3A.
voici donc mon code source , pour le câblage mes photos ne sont pas terribles, j'ai essayé de détailler au mieux sur le code.

/* image bmp 24 bits, ecran tft 128x160/ 1.8", 240x280/1.69", 135x240/1.14"
   Affichage d'images en format .bmp sur un
    5 écrans 4 ST7735 et 1 ST7789 avec lecteurcarte sd,  module arduino NANO
    auteur: SKRZYPEK Frederic

   basé sur 
   https://electroniqueamateur.blogspot.com/2020/04/aficher-sur-un-ecran-st7735-les.html
*/

// inclusion des bibliothèques
#include <Adafruit_GFX.h>    // graphics library
#include <Adafruit_ST7789.h> // spécifique à l'écran
#include <Adafruit_ST7735.h> // spécifique à l'écran
#include <SPI.h>             // communication SPI (écran et carte SD)
#include <SD.h>              // carte SD

// définition des broches utilisée
#define ECRAN_CS     10 // Aout R=1kΩ
#define ECRAN_DC    9 // Aout R=1kΩ
//#define ECRAN_RST5   5
#define RESET_RST   -1 // 2 bornes RST sur NANO
//D11 -> MOSI SD + SDA TFT (ecran3.3v 1/2/3/4 Aout R=1kΩ, ecran 5v pas de resistance sur commun carte sd et sck ecran )
//D12 -> MISO SD
//D13 -> SCK SD + SCL TFT
#define SD_CS      4

#define ECRAN1_CS A0 // Aout R=1kΩ
#define ECRAN1_DC A1  // Aout R=1kΩ 

#define ECRAN2_CS A3// Aout R=1kΩ
#define ECRAN2_DC A2 // Aout R=1kΩ

#define ECRAN3_CS A4 // Aout R=1kΩ
#define ECRAN3_DC A5 // Aout R=1kΩ
 
#define ECRAN4_CS  8 // Aout R=1kΩ
#define ECRAN4_DC  7 // Aout R=1kΩ
//---  jusqu a 7 ecrans maxi si SD_cs sur D10  ( D3 D2 / D4 D5 / D6 D7 / D8 D9 / A0 A1 / A2 A3 / A4 A5). A6 A7 / RX0 TX1 ne fonctionne pas pour les ecrans CS DC.

#define dureeAffichage 7500 // Duree d'affichage 

Adafruit_ST7735 tft = Adafruit_ST7735(ECRAN_CS, ECRAN_DC,RESET_RST);
Adafruit_ST7789 tft1 = Adafruit_ST7789(ECRAN1_CS, ECRAN1_DC, RESET_RST);
Adafruit_ST7789 tft2 = Adafruit_ST7789(ECRAN2_CS, ECRAN2_DC, RESET_RST);
Adafruit_ST7789 tft3 = Adafruit_ST7789(ECRAN3_CS, ECRAN3_DC, RESET_RST);
Adafruit_ST7789 tft4 = Adafruit_ST7789(ECRAN4_CS, ECRAN4_DC,RESET_RST );

// ------------------------------------variables
  int     largeur_image=0;
  int     hauteur_image=0;
  int     mon_ecran240 =0;
  int     mon_ecran280 =0;
  #define CORNER_RADIUS 0
// -   -----_-----------------------definition des couleurs

#define NOIR 0x0000
#define BLEU 0x001F
#define ROUGE 0xF800
#define VERT 0x07E0
#define CYAN 0x07FF
#define VIOLLET 0xF81F
#define JAUNE 0xFFE0
#define BLANC 0xFFFF

void setup(void) {
  Serial.begin(9600); // utile pour débogage

   tft.initR(INITR_BLACKTAB); // intialisation des écrans
   tft1.init(135,240);
   tft2.init(240,280);
   tft3.init(240,280);
   tft4.init(135,240);

   tft.setRotation(3);  // mode paysage
   tft1.setRotation(1);  // mode paysage
   tft2.setRotation(3);  // mode paysage
   tft3.setRotation(3);  // mode paysage
   tft4.setRotation(1);  // mode paysage

  //Serial.print("Initialisation de la carte SD...");
  if (!SD.begin(SD_CS)) {
   Serial.println("Echec!");
    return;
  }
  Serial.println("Reussie!");
 //------------------------------TEST des ecrans
  tft.fillScreen(0);
  tft1.fillScreen(0); 
  tft2.fillScreen(0); 
  tft3.fillScreen(0);  
  tft4.fillScreen(0);
  
delay(200);
  tft4.fillRect(0,0,135,240, BLEU);
  tft4.drawRect(0,0,135,240, BLEU); 
  tft4.fillRect(0,0,135,240,ROUGE);
  tft4.drawRect(0,0,135,240, ROUGE);
delay(200);
 tft1.fillRect(0,0,135,240, JAUNE);
  tft1.drawRect(0,0,135,240,JAUNE); 
  tft1.fillRect(0,0,135,240,VERT);
  tft1.drawRect(0,0,135,240, VERT);
 delay(200); 
  tft2.fillRect(0,0,240,280,CYAN);
  tft2.drawRect(0,0,240,280, CYAN); 
  tft2.fillRect(0,0,240,280,BLEU);
  tft2.drawRect(0,0,240,280, BLEU);
  delay(200);
  tft3.fillRect(0,0,240,280, VERT);
  tft3.drawRect(0,0,240,280,VERT); 
  tft3.fillRect(0,0,240,280,VIOLLET);
  tft3.drawRect(0,0,240,280, VIOLLET);
 delay(200); 
}

void loop() {

 // on regarde le contenu de la carte SD, et on tente
  // d'afficher chaque fichier, un après l'autre.

  File root = SD.open("/");
  while (true) {
    File entry =  root.openNextFile();
    if (! entry) {    
      root.close();
      return;
    } // fichier lu mais inexistant ? SYSTEM~1 genere un retour au 1er bmp
   if (entry.name() =='SYSTEM~1') {
      Serial.print(entry.name());
      delay(200); }
     else {        
        controlelataille(entry.name(), 0, 0);
         Serial.print(largeur_image); // je controle la taille de l'image pour envoie sur mon bon ecran
              if (largeur_image == 160 )   {
                   bmpDraw160(entry.name(), 0, 0);
                 }
                 if (largeur_image == 240 )   {
                    if (mon_ecran240 == 0) {
                        bmpDraw240_1(entry.name(), 0, 0);
                        mon_ecran240 = 1 ;}
                        else {bmpDraw240_2(entry.name(), 0, 0);
                        mon_ecran240 = 0;}
                 }
                 if (largeur_image == 280 )   {
                 if (mon_ecran280 == 0) {
                        bmpDraw280_1(entry.name(), 0, 0);
                        mon_ecran280 = 1 ;}
                        else {bmpDraw280_2(entry.name(), 0, 0);
                        mon_ecran280 = 0;}
                 }


     delay(1000);
          }
    entry.close();
  }
}

//-----------------------------------------------------------------------------------------------------------------------------
#define BUFFPIXEL 20// nombre de pixels traités à la fois.

// la fonction bmpDrawd'Adafruit qui affiche un fichier .bmp:

void bmpDraw160(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft.width()) || (y >= tft.height())) return;

  //Serial.print(F("Chargement de l'image "));
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("Fichier non trouve"));
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft.width())  w = tft.width()  - x;
        if ((y + h - 1) >= tft.height()) h = tft.height() - y;
        tft.startWrite();
        tft.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft.endWrite();

        delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
}//--------------------------------------------------------------------------------------------------------------

void bmpDraw240_1(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft1.width()) || (y >= tft1.height())) return;

  //Serial.print(F("Chargement de l'image "));
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("Fichier non trouve"));
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft1.width())  w = tft1.width()  - x;
        if ((y + h - 1) >= tft1.height()) h = tft1.height() - y;
        tft1.startWrite();
        tft1.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft1.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft1.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft1.pushColor(tft.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft1.endWrite();

        //delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
}  //-------------------------------- fin image 240 ecran n°1

  void bmpDraw240_2(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft4.width()) || (y >= tft4.height())) return;

  //Serial.print(F("Chargement de l'image "));
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("Fichier non trouve"));
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft4.width())  w = tft4.width()  - x;
        if ((y + h - 1) >= tft4.height()) h = tft4.height() - y;
        tft4.startWrite();
        tft4.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft4.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft4.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft4.pushColor(tft.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft4.endWrite();

        //delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
}  //-------------------------------- fin image 240 second


void bmpDraw280_1(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft2.width()) || (y >= tft2.height())) return;

  //Serial.print(F("Chargement de l'image "));
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("Fichier non trouve"));
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft2.width())  w = tft2.width()  - x;
        if ((y + h - 1) >= tft2.height()) h = tft2.height() - y;
        tft2.startWrite();
        tft2.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft2.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft2.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft2.pushColor(tft.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft2.endWrite();

        delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
} //-----------------------fin ecran 280

void bmpDraw280_2(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft3.width()) || (y >= tft3.height())) return;

  //Serial.print(F("Chargement de l'image "));
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("Fichier non trouve"));
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- procedé!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft3.width())  w = tft3.width()  - x;
        if ((y + h - 1) >= tft3.height()) h = tft3.height() - y;
        tft3.startWrite();
        tft3.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft3.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft3.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft3.pushColor(tft.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft3.endWrite();

        //delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
} //-----------------------fin ecran 280 second


// pour controle taille image
void controlelataille(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;  
  uint8_t  bmpDepth;              
  uint32_t bmpImageoffset;       
  uint32_t rowSize;              
  
  boolean  goodBmp = false;      
  boolean  flip    = true;   

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
   // Serial.print(F("pas d'image"));
    return;
  }

  // Analyser l'en-tête BMP
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    //Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Lire et ignorer les octets du créateur
    bmpImageoffset = read32(bmpFile); // Début des données d'image
  
    //Lire l'en-tête DIB
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    largeur_image =read32(bmpFile);
    hauteur_image= read32(bmpFile);
    //Serial.print(largeur_image);
      // Serial.print('x');
      //  Serial.print(hauteur_image);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
     // Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
       
      } // end goodBmp
    }
  }

  bmpFile.close();
  //if(!goodBmp) Serial.println(F("BMP format non reconnu."));
}


uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}





1 Like

Bonjour @frederic_skrzypek

Ce genre de projet m'intéresse, je fais également des maquettes.

Tu arrives à controler 5 écrans avec une seule nano? Je pensais cela impossible vu la quantité de mémoire et le nombre de broche de cette carte...

Pourrais tu détailler ton câblage? Un schéma peut-être...

Bonjour.

Voici un schema pour 3 écrans et carte SD, pour 5 écrans il suffit de reproduire le même câblage sur les autres sorties indiquées sur le programme 5 écrans et model different
Attention toutefois au carte SD utilisées, (la marque, la taille), au delà de 512 MO, j'ai eu des problèmes de lecture. et attention au tension 3.3v ecrans et 5v
sur le schema les écrans sont en 3.3v et la carte SD/ NANO sur 5v

/* image bmp 24 bits, ecran tft 128x160/ 1.8", 240x280/1.69", 135x240/1.14"
   Affichage d'images en format .bmp sur un
    3 écrans  ST7789 avec lecteurcarte sd,  module arduino NANO
    auteur: SKRZYPEK Frederic

   basé sur 
   https://electroniqueamateur.blogspot.com/2020/04/aficher-sur-un-ecran-st7735-les.html
*/

// inclusion des bibliothèques
#include <Adafruit_GFX.h>    // graphics library
#include <Adafruit_ST7789.h> // spécifique à l'écran

#include <SPI.h>             // communication SPI (écran et carte SD)
#include <SD.h>              // carte SD
#include <Servo.h>
// définition des broches utilisée
#define ECRAN1_CS     8 // Ajout R=1kΩ
#define ECRAN1_DC    9 // Ajout R=1kΩ

#define RESET_RST   -1 // 2 bornes RST sur NANO
//D11 -> MOSI SD + SDA TFT (ecran3.3v 1/2/3/4 Ajout R=1kΩ, pas de resistance sur commun carte sd et sck ecran )
//D12 -> MISO SD
//D13 -> SCK SD + SCL TFT
#define SD_CS      10

#define ECRAN3_CS A0 // Ajout R=1kΩ
#define ECRAN3_DC A1  // Ajout R=1kΩ 

#define ECRAN2_CS  A2 // Ajout R=1kΩ
#define ECRAN2_DC  A3 // Ajout R=1kΩ


//---  jusqu a 5 ecrans maxi si SD_cs sur D10  ( D3 D2 / D4 D5 / D6 D7 / D8 D9 / A0 A1 / A2 A3 / A4 A5). A6 A7 / RX0 TX1 ne fonctionne pas pour les ecrans CS DC.

#define dureeAffichage 7500 // Duree d'affichage 
#define PAUSE 5000

#define BUFFPIXEL 20 // nombre de pixels traités à la fois.

Adafruit_ST7789 tft1 = Adafruit_ST7789(ECRAN1_CS, ECRAN1_DC, RESET_RST);
Adafruit_ST7789 tft2 = Adafruit_ST7789(ECRAN2_CS, ECRAN2_DC, RESET_RST);
Adafruit_ST7789 tft3 = Adafruit_ST7789(ECRAN3_CS, ECRAN3_DC, RESET_RST);

// ------------------------------------variables
Servo myservo;
Servo myservo2;   // create servo object to control a servo
// twelve servo objects can be created on most boards
int x; int y;
  
// Initialisation des variables
  int     largeur_image=0;
  int     hauteur_image=0;
  int     mon_ecran240 =0;
 int     alerte_rouge =0;
 int pos = 0;
 int pos2 = 145;   // initialise le mini maxi servo porte
 int temp_porte =200; 
// -   -----_-----------------------definition des couleurs

#define NOIR 0x0000
#define BLEU 0x001F
#define ROUGE 0xF800
#define VERT 0x07E0
#define CYAN 0x07FF
#define VIOLLET 0xF81F
#define JAUNE 0xFFE0
#define BLANC 0xFFFF

void setup(void) {
  Serial.begin(9600); // utile pour débogage

    // intialisation des écrans
   tft1.init(135,240);
   tft2.init(135,240);
   tft3.init(135,240);
   

    // mode essai
   tft1.setRotation(0);  // mode paysage
   tft2.setRotation(0);  // mode paysage
   tft3.setRotation(0);  // mode paysage
   
  tft1.fillRect(0,0,135,240,BLEU);
  tft2.fillRect(0,0,135,240,VERT);
  tft3.fillRect(0,0,135,240,ROUGE);
  
   tft1.setRotation(1);  // mode paysage
   tft2.setRotation(1);  // mode paysage
   tft3.setRotation(1);  // mode paysage
   
//Serial.print("Initialisation de la carte SD...");
  if (!SD.begin(SD_CS)) {
   Serial.println("Echec!");
    return;
  }
  Serial.println("Reussie!");
 //------------------------------TEST des ecrans
  //tft5.fillScreen(1);
  tft1.fillScreen(1); 
  tft2.fillScreen(1);  
  tft3.fillScreen(1);

  myservo.attach(4); 
  myservo2.attach(3); // attaches the servo on pin'to the servo object
  
 delay(200); 
}

void loop() {

 // on regarde le contenu de la carte SD, et on tente
  // d'afficher chaque fichier, un après l'autre.

  File root = SD.open("/");
  while (true) {
    delay(200);
   File entry =  root.openNextFile();
    if (! entry) {  Serial.println("rater");  
     root.close(); 
      return;
    } // fichier lu mais inexistant ? SYSTEM~1 genere un retour au 1er bmp
   if (entry.name() =='SYSTEM~1') {
      Serial.print(entry.name());
      delay(200); }
     else {        
        //controlelataille(entry.name(), 0, 0);
      //Serial.print("largeur image:");
        // Serial.print(largeur_image); // je controle la taille de l'image pour envoie sur mon bon ecran
     Serial.print("   nom:");
              Serial.println(entry.name());
              //entry.close();
                    //Serial.print("ecran:");
                    //Serial.println(mon_ecraSn240);
                //if (entry.name() !="" )   {
                    switch (mon_ecran240) {
                    case 0: 
                     mon_ecran240 =1;
                     alerte_rouge = alerte_rouge+1 ;
                    bmpDraw(entry.name(), 0, 0);
                    entry.close();
                     break;  

                  case 1: 
                    mon_ecran240=2;
                  bmpDraw_ecran2(entry.name(), 0, 0);
                  entry.close();
                    break;
                    
                    case 2: 
                    mon_ecran240=3;
                    bmpDraw_ecran3(entry.name(), 0, 0);
                    entry.close();
                    
                     break;
                    
                    
                    case 3:
                      mon_ecran240=4;
                    //portes_assensseur();
                    ouvre_porte();
                    //Serial.print(mon_ecran240);
                 delay(3000);
                      break;

                    case 4:
                      mon_ecran240=0;
                    condition_alert()
                    //Serial.print(mon_ecran240);
                 delay(3000);
                      break;
                    }
              
                 
                 
     }

     delay(1000);
        //  }
    
  }
}

//-----------------------------------------------------------------------------------------------------------------------------


// la fonction bmpDrawd'Adafruit qui affiche un fichier .bmp:

void bmpDraw(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft1.width()) || (y >= tft1.height())) return;

  Serial.print(F("Chargement de l'image "));
  Serial.print("ecran:");
  Serial.println(mon_ecran240);
 // Serial.println(filename);
    
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   Serial.print(F("Fichier non trouve"));
   
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft1.width())  w = tft1.width()  - x;
        if ((y + h - 1) >= tft1.height()) h = tft1.height() - y;
        tft1.startWrite();
        tft1.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft1.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft1.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft1.pushColor(tft1.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft1.endWrite();

        delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close(); 
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
}//--------------------------------------------------------------------------------------------------------------

void bmpDraw_ecran2(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if ((x >= tft2.width()) || (y >= tft2.height())) return;

  Serial.print(F("Chargement de l'image "));
  Serial.print("ecran:");
  Serial.println(mon_ecran240);
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
Serial.print(F("Fichier non trouve"));
Serial.print(filename);
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft2.width())  w = tft2.width()  - x;
        if ((y + h - 1) >= tft2.height()) h = tft2.height() - y;
        tft2.startWrite();
        tft2.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft2.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft2.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft2.pushColor(tft2.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft2.endWrite();

        //delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
}  //-------------------------------- fin image  ecran n°2

  

void bmpDraw_ecran3(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // largeur et hauteur en pixels
  uint8_t  bmpDepth;              // bits par pixels (doit être 24)
  uint32_t bmpImageoffset;        // Début de la description de l'image dans le fichier
  uint32_t rowSize;               
  uint8_t  sdbuffer[3 * BUFFPIXEL]; 
  uint8_t  buffidx = sizeof(sdbuffer); 
  boolean  goodBmp = false;       
  boolean  flip    = true;       
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();
Serial.print("ecran:");
  Serial.println(mon_ecran240);
  if ((x >= tft3.width()) || (y >= tft3.height())) return;

  Serial.print(F("Chargement de l'image "));
  
  // ouverture du fichier

  if ((bmpFile = SD.open(filename)) == NULL) {
   Serial.print(F("Fichier non trouve"));
   Serial.print(filename);
    return;
  }
 // Analyse du fichier
  if (read16(bmpFile) == 0x4D42) { // lecture des 16 premiers bits du fichier <
    read32(bmpFile); // lecture des 32 bits suivants (pas utiles)
    read32(bmpFile);
    bmpImageoffset = read32(bmpFile);
    read32(bmpFile);
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if (read16(bmpFile) == 1) {
      bmpDepth = read16(bmpFile); // bits par pixel
      if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = non compressé
        goodBmp = true; // Supported BMP format -- proceed!
        rowSize = (bmpWidth * 3 + 3) & ~3;
        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }
        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= tft3.width())  w = tft3.width()  - x;
        if ((y + h - 1) >= tft3.height()) h = tft3.height() - y;
        tft3.startWrite();
        tft3.setAddrWindow(x, y, w, h);

        for (row = 0; row < h; row++) { // pour chaque ligne
          if (flip)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) {
            tft3.endWrite();
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer);
          }

          for (col = 0; col < w; col++) { // pour chaque pixel
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft3.startWrite();
            }
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft3.pushColor(tft3.color565(r, g, b));
          } // fin pixel
        } // fin ligne
        tft3.endWrite();

        delay(2000); // on laisse l'image à l'écran 20 secondes

      } // fin goodBmp
    }
  }
  bmpFile.close();
  if (!goodBmp) Serial.println(F("Format de BMP non reconnu"));
} //-----------------------fin ecran 3


void portes_assensseur() {
 pos2=pos;
  for ( pos= 0; pos <= 150; pos += 1) { 
    x=pos;
    y=180-x;
  
    myservo.write(pos); 
    myservo2.write(y);
    //Serial.print("position x:");Serial.println(x);
    //Serial.print("position y:");Serial.println(y);
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  delay(5000);

  pos2=pos;
  for (pos = 150; pos >= 0; pos -= 1){
  
   x=pos;
    y=180-x;
   
    myservo.write(pos); 
    myservo2.write(y); 
    //Serial.print("position x:");Serial.println(x);
    //Serial.print("position y:");Serial.println(y);
    //pos2=pos2+pos;            // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  

delay(15000);
} // END BASIC TEXT EXAMPLE

void condition_alert() {
  // Show text scaling with built-in font.
  tft1.fillScreen(ROUGE);
    tft1.setTextColor(NOIR);
 tft1.setFont();
 //tft.setFont(&FreeSerif9pt7b;               // Use default font
  tft1.setCursor(30, 30); // Initial cursor position
  
  tft1.setTextSize(3);
  tft1.println(F("CONDITION"));
  tft1.setTextSize(5);
  tft1.setCursor(40, 65); // Initial cursor position
  tft1.println("ALERT");
  
  delay(1000);
} // END BASIC TEXT EXAMPLE

// pour controle taille image
void controlelataille(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;  
  uint8_t  bmpDepth;              
  uint32_t bmpImageoffset;       
  uint32_t rowSize;              
  
  boolean  goodBmp = false;      
  boolean  flip    = true;   

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print(F("pas d'image"));
    return;
  }

  // Analyser l'en-tête BMP
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Lire et ignorer les octets du créateur
    bmpImageoffset = read32(bmpFile); // Début des données d'image
  
    //Lire l'en-tête DIB
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    largeur_image =read32(bmpFile);
    hauteur_image= read32(bmpFile);
   Serial.print(largeur_image);
      // Serial.print('x');
      //  Serial.print(hauteur_image);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
     // Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
       
      } // end goodBmp
    }
  }

  bmpFile.close();
  //if(!goodBmp) Serial.println(F("BMP format non reconnu."));
}


uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

Merci pour le schéma et les explications. :slight_smile:

Mon projet actuel est un diorama urbain futuriste, je compte utiliser des petits tft pour faire des affiches publicitaires.

Question mémoires, tu n'a pas de soucis en pilotant 5 écrans avec une Nano ?

Tu as avancé dans ta maquette ?

pour la mémoire, le programme lie le fichier bmp sur la carte SD , et j'utilise un buffer, ( 20 dans le programme) tu peut éventuellement modifier la valeur et voir si cela rend plus rapidement ou moins, pour t'on projet, généralement les pubs en ville apparaisse pas rapidement.

pour mon projet regarde sur ma page Facebook, les diffusions sont en mode public.
recherche frederic skrzypek.

au plaisir de voir l'avancement de t'on projet , j'espère que les explications te seront utiles.

Salut! :slight_smile:
J'ai parcouru ton projet sur Facebook, je sais qu'il n'est pas encore terminé mais bravo pour le travail déjà accompli. J'ai beaucoup aimé la porte commandé par servomoteurs.

Je fait un diorama à plus petite échelle, car mes Gundams sont à l'échelle 1/144. Ce sont des "robots géants" (les maquettes mesures en moyenne 15cm de haut).

Dans mes dioramas j'utilise plus ou moins les mêmes techniques que toi sauf que je fais pas encore d'impression 3D.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.