« ESP32 écran, affichage d'un fond de carte » : différence entre les versions
Aller à la navigation
Aller à la recherche
Aucun résumé des modifications |
Aucun résumé des modifications |
||
| Ligne 1 : | Ligne 1 : | ||
L'objectif serait de pouvoir afficher un fond de carte sur un module ESP32 écran, par exemple pour suivre des positions GPS. | L'objectif serait de pouvoir afficher un fond de carte sur un module ESP32 écran, par exemple pour suivre des positions GPS. | ||
Pour l'instant il ne s'agit que de la partie affichage, | Pour l'instant il ne s'agit que de la partie affichage, et qui ne fonctionne. Le test porte sur la récupération d'une tuile (un morceau de carte), c'est à dire d'un fichier d'une image en format PNG. La récupération et l'affichage se font bien. C'est la bibliothèque PNGdec qui est utilisée. | ||
Exemple d'URL de récupération de tuile : | Exemple d'URL de récupération de tuile : | ||
| Ligne 17 : | Ligne 17 : | ||
const char* ssid = "2.4GHz-MEMOIRE-VIVE"; | const char* ssid = "2.4GHz-MEMOIRE-VIVE"; | ||
const char* password = " | const char* password = "memoirevive79"; | ||
TFT_eSPI tft = TFT_eSPI(); | TFT_eSPI tft = TFT_eSPI(); | ||
PNG png; | PNG png; | ||
// === Callback appelé par PNGdec pour chaque ligne de pixels === | |||
int pngDraw(PNGDRAW *pDraw) { | int pngDraw(PNGDRAW *pDraw) { | ||
static int offsetX = 0; | static int offsetX = 0; | ||
static int offsetY = 0; | static int offsetY = 0; | ||
if (pDraw->y == 0) { | if (pDraw->y == 0) { | ||
int width = pDraw->iWidth; | int width = pDraw->iWidth; | ||
| Ligne 29 : | Ligne 31 : | ||
offsetX = (tft.width() - width) / 2; | offsetX = (tft.width() - width) / 2; | ||
offsetY = (tft.height() - height) / 2; | offsetY = (tft.height() - height) / 2; | ||
Serial.printf(" | |||
Serial.printf("\n=== Début du rendu PNG ===\n"); | |||
Serial.printf("Dimensions PNG : %d x %d\n", width, height); | |||
Serial.printf("Offset calculé : (%d, %d)\n", offsetX, offsetY); | |||
Serial.printf("Heap libre avant rendu : %d\n", ESP.getFreeHeap()); | |||
} | |||
if (pDraw->y % 10 == 0) | |||
Serial.printf("Décodage ligne %d / %d\n", pDraw->y, png.getHeight()); | |||
if (pDraw->iWidth > 512) { | |||
Serial.printf("⚠️ Ligne trop large (%d px)\n", pDraw->iWidth); | |||
return 0; | |||
} | } | ||
static uint16_t lineBuffer[512]; | |||
static uint16_t lineBuffer[512]; | |||
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_LITTLE_ENDIAN, 0xFFFF); | png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_LITTLE_ENDIAN, 0xFFFF); | ||
if (pDraw->y < 3) { | |||
Serial.printf("Premiers pixels ligne %d : ", pDraw->y); | |||
for (int i = 0; i < min(4, pDraw->iWidth); i++) | |||
Serial.printf("%04X ", lineBuffer[i]); | |||
Serial.println(); | |||
} | |||
tft.pushImage(offsetX, offsetY + pDraw->y, pDraw->iWidth, 1, lineBuffer); | tft.pushImage(offsetX, offsetY + pDraw->y, pDraw->iWidth, 1, lineBuffer); | ||
if (pDraw->y == png.getHeight() - 1) { | |||
Serial.printf("=== Fin du rendu PNG ===\n"); | |||
Serial.printf("Heap libre après rendu : %d\n", ESP.getFreeHeap()); | |||
} | |||
return 1; | return 1; | ||
} | } | ||
// --- Télécharge et affiche une tuile OSM avec debug complet --- | |||
void showTile(int zoom, int x, int y) { | void showTile(int zoom, int x, int y) { | ||
Serial.printf(" | Serial.printf("Heap avant téléchargement: %d\n", ESP.getFreeHeap()); | ||
WiFiClientSecure client; | WiFiClientSecure client; | ||
client.setInsecure(); | client.setInsecure(); | ||
HTTPClient https; | HTTPClient https; | ||
char url[128]; | char url[128]; | ||
sprintf(url, "https://tile.openstreetmap.org/%d/%d/%d.png", zoom, x, y); | sprintf(url, "https://tile.openstreetmap.org/%d/%d/%d.png", zoom, x, y); | ||
| Ligne 52 : | Ligne 83 : | ||
} | } | ||
https.addHeader("User-Agent", "Mozilla/5.0 (ESP32) | https.addHeader("User-Agent", "Mozilla/5.0 (ESP32)"); | ||
int httpCode = https.GET(); | int httpCode = https.GET(); | ||
if (httpCode != HTTP_CODE_OK) { | if (httpCode != HTTP_CODE_OK) { | ||
| Ligne 61 : | Ligne 93 : | ||
} | } | ||
WiFiClient *stream = https.getStreamPtr(); | |||
uint8_t | // --- Lecture dynamique du flux (chunked safe) --- | ||
std::vector<uint8_t> buf; | |||
unsigned long t0 = millis(); | |||
while (https.connected()) { | |||
while (stream->available()) { | |||
buf.push_back(stream->read()); | |||
} | |||
if (millis() - t0 > 10000) { | |||
Serial.println("⏱ Timeout lecture !"); | |||
break; | |||
} | |||
delay(1); | |||
} | } | ||
int index = buf.size(); | |||
int index = | |||
Serial.printf("Téléchargé %d octets\n", index); | Serial.printf("Téléchargé %d octets\n", index); | ||
// | // --- Vérification du magic number PNG --- | ||
if (index >= 8) { | |||
Serial.print("Magic number début : "); | |||
for (int i = 0; i < 8; i++) Serial.printf("%02X ", buf[i]); | |||
Serial.println(); | |||
Serial.print("Magic number fin : "); | |||
for (int i = max(0, index - 8); i < index; i++) Serial.printf("%02X ", buf[i]); | |||
Serial.println(); | |||
} | } | ||
// | // --- Vérifier IEND --- | ||
bool hasIEND = false; | |||
if (index >= 8) { | |||
uint8_t tail[8]; | |||
for (int i = 0; i < 8; i++) tail[i] = buf[index - 8 + i]; | |||
hasIEND = (tail[0] == 0x49 && tail[1] == 0x45 && tail[2] == 0x4E && | |||
tail[3] == 0x44 && tail[4] == 0xAE && tail[5] == 0x42 && | |||
tail[6] == 0x60 && tail[7] == 0x82); | |||
} | } | ||
Serial.println(); | if (!hasIEND) Serial.println("⚠️ PNG incomplet (IEND manquant)"); | ||
// | // --- Calcul checksum simple --- | ||
int rc = png.openRAM(buf, index, pngDraw); | uint32_t checksum = 0; | ||
for (uint8_t c : buf) checksum += c; | |||
Serial.printf("Checksum buffer: %u\n", checksum); | |||
// --- Décodage PNG --- | |||
int rc = png.openRAM(buf.data(), index, pngDraw); | |||
Serial.printf("Résultat openRAM = %d\n", rc); | Serial.printf("Résultat openRAM = %d\n", rc); | ||
if (rc == PNG_SUCCESS) { | if (rc == PNG_SUCCESS) { | ||
Serial.println("✅ PNG ouvert avec succès"); | |||
tft.startWrite(); | tft.startWrite(); | ||
tft.fillScreen(TFT_BLACK); | tft.fillScreen(TFT_BLACK); | ||
png.decode(NULL, 0); | int decRes = png.decode(NULL, 0); | ||
tft.endWrite(); | tft.endWrite(); | ||
Serial. | Serial.printf("Résultat decode() = %d\n", decRes); | ||
} else { | } else { | ||
Serial. | Serial.println("❌ Erreur PNGdec :"); | ||
switch (rc) { | |||
case PNG_MEM_ERROR: Serial.println(" -> Erreur mémoire"); break; | |||
case PNG_INVALID_PARAMETER: Serial.println(" -> Paramètres invalides"); break; | |||
case PNG_INVALID_FILE: Serial.println(" -> Fichier PNG invalide ou corrompu"); break; | |||
Serial. | case PNG_TOO_BIG: Serial.println(" -> PNG trop grand"); break; | ||
default: Serial.printf(" -> Code inconnu (%d)\n", rc); break; | |||
} | } | ||
} | } | ||
https.end(); | https.end(); | ||
png.close(); | png.close(); | ||
Serial.printf(" | Serial.printf("Heap après libération: %d\n", ESP.getFreeHeap()); | ||
} | } | ||
| Ligne 142 : | Ligne 188 : | ||
Serial.println(WiFi.localIP()); | Serial.println(WiFi.localIP()); | ||
tft.setTextDatum(MC_DATUM); | tft.setTextDatum(MC_DATUM); | ||
tft.drawString("Chargement carte...", tft.width()/2, tft.height()/2); | tft.drawString("Chargement carte...", tft.width()/2, tft.height()/2); | ||
| Ligne 158 : | Ligne 194 : | ||
} | } | ||
void loop() { | void loop() {} | ||
</pre> | </pre> | ||
Et la sortie de la console, | Et la sortie de la console, | ||
<pre> | <pre> | ||
Connexion WiFi à 2.4GHz-MEMOIRE-VIVE...... | |||
WiFi connecté ! | WiFi connecté ! | ||
192.168.1.221 | 192.168.1.221 | ||
Heap avant téléchargement: 183556 | |||
https://tile.openstreetmap.org/15/16614/11304.png | https://tile.openstreetmap.org/15/16614/11304.png | ||
Téléchargé | ⏱ Timeout lecture ! | ||
Téléchargé 9638 octets | |||
Magic number: 89 50 4E 47 0D 0A 1A 0A | Magic number début : 89 50 4E 47 0D 0A 1A 0A | ||
Magic number fin : 49 45 4E 44 AE 42 60 82 | |||
Checksum buffer: 1171860 | |||
Résultat openRAM = 0 | Résultat openRAM = 0 | ||
✅ PNG ouvert avec succès | |||
</pre> | === Début du rendu PNG === | ||
Dimensions PNG : 256 x 256 | |||
Offset calculé : (112, 32) | |||
Heap libre avant rendu : 120428</pre> | |||
Version du 15 octobre 2025 à 09:30
L'objectif serait de pouvoir afficher un fond de carte sur un module ESP32 écran, par exemple pour suivre des positions GPS.
Pour l'instant il ne s'agit que de la partie affichage, et qui ne fonctionne. Le test porte sur la récupération d'une tuile (un morceau de carte), c'est à dire d'un fichier d'une image en format PNG. La récupération et l'affichage se font bien. C'est la bibliothèque PNGdec qui est utilisée.
Exemple d'URL de récupération de tuile :
https://tile.openstreetmap.org/15/16614/11304.png
Voici le code, à ce stade,
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <TFT_eSPI.h>
#include <PNGdec.h>
const char* ssid = "2.4GHz-MEMOIRE-VIVE";
const char* password = "memoirevive79";
TFT_eSPI tft = TFT_eSPI();
PNG png;
// === Callback appelé par PNGdec pour chaque ligne de pixels ===
int pngDraw(PNGDRAW *pDraw) {
static int offsetX = 0;
static int offsetY = 0;
if (pDraw->y == 0) {
int width = pDraw->iWidth;
int height = png.getHeight();
offsetX = (tft.width() - width) / 2;
offsetY = (tft.height() - height) / 2;
Serial.printf("\n=== Début du rendu PNG ===\n");
Serial.printf("Dimensions PNG : %d x %d\n", width, height);
Serial.printf("Offset calculé : (%d, %d)\n", offsetX, offsetY);
Serial.printf("Heap libre avant rendu : %d\n", ESP.getFreeHeap());
}
if (pDraw->y % 10 == 0)
Serial.printf("Décodage ligne %d / %d\n", pDraw->y, png.getHeight());
if (pDraw->iWidth > 512) {
Serial.printf("⚠️ Ligne trop large (%d px)\n", pDraw->iWidth);
return 0;
}
static uint16_t lineBuffer[512];
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_LITTLE_ENDIAN, 0xFFFF);
if (pDraw->y < 3) {
Serial.printf("Premiers pixels ligne %d : ", pDraw->y);
for (int i = 0; i < min(4, pDraw->iWidth); i++)
Serial.printf("%04X ", lineBuffer[i]);
Serial.println();
}
tft.pushImage(offsetX, offsetY + pDraw->y, pDraw->iWidth, 1, lineBuffer);
if (pDraw->y == png.getHeight() - 1) {
Serial.printf("=== Fin du rendu PNG ===\n");
Serial.printf("Heap libre après rendu : %d\n", ESP.getFreeHeap());
}
return 1;
}
// --- Télécharge et affiche une tuile OSM avec debug complet ---
void showTile(int zoom, int x, int y) {
Serial.printf("Heap avant téléchargement: %d\n", ESP.getFreeHeap());
WiFiClientSecure client;
client.setInsecure();
HTTPClient https;
char url[128];
sprintf(url, "https://tile.openstreetmap.org/%d/%d/%d.png", zoom, x, y);
Serial.println(url);
if (!https.begin(client, url)) {
Serial.println("Erreur HTTPS.begin()");
return;
}
https.addHeader("User-Agent", "Mozilla/5.0 (ESP32)");
int httpCode = https.GET();
if (httpCode != HTTP_CODE_OK) {
Serial.printf("Erreur HTTP: %d\n", httpCode);
Serial.printf("Réponse: %s\n", https.getString().c_str());
https.end();
return;
}
WiFiClient *stream = https.getStreamPtr();
// --- Lecture dynamique du flux (chunked safe) ---
std::vector<uint8_t> buf;
unsigned long t0 = millis();
while (https.connected()) {
while (stream->available()) {
buf.push_back(stream->read());
}
if (millis() - t0 > 10000) {
Serial.println("⏱ Timeout lecture !");
break;
}
delay(1);
}
int index = buf.size();
Serial.printf("Téléchargé %d octets\n", index);
// --- Vérification du magic number PNG ---
if (index >= 8) {
Serial.print("Magic number début : ");
for (int i = 0; i < 8; i++) Serial.printf("%02X ", buf[i]);
Serial.println();
Serial.print("Magic number fin : ");
for (int i = max(0, index - 8); i < index; i++) Serial.printf("%02X ", buf[i]);
Serial.println();
}
// --- Vérifier IEND ---
bool hasIEND = false;
if (index >= 8) {
uint8_t tail[8];
for (int i = 0; i < 8; i++) tail[i] = buf[index - 8 + i];
hasIEND = (tail[0] == 0x49 && tail[1] == 0x45 && tail[2] == 0x4E &&
tail[3] == 0x44 && tail[4] == 0xAE && tail[5] == 0x42 &&
tail[6] == 0x60 && tail[7] == 0x82);
}
if (!hasIEND) Serial.println("⚠️ PNG incomplet (IEND manquant)");
// --- Calcul checksum simple ---
uint32_t checksum = 0;
for (uint8_t c : buf) checksum += c;
Serial.printf("Checksum buffer: %u\n", checksum);
// --- Décodage PNG ---
int rc = png.openRAM(buf.data(), index, pngDraw);
Serial.printf("Résultat openRAM = %d\n", rc);
if (rc == PNG_SUCCESS) {
Serial.println("✅ PNG ouvert avec succès");
tft.startWrite();
tft.fillScreen(TFT_BLACK);
int decRes = png.decode(NULL, 0);
tft.endWrite();
Serial.printf("Résultat decode() = %d\n", decRes);
} else {
Serial.println("❌ Erreur PNGdec :");
switch (rc) {
case PNG_MEM_ERROR: Serial.println(" -> Erreur mémoire"); break;
case PNG_INVALID_PARAMETER: Serial.println(" -> Paramètres invalides"); break;
case PNG_INVALID_FILE: Serial.println(" -> Fichier PNG invalide ou corrompu"); break;
case PNG_TOO_BIG: Serial.println(" -> PNG trop grand"); break;
default: Serial.printf(" -> Code inconnu (%d)\n", rc); break;
}
}
https.end();
png.close();
Serial.printf("Heap après libération: %d\n", ESP.getFreeHeap());
}
void setup() {
Serial.begin(115200);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
Serial.printf("Connexion WiFi à %s...", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connecté !");
Serial.println(WiFi.localIP());
tft.setTextDatum(MC_DATUM);
tft.drawString("Chargement carte...", tft.width()/2, tft.height()/2);
showTile(15, 16614, 11304);
}
void loop() {}
Et la sortie de la console,
Connexion WiFi à 2.4GHz-MEMOIRE-VIVE...... WiFi connecté ! 192.168.1.221 Heap avant téléchargement: 183556 https://tile.openstreetmap.org/15/16614/11304.png ⏱ Timeout lecture ! Téléchargé 9638 octets Magic number début : 89 50 4E 47 0D 0A 1A 0A Magic number fin : 49 45 4E 44 AE 42 60 82 Checksum buffer: 1171860 Résultat openRAM = 0 ✅ PNG ouvert avec succès === Début du rendu PNG === Dimensions PNG : 256 x 256 Offset calculé : (112, 32) Heap libre avant rendu : 120428