charger une image sur un ili9341 3.2"

Bonjour.

Je cherche a faire un module d'interface graphique pour con troller un appareil géré par un RPI4
J'utilise:
NodeMcu v3

dsl, petit plantage

j'utilise :
écran 3,2" tactile ILI9341 SKU:MSP3218
NodeMcu v3 .

avec :

#include <Arduino.h>
#include <Adafruit_ILI9341esp.h>
#include <Adafruit_GFX.h>
#include <XPT2046.h>
#include <SdFat.h>

tous fonctionne, le TFt, le tactil, la SD et ma com WiFi entre le RPI4 et l'arduino
Je parvient a afficher une images contenue dans le code

#define DRAGON_WIDTH 110
#define DRAGON_HEIGHT 70

const PROGMEM uint16_t dragonBitmap[DRAGON_WIDTH * DRAGON_HEIGHT] = {
    0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF,
.....

affiché avec

  tft.drawRGBBitmap(0,0,dragonBitmap,DRAGON_WIDTH, DRAGON_HEIGHT);

, maintenant je voudrais charger ce bmp sur ma carte SD pour l'afficher

Et j'ai du mal a trouver les bonnes infos, quelqu'un aurais des infos correspondantes ?

PS si quelqu'un veux des infos sur les branchements et codes pour en arriver la je donnerais au cas par cas, mais une fois tous fonctionnel, je publierais les codes 'propre'

mackerjoga:
, maintenant je voudrais charger ce bmp sur ma carte SD pour l'afficher

Je suppose que tu veux dire "charger ce bmp depuis ma carte SD"

Il faut d'abord lire le fichier sur la carte SD et le placer dans un buffer en mémoire puis passer comme argument à drawRGBBitmap l'adresse de début du buffer.

RE, suite de mes test.

j'ai trouvé ce traitement affichage d'image dans les exemple de la bibliotéque Adafruit_TFTLCD_librairie :

tftbmp.ino :

hors ce n'est pas prevus sur ma librairies <Adafruit_ILI9341esp.h>, le code void bmpdraw(File f, int x, int y) semble bien fait, j'ais donc verifié les differances d'usage pour Adafruit_ILI9341esp et Adafruit_TFTLCD.
Pratiquement tous les usages sont identique

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          tft.pushColors(lcdbuffer, lcdidx, first);

seul probléme, pushColors qui prend 3 parametres dans Adafruit_TFTLCD

// Issues 'raw' an array of 16-bit color values to the LCD; used
// externally by BMP examples.  Assumes that setWindowAddr() has
// previously been set to define the bounds.  Max 255 pixels at
// a time (BMP examples read in small chunks due to limited RAM).
void Adafruit_TFTLCD::pushColors(uint16_t *data, uint8_t len, boolean first) {
  uint16_t color;
  uint8_t hi, lo;
  CS_ACTIVE;
  if (first == true) { // Issue GRAM write command only on first call
    CD_COMMAND;
    if (driver == ID_932X)
      write8(0x00);
    if ((driver == ID_9341) || (driver == ID_HX8357D)) {
      write8(0x2C);
    } else {
      write8(0x22);
    }
  }
  CD_DATA;
  while (len--) {
    color = *data++;
    hi = color >> 8; // Don't simplify or merge these
    lo = color;      // lines, there's macro shenanigans
    write8(hi);      // going on.
    write8(lo);
  }
  CS_IDLE;
}

et un seul dans Adafruit_ILI9341esp :

void Adafruit_ILI9341::pushColor(uint16_t color) {
  if (hwSPI) spi_begin();

  spiDcHigh();
  spiCsLow();

#ifdef ESP8266
  spiwrite16(color);
#else
  spiwrite(color >> 8);
  spiwrite(color);
#endif

  spiCsHigh();

  if (hwSPI) spi_end();
}

Donc je cherche a modifier l’appel dans mon ino

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster.  20 pixels seems a
// good balance.

#define BUFFPIXEL 20

void bmpDraw(char *filename, int x, int y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
  uint16_t lcdbuffer[BUFFPIXEL];  // pixel out buffer (16-bit per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();
  uint8_t  lcdidx = 0;
  boolean  first = true;

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

  Serial.println();
  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.println(F("File not found"));
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.println(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    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!
        Serial.print(F("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...
          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each column...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              // Push LCD buffer to the display first
              if(lcdidx > 0) {
                tft.pushColor(lcdbuffer);//tft.pushColors(lcdbuffer, lcdidx, first); differance entre les 2 codes, un parametre
                lcdidx = 0;
                first  = false;
              }
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }

            // Convert pixel from BMP to TFT format
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          } // end pixel
        } // end scanline
        // Write any remaining data to LCD
        if(lcdidx > 0) {
          tft.pushColor(lcdbuffer);//tft.pushColors(lcdbuffer, lcdidx, first); differance entre les 2 codes, un parametre
        } 
        Serial.print(F("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println(F("BMP format not recognized."));
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

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;
}

plus pressisement ces lignes :

          tft.pushColor(lcdbuffer);//tft.pushColors(lcdbuffer, lcdidx, first); differance entre les 2 codes, un parametre

logiquement ce resultat :

exit status 1
invalid conversion from 'uint16_t* {aka short unsigned int*}' to 'uint16_t {aka short unsigned int}' [-fpermissive]

Une idée, Quelqu'un ?

La librairie Adafruit_ILI9341esp est obsolète. Elle n'est même plus dans les dépots.
La librairie Adafruit_ILI9341 supporte directement les ESP

merci :wink:

Il y a un tuto ici pour cet écran avec un ESP32 (avec carte SD et affichage d'images) et la bibliothèque TFT_eSPI

merci je regarde ca :wink:

Merci, quand on a la tête dans le guidon, j'avais trouvé cette vidéo, mais étant parti sur un autre manipulation, je n'avais regardé que les connections sans prêter attention au reste, du coup du temps de perdu, merci #lesept de m'avoir remis dessus ça devrait régler mes problèmes et en plus en jpg

Merci lesept :slight_smile: :confused:

encore merci, le code du tuto marche a présent, mais je commence a le modifier et j'ai un pb :

#include <SPI.h>
#include <FS.h>
#include <SD.h>
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();

// JPEG decoder library
#include <JPEGDecoder.h>

int MY_TOUCH_CS = D2;
int MY_TFT_CS = D8;
int MY_SD_CS = D3;
String imm="";
String imm1="fond01.jpg";
String imm2="fond02.jpg";
// set up variables using the SD utility library functions:
/*
Sd2Card card;
SdVolume volume;
SdFile root;*/
//####################################################################################################
// Setup
//####################################################################################################
void setup() {
  Serial.begin(115200);
  pinMode(MY_TOUCH_CS, OUTPUT);
  pinMode(MY_TFT_CS, OUTPUT);
  pinMode(MY_SD_CS, OUTPUT);
  // Set all chip selects high to avoid bus contention during initialisation of each peripheral
  digitalWrite(MY_TOUCH_CS, HIGH); // Touch controller chip select (if used)
  digitalWrite(MY_TFT_CS, HIGH); // TFT screen chip select
  digitalWrite(MY_SD_CS, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
  tft.begin();

  if (!SD.begin(MY_SD_CS)) {
    Serial.println("Card Mount Failed");
    return;
  }
  Serial.println("initialisation done.");
  // all files are in landscape, you would need to change the code if you wanted portrait
  tft.setRotation(3);  // landscape
  tft.fillScreen(random(0xFFFF));

  File root=controleFile("/");
  if(!root){
      return;
  }else{
    root.close();
  }

  File images=controleFile("images/");
  if(!images){
      return;
  }else{
    images.close();
  }
}

//####################################################################################################
// Main loop
//####################################################################################################
void loop() {
  
  uint16_t t_x = 0, t_y = 0; // To store the touch coordinates

  // Pressed will be set true is there is a valid touch on the screen
  boolean pressed = tft.getTouch(&t_x, &t_y);
  if(pressed){
    if(imm=="fond01.jpg"){
      imm=imm2;
      action();
    }else{
      imm=imm1;
      action();
    }
  }
  delay(200);
}

void action(){    
  File image=controleFile(imm);
  if(image){
    //tft.fillScreen(ILI9341_BLACK);
    drawSdJpeg(image.name(), 0, 0);
    image.close();
  }else{
    drawSdJpeg("4b2.jpg", 0, 0);
  }
}

File controleFile(String nom){  
  File temp;
  Serial.print("le fichier/dossier ");
  Serial.print(nom);
  if(SD.exists(nom)){
    Serial.print(" existe");
    temp = SD.open(nom);
    if(temp){
      Serial.println(" et peut etre ouvert !");
      File cont=temp.openNextFile();  // Opens next file in root
      if(cont){
        Serial.println("il contiens :");
        while(cont){
          if(!cont.isDirectory()){
            Serial.println(cont.name());
          }
          cont = temp.openNextFile();  // Opens next file in root
        }
      }
      cont.close(); 
    }else{      
      Serial.println(" mais ne peut etre ouvert !!!");
    }
  }else{
    Serial.println(" existe pas");
  }
  return temp;

sur le moniteur il m'affiche ca :

11:18:13.702 -> le fichier/dossier / existe et peut etre ouvert !
11:18:13.702 -> il contiens :
11:18:13.702 -> fond01.jpg
11:18:13.702 -> fond02.jpg
11:18:13.702 -> 4b2.jpg
11:18:13.702 -> le fichier/dossier images/ existe et peut etre ouvert !
11:18:13.749 -> il contiens :
11:18:13.749 -> fond01.jpg
11:18:13.749 -> fond02.jpg
11:27:51.759 -> le fichier/dossier fond01.jpg existe pas
11:27:51.759 -> ERROR: File "4b2.jpg" not found!

Je dois me manquer sur le chemin mais je ne trouve pas, vous voyer ce qui m'échape ? :wink:

J'ai mis que la partie utile du code

ps a l'origine ce bous de code faisais bien le taf :

file = root.openNextFile(); // Opens next file in root
while(file)
{
if(!file.isDirectory())
{
drawSdJpeg(file.name(), 0, 0); // This draws a jpeg pulled off the SD Card
delay(4000);
}
file = root.openNextFile(); // Opens next file in root
}

C'est cette ligne

drawSdJpeg("4b2.jpg", 0, 0);

qui ne lui plaît pas. Peut-être que ce fichier n'existe pas dans ta carte SD.

Affiche l'arborescence de ta carte.

elle existe, en plus lors du listing des fichier il apparais je pense que mon chemin doit avoir un problème par contre mais je ne trouve pas le quel

Lorsque tu reviens de controleFile() tu refermes tous les fichiers ouverts donc dans action() tu n'es plus dans le répertoire images mais dans le répertoire racine.

Merci effectivement le temp.close() etait de trop mais ça change pas le pb :wink:

je pense que deja ce morceau de code cristalise le probléme :

drawSdJpeg("4b2.jpg", 0, 0);

il devrais fonctionner puisque le jpg existe et est listé préalablement, si je comprend pourquoi ca fonctionne pas je réglerais le reste
Pour info :

void drawSdJpeg(const char *filename, int xpos, int ypos) {

drawSdJpeg() ne dois pas chercher le fichier dans le bon répertoire . Je suis persuadé qu'il doit le chercher dans la racine.
Met ton code complet.

je pense aussi mais j'ai mi pour test le jpg sur la racine et ca marche pas :frowning:

Je tiens le probléme il me semble, il s'ajirais d'un probléme sur le SPI et la selection CS ou SS , il y a 3 esclave sur mon systéme, le touch, le SD et l'affichage, hor le fichier commence a etre introuvable apres le code :

boolean pressed = tft.getTouch(&t_x, &t_y);

Avant pas de probéme donc je pense que la liaison avec le touch n'est pas fermé, donc l'arduino cherche le fichier sur le Touch qui évidement ne l'a pas

void loop() {
  
    File imag=controleFile(txt_images+imm1);
    imag.close();
  uint16_t t_x = 0, t_y = 0; // To store the touch coordinates

  // Pressed will be set true is there is a valid touch on the screen
  boolean pressed = tft.getTouch(&t_x, &t_y);
  if(pressed){
    if(imm==imm1){
      imm=imm2;
      action();
    }else{
      imm=imm1;
      action();
    }
  }
  delay(200);
}

Il y aurais un équivalant de file.close() pour le touch ?, le code n'est pas bien documenté :wink: