You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32(Arduino)BMP转RGB565后LVGL显示像素错位求助

基于ESP32的BMP转RGB565图像显示错位问题

正在开展一项基于ESP32的项目,ESP32从PHP页面获取经JPG转换而来的BMP图像,随后需将该BMP图像转为RGB565格式,以通过LVGL进行显示。经测试,显示模块与LVGL本身无异常(标准图像可正常展示),但转换后的图像虽可识别,彩色像素却出现随机错位的情况,而该BMP图像在Windows系统中查看完全正常。

PHP图像获取与转换代码

<?php
// Function to make a request to the Mouser API and fetch the image
function fetchAndConvertImage() {
    // API key for accessing the Mouser API
    $apiKey = 'MY_API_KEY';
    
    // URL for making a request to the Mouser API
    $url = 'https://api.mouser.com/api/v1/search/keyword?apiKey=' . $apiKey;
    
    // Data to be sent in the request
    $requestData = array(
        'SearchByKeywordRequest' => array(
            'keyword' => 'Thermistor',
            'records' => 0,
            'startingRecord' => 0,
            'searchOptions' => 'string',
            'searchWithYourSignUpLanguage' => 'en'
        )
    );
    
    // Encode the data in JSON format
    $jsonData = json_encode($requestData);
    
    // Set up the options for the POST request
    $options = array(
        'http' => array(
            'method' => 'POST',
            'header' => 'Content-Type: application/json',
            'content' => $jsonData
        )
    );
    
    // Create a context for the request
    $context = stream_context_create($options);
    
    // Make the request to the Mouser API
    $response = file_get_contents($url, false, $context);
    
    // Decode the JSON response
    $responseData = json_decode($response, true);
    
    // Extract the image path from the response
    $imagePath = $responseData['SearchResults']['Parts'][0]['ImagePath'];
    
    // Read the image data from the specified URL
    $imageData = file_get_contents("$imagePath");
    
    // Generate a unique file name
    $fileName = uniqid('image_', true) . '.bmp';
    $filePath = __DIR__ . '/' . $fileName;
    
    // Get image dimensions
    $image = imagecreatefromstring($imageData);
    $width = imagesx($image);
    $height = imagesy($image);
    
    // Calculate row size including padding
    $rowSize = floor(($width * 3 + 3) / 4) * 4; // Each pixel is 3 bytes (RGB), and we need to ensure each row is a multiple of 4 bytes
    
    // BMP header
    $bmpHeader = pack('c', 0x42) . pack('c', 0x4D); // Signature BM
    $bmpHeader .= pack('V', 14 + 40 + ($rowSize * $height)); // File size
    $bmpHeader .= pack('v', 0) . pack('v', 0); // Reserved
    $bmpHeader .= pack('V', 14 + 40); // Data offset
    $bmpHeader .= pack('V', 40); // Header size
    $bmpHeader .= pack('V', $width); // Image width
    $bmpHeader .= pack('V', $height); // Image height
    $bmpHeader .= pack('v', 1); // Number of planes
    $bmpHeader .= pack('v', 24); // Bits per pixel
    $bmpHeader .= pack('V', 0); // Compression (0 = no compression)
    $bmpHeader .= pack('V', 0); // Image size (can be 0 when no compression)
    $bmpHeader .= pack('V', 0); // Horizontal resolution (pixels per meter)
    $bmpHeader .= pack('V', 0); // Vertical resolution (pixels per meter)
    $bmpHeader .= pack('V', 0); // Number of colors in the palette
    $bmpHeader .= pack('V', 0); // Number of important colors
    
    // Convert image data to BMP format
    $bmpData = '';
    for ($y = $height - 1; $y >= 0; --$y) {
        for ($x = 0; $x < $width; ++$x) {
            $rgb = imagecolorat($image, $x, $y);
            $color = imagecolorsforindex($image, $rgb);
            $bmpData .= pack('C', $color['blue']); // Blue
            $bmpData .= pack('C', $color['green']); // Green
            $bmpData .= pack('C', $color['red']); // Red
        }
        // Add padding to ensure row size is a multiple of 4 bytes
        $paddingBytes = $rowSize - ($width * 3);
        $bmpData .= str_repeat(pack('C', 0), $paddingBytes);
    }
    
    // Save BMP file
    file_put_contents($filePath, $bmpHeader . $bmpData);
    
    // Free memory
    imagedestroy($image);
    
    // Return file name
    return $fileName;
}

// Call the function to fetch and convert the image
$imagePath = fetchAndConvertImage();
echo "https://theyoungmaker.ddns.net/opencomponent/$imagePath";
?>

ESP32图像获取与转换代码

bool getImageFromURL(String url) {
  http.begin(url);
  int httpCode = http.GET();

  if (httpCode == HTTP_CODE_OK) {
    WiFiClient* stream = http.getStreamPtr();
    size_t totalLen = http.getSize();
    size_t len = totalLen;

    // Allocate buffer for the image data
    uint8_t* bmp_data = (uint8_t*)malloc(len);
    if (!bmp_data) {
      Serial.println("Failed to allocate memory for image data");
      http.end();
      return false;
    }

    // Read data into buffer
    size_t index = 0;
    while (http.connected() && (len > 0 || len == -1)) {
      size_t size = stream->available();
      if (size) {
        int c = stream->readBytes((char*)(bmp_data + index), size);
        index += c;
        if (len > 0) {
          len -= c;
        }
      }
      delay(1);
    }

    // Parse BMP header
    if (bmp_data[0] != 'B' || bmp_data[1] != 'M') {
      Serial.println("Not a valid BMP file");
      free(bmp_data);
      http.end();
      return false;
    }

    uint32_t dataOffset = *((uint32_t*)(bmp_data + 10));
    uint32_t width = *((uint32_t*)(bmp_data + 18));
    uint32_t height = *((uint32_t*)(bmp_data + 22));
    uint16_t bitsPerPixel = *((uint16_t*)(bmp_data + 28));
    uint32_t compression = *((uint32_t*)(bmp_data + 30));

    if (bitsPerPixel != 24 || compression != 0) {
      Serial.println("Unsupported BMP format");
      free(bmp_data);
      http.end();
      return false;
    }

    // Allocate buffer for the image data in 16-bit format (RGB565)
    size_t imgSize = width * height * 2;
    gui_img_nouveau_projet_png_data = (uint8_t*)malloc(imgSize);
    if (!gui_img_nouveau_projet_png_data) {
      Serial.println("Failed to allocate memory for image data");
      free(bmp_data);
      http.end();
      return false;
    }

    // Convert pixel data to 16-bit (RGB565) format
    uint8_t* src = bmp_data + dataOffset;
    uint16_t* dst = (uint16_t*)gui_img_nouveau_projet_png_data;
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        uint8_t b = *src++;
        uint8_t g = *src++;
        uint8_t r = *src++;
        *dst++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
      }
      // Skip padding bytes if any
      src += (4 - (width * 3) % 4) % 4;
    }

    // Set image descriptor data
    gui_img_nouveau_projet_png.header.always_zero = 0;
    gui_img_nouveau_projet_png.header.w = width;
    gui_img_nouveau_projet_png.header.h = height;
    gui_img_nouveau_projet_png.data_size = imgSize;
    gui_img_nouveau_projet_png.header.cf = LV_IMG_CF_TRUE_COLOR;
    gui_img_nouveau_projet_png.data = gui_img_nouveau_projet_png_data;

    free(bmp_data);
    http.end();
    return true;
  } else {
    Serial.printf("HTTP GET failed, error: %s\n", http.errorToString(httpCode).c_str());
    http.end();
    return false;
  }
}

图像对比

  • 原图像:
    原图像

  • 显示后的图像:
    显示后的图像

不确定问题出在服务器端的JPG转BMP环节,还是ESP32端的BMP转RGB565环节,恳请提供相关见解与解决建议。

内容的提问来源于stack exchange,提问作者Loic Daigle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:08:11