Jak przesłać obraz z GIGA Display Shield za pomocą WIFI do laptopa. Napisz program z wykorzystaniem #include "stm32h7xx.h"
#include "Arduino_H7_Video.h"
#include "lvgl.h"
#include "Arduino_GigaDisplayTouch.h"
Arduino_GigaDisplayTouch TouchDetector;
#include "Arduino_GigaDisplay_GFX.h"
Skopiuj, uzupełnij SSID/hasło i wgraj. Po uruchomieniu w Monitorze szeregowym zobaczysz IP. Otwórz w przeglądarce: http://IP/ (ten sam LAN).
Uwaga: Kod używa dsi_getCurrentFrameBuffer() (nagłówek dsi.h) – standardowo dostępny w rdzeniu GIGA z Arduino_H7_Video. Jeśli nie masz dsi.h, zobacz sekcję „Wariant bez dsi.h”.
#include "stm32h7xx.h"
#include "Arduino_H7_Video.h"
#include "lvgl.h"
#include "Arduino_GigaDisplayTouch.h"
Arduino_GigaDisplayTouch TouchDetector;
#include "Arduino_GigaDisplay_GFX.h"
#include <WiFi.h>
#include "dsi.h" // dostęp do bieżącego framebuffera (DSI/LTDC)
#define WIFI_SSID "Twoje_SSID"
#define WIFI_PASS "Twoje_HASLO"
#define SWAP_RGB565 0 // ustaw na 1, jeśli kolory będą nieprawidłowe (zamiana bajtów)
WiFiServer server(80);
// Ekran GIGA Display Shield (orientacja pozioma 800x480)
Arduino_H7_Video Display(800, 480, GigaDisplayShield);
// Prosty rysunek testowy (żeby było co oglądać)
void drawTest() {
Display.beginDraw();
Display.background(0, 0, 0);
Display.stroke(255, 255, 255);
Display.fill(0, 128, 255);
Display.rect(40, 40, 240, 120);
Display.fill(255, 0, 0);
Display.circle(400, 240, 80);
Display.noFill();
Display.stroke(0, 255, 0);
for (int r = 0; r <= 200; r += 10) Display.circle(650, 240, r);
Display.endDraw();
}
// Nagłówek BMP 24bpp (top-down: ujemna wysokość)
static void sendBMPHeader(WiFiClient& c, int w, int h, uint32_t imgSize) {
uint32_t fileSize = 54 + imgSize;
uint8_t hdr[54] = {0};
hdr[0] = 'B'; hdr[1] = 'M';
hdr[2] = (uint8_t)(fileSize); hdr[3] = (uint8_t)(fileSize >> 8);
hdr[4] = (uint8_t)(fileSize >> 16); hdr[5] = (uint8_t)(fileSize >> 24);
hdr[10] = 54; // offset do danych
hdr[14] = 40; // DIB header size
// szerokość
hdr[18] = (uint8_t)(w); hdr[19] = (uint8_t)(w >> 8);
hdr[20] = (uint8_t)(w >> 16); hdr[21] = (uint8_t)(w >> 24);
// wysokość ujemna = top-down
int32_t nh = -h;
hdr[22] = (uint8_t)(nh); hdr[23] = (uint8_t)(nh >> 8);
hdr[24] = (uint8_t)(nh >> 16); hdr[25] = (uint8_t)(nh >> 24);
hdr[26] = 1; hdr[28] = 24; // 1 płaszczyzna, 24 bpp
c.write(hdr, 54);
}
// Konwersja jednej linii RGB565 -> BGR888 do bufora wyjściowego
static void convertLineRGB565toBGR888(const uint16_t* src565, uint8_t* dst, int w) {
for (int x = 0; x < w; x++) {
uint16_t p = src565[x];
#if SWAP_RGB565
p = (uint16_t)((p >> 8) | (p << 8));
#endif
uint8_t r5 = (p >> 11) & 0x1F;
uint8_t g6 = (p >> 5) & 0x3F;
uint8_t b5 = p & 0x1F;
// rozszerzenie do 8 bitów (proste doszacowanie)
uint8_t r = (r5 << 3) | (r5 >> 2);
uint8_t g = (g6 << 2) | (g6 >> 4);
uint8_t b = (b5 << 3) | (b5 >> 2);
// BMP: B, G, R
int i = 3 * x;
dst[i + 0] = b;
dst[i + 1] = g;
dst[i + 2] = r;
}
}
static void handleRoot(WiFiClient& c, IPAddress ip) {
c.println("HTTP/1.1 200 OK");
c.println("Content-Type: text/html; charset=utf-8");
c.println("Connection: close");
c.println();
c.print("<!doctype html><html><head><meta charset='utf-8'>");
c.print("<title>GIGA Display Stream (BMP refresh)</title>");
c.print("<style>body{background:#111;color:#eee;font:16px sans-serif;text-align:center}img{max-width:95%;border:2px solid #444}</style>");
c.print("</head><body><h1>GIGA Display - podgląd</h1>");
c.print("<p>IP: ");
c.print(ip);
c.print("</p>");
c.print("<img id='im' src='/capture?t=0'><br>");
c.print("<label>FPS: <input id='fps' type='number' min='0.2' max='10' step='0.2' value='2'></label>");
c.print("<script>let i=document.getElementById('im');let f=document.getElementById('fps');let t;function go(){clearInterval(t);let dt=1000/Math.max(0.2,Math.min(10,parseFloat(f.value)||2));t=setInterval(()=>{i.src='/capture?t='+Date.now()},dt);}f.onchange=go;go();</script>");
c.print("</body></html>");
}
static void handleCapture(WiFiClient& c) {
const void* fbv = dsi_getCurrentFrameBuffer(); // wskaźnik na RGB565
if (!fbv) {
c.println("HTTP/1.1 503 Service Unavailable");
c.println("Connection: close"); c.println(); return;
}
int w = Display.width();
int h = Display.height();
// rozmiary BMP (wyrównanie linii do 4 bajtów)
uint32_t rowSize = w * 3;
uint32_t padding = (4 - (rowSize & 3)) & 3;
uint32_t imgSize = (rowSize + padding) * h;
c.println("HTTP/1.1 200 OK");
c.println("Content-Type: image/bmp");
c.println("Cache-Control: no-store, no-cache, must-revalidate");
c.println("Connection: close");
c.println();
sendBMPHeader(c, w, h, imgSize);
const uint16_t* fb16 = (const uint16_t*)fbv;
// bufor jednej linii BGR888
static uint8_t line[800 * 3]; // wystarczy dla w<=800
for (int y = 0; y < h; y++) {
const uint16_t* src = fb16 + (y * w);
convertLineRGB565toBGR888(src, line, w);
c.write(line, rowSize);
if (padding) {
static const uint8_t pad[3] = {0,0,0};
c.write(pad, padding);
}
// krótkie oddanie czasu WiFi (opcjonalnie)
if ((y & 0x1F) == 0) delay(1);
}
}
void setup() {
Serial.begin(115200);
Display.begin();
TouchDetector.begin();
drawTest(); // narysuj przykładową scenę
// WiFi (tryb stacji)
Serial.print("Laczenie z WiFi: "); Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nPolaczono.");
Serial.print("IP: "); Serial.println(WiFi.localIP());
server.begin();
Serial.println("Serwer HTTP uruchomiony. Otworz http://IP/ w przegladarce.");
}
void loop() {
// (opcjonalnie) prosta animacja co 1 s, aby widzieć zmianę obrazu
static uint32_t t0 = 0;
if (millis() - t0 > 1000) {
t0 = millis();
Display.beginDraw();
Display.fill(0, 0, 0);
Display.stroke(255, 255, 255);
Display.text("BMP over WiFi", 20, 20);
Display.stroke(255, 255, 0);
Display.rect(18, 16, 240, 32);
Display.endDraw();
}
WiFiClient c = server.available();
if (!c) { delay(1); return; }
// minimalny parser HTTP
String req;
uint32_t tStart = millis();
while (c.connected() && millis() - tStart < 1000) {
while (c.available()) {
char ch = c.read();
req += ch;
if (ch == '\n') {
if (req.startsWith("GET / ")) { handleRoot(c, WiFi.localIP()); }
else if (req.startsWith("GET /capture")) { handleCapture(c); }
else { c.println("HTTP/1.1 404 Not Found"); c.println("Connection: close"); c.println(); }
c.stop();
return;
}
}
}
c.stop();
}
Wariant bez dsi.h (gdyby nagłówek nie był dostępny): zamiast dsi_getCurrentFrameBuffer() utrzymuj „cień” framebuffer’a — w swoim lvgl_flush_cb kopiuj każdy prostokąt (area) do własnej tablicy RGB565 (SDRAM.malloc(8004802)). Do BMP konwertujesz z tej tablicy. Integracja LVGL z Arduino_H7_Video jest opisana w dokumentacji LVGL; przy rdzeniu GIGA ≥4.1.5 działa także z LVGL 9.x. (docs.lvgl.io)
Jeżeli chcesz, przygotuję dla Ciebie wersję MJPEG (multipart/x‑mixed‑replace) z enkoderem JPEG i ograniczeniem rozdzielczości (np. 400×240), aby osiągnąć 8–15 FPS na GIGA.