如何将jpeg字节流转换为jpeg图像并在1.8英寸TFT显示屏上显示

时间:2019-01-14 13:15:24

标签: esp32

我有带凸轮的esp32,它正在传输压缩的jpeg流。

设备名称-TTGO T-Journal ESP32相机开发板OV2640 SMA WiFi 3dbi天线0.91 OLED相机板

我有esp32,具有1.8英寸的TFT屏幕和一个SD卡插槽,可以用作接收器。

设备名称-ESP32 TS V1.2 MPU9250 1.8英寸TFT蓝牙Wifi MicroSD卡插槽扬声器模块

在Chrome浏览器上,我将jpeg图像显示为一个接一个的图像(视频)。

我可以看到传入的压缩jpeg流,并将其打印在串行监视器上。 但是我需要在接收端对jpeg流进行解码/解压缩并绘制TFT屏幕的像素。

我缺少哪些步骤?有人可以建议在TFT屏幕上进行jpeg图像渲染的代码吗?

image1

image2

发射机的草图-

#include "OV2640.h"
#include <WiFi.h>
#include <WebServer.h>
#include <WiFiClient.h>

#define ENABLE_OLED //if want use oled ,turn on thi macro

#ifdef ENABLE_OLED
#include "SSD1306.h"
#define OLED_ADDRESS 0x3c
#define I2C_SDA 14
#define I2C_SCL 13
SSD1306Wire display(OLED_ADDRESS, I2C_SDA, I2C_SCL, GEOMETRY_128_32);
#endif

OV2640 cam;
WebServer server(80);

IPAddress apIP = IPAddress(192, 168, 1, 1);

void handle_jpg_stream(void)
{
    WiFiClient client = server.client();
    String response = "HTTP/1.1 200 OK\r\n";
response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
server.sendContent(response);

while (1)
{
    cam.run();
    if (!client.connected())
        break;
    response = "--frame\r\n";
    response += "Content-Type: image/jpeg\r\n\r\n";
    server.sendContent(response);

    client.write((char *)cam.getfb(), cam.getSize());
    server.sendContent("\r\n");
    if (!client.connected())
        break;
    }
}

void handle_jpg(void)
{
    WiFiClient client = server.client();

    cam.run();
    if (!client.connected())
    {
        return;
    }
     String response = "HTTP/1.1 200 OK\r\n";
     response += "Content-disposition: inline; filename=capture.jpg\r\n";
    response += "Content-type: image/jpeg\r\n\r\n";
    server.sendContent(response);
    client.write((char *)cam.getfb(), cam.getSize());
}

void handleNotFound()
{
    String message = "Server is running!\n\n";
    message += "URI: ";
    message += server.uri();
    message += "\nMethod: ";
    message += (server.method() == HTTP_GET) ? "GET" : "POST";
   message += "\nArguments: ";
   message += server.args();
   message += "\n";
   server.send(200, "text/plain", message);
}

void setup()
{
    Serial.begin(115200);
    while (!Serial)
    {
        ;
     }
camera_config_t camera_config;
camera_config.ledc_channel = LEDC_CHANNEL_0;
camera_config.ledc_timer = LEDC_TIMER_0;
camera_config.pin_d0 = 17;
camera_config.pin_d1 = 35;
camera_config.pin_d2 = 34;
camera_config.pin_d3 = 5;
camera_config.pin_d4 = 39;
camera_config.pin_d5 = 18;
camera_config.pin_d6 = 36;
camera_config.pin_d7 = 19;
camera_config.pin_xclk = 27;
camera_config.pin_pclk = 21;
camera_config.pin_vsync = 22;
camera_config.pin_href = 26;
camera_config.pin_sscb_sda = 25;
camera_config.pin_sscb_scl = 23;
camera_config.pin_reset = 15;
camera_config.xclk_freq_hz = 20000000;
camera_config.pixel_format = CAMERA_PF_JPEG;
camera_config.frame_size = CAMERA_FS_SVGA;

cam.init(camera_config);

WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
bool result = WiFi.softAP("TTGO-CAMERA", "12345678", 1, 0);
if (!result)
{
    Serial.println("AP Config failed.");
    return;
}
else
{
    Serial.println("AP Config Success.");
    Serial.print("AP MAC: ");
    Serial.println(WiFi.softAPmacAddress());
}

#ifdef ENABLE_OLED
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(128 / 2, 32 / 2, WiFi.softAPIP().toString());
display.display();
#endif

server.on("/", HTTP_GET, handle_jpg_stream);
server.on("/jpg", HTTP_GET, handle_jpg);
server.onNotFound(handleNotFound);
server.begin();
}

void loop()
{
    server.handleClient();
}

接收者的草图-

#include <WiFi.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Adafruit_ImageReader.h> // Image-reading functions
//#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <SPI.h>
#include "WiFi.h"
#include <JPEGDecoder.h>
//#include <FileIO.h>




// For the breakout, you can use any 2 or 3 pins
// These pins will also work for the 1.8" TFT shield
#define TFT_CS 16
#define TFT_RST 9  // you can also connect this to the Arduino reset
                  // in which case, set this #define pin to -1!
#define TFT_DC 17

// Option 2: use any pins but a little slower!
#define TFT_SCLK 5   // set these to be whatever pins you like!
#define TFT_MOSI 23   // set these to be whatever pins you like!
#define PIN_NUM_MISO 2
#define PIN_NUM_MOSI 15
#define PIN_NUM_CLK  14
#define PIN_NUM_CS   13



Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK,     TFT_RST);

String text="";


const char* ssid     = "TTGO-CAMERA";
const char* password = "12345678";
const char* host     = "192.168.1.1";
const char* url      = "/";

IPAddress local_IP(192, 168, 1, 3);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8); //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

//PImage logo;


#define BUFFPIXEL 20
  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 buffer (R+G+B 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;               // pixel color for tft
  File     bmpFile; File     bmpFile1;
  uint32_t pos = 0, startTime ;
  uint8_t xx=0; uint16_t yy=0; int ww=0; int hh=0;
  int bmpfilepos;

//How to use -
//tft.fillScreen(ST7735_BLACK);
//testdrawtext(2,3,ST7735_GREEN,text);



void testdrawtext( int leftoffset , int topoffset , uint16_t color ,  String text ) {
  tft.setCursor(leftoffset, topoffset);  // (offset from left margin , offset from top margin. )
  tft.setTextColor(color);
  tft.setTextWrap(true);
  tft.setTextSize(1);
  tft.print(text  ); 
    }




void jpegbytesdisplay()
{
 // Create a buffer for the packet
    char dataBuff[240];

    delay(5000);

  Serial.print("connecting to ");
  Serial.println(host);

  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  int cnt=0;

  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

  Serial.print("Requesting URL: ");
  Serial.println(url);

  // This will send the request to the cam server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
           "Host: " + host + "\r\n" +
           "Connection: close\r\n\r\n");
  unsigned long timeout = millis();



  while (client.available() == 0) 
  {
    if (millis() - timeout > 5000) 
    {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return;
    }
  }


   int max_y=200;
   int ypos =200;
   int max_x=0 ;
   int xpos =0;

  // Read all the lines of the reply from server and print them to Serial

  ImageReturnCode stat;
  while (client.available()) 
  {
    //================================
   // String line = client.readStringUntil('\r');
   // Serial.print(line);


    //================================
    //bytes put into char[len] databuffer.
    size_t len=client.available();
    unsigned char buf[len];
    client.readBytes(buf, len);

    Serial.println("------------------------");
    Serial.println(len);
    //Serial.write(buf);

     //================================
     // bufffer to tft screen pixel rendering

      int col=0;     
  int buffidx =0;
  cnt++;

    tft.fillScreen(ST7735_BLACK);
    tft.startWrite();
    tft.setAddrWindow(0, 0, 500, 500);


String fname="/image2" + String(cnt) + ".jpg";
File myFile = SD.open(fname, FILE_WRITE);
myFile.write(buf,len);
myFile.close();  

     for (col=0; col<len; col++) 
       { 
          Serial.print(buf[col]);
       }
    Serial.println();
    Serial.println("------------------------");


      // For each pixel...
     for (col=0; col<len; col++) 
     { 
         if (buffidx >= sizeof(buf)) 
              {  
          buffidx = 0; // Set index to beginning
                break;
        }


        b = buf[buffidx++]; //          [B]
        g = buf[buffidx++]; //     [G]
        r = buf[buffidx++]; //   [R]



        tft.pushColor(tft.color565(r,g,b));   
                             // Display pixel on tft screen one after another.



   } // end pixel loop.

    //================================

   delay(1000);
    tft.endWrite();    
  }

    }







 void setupNormal()
{


   Serial.begin(115200);


   // Use this initializer if you're using a 1.8" TFT
  tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab

  Serial.println("Initialized");


  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("STA Failed to configure");
  }

  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }


  tft.fillScreen(ST7735_BLACK);

}


void setup()
{

    setupNormal();
    jpegbytesdisplay();


      }



void loop()
{



  }

1 个答案:

答案 0 :(得分:0)

这是我在任何TFT上显示JPEG图像文件的结果-

我使用JPEGDecoder库,而不是tft.pushColor,而是使用tft.drawPixel方法。

  for (int jj  =mcu_y;jj<mcu_y + win_h;jj++)
     {
        for (int ii=mcu_x;ii<mcu_x + win_w ;ii++)
        {
            // TFTscreen.drawPixel(ii,jj,tft.color565(255,0,0));
             TFTscreen.drawPixel(ii,jj,*pImg++ ); 
        }
     } 

对于BMP,我们需要rgb值以在TFT屏幕上渲染每个像素。 但是对于JPG,首先我们需要在T​​FT上解码并创建MCU切片 然后,我们用从JPEG流解码器接收到的颜色绘制那些MCU瓦片。 * pImg是保存颜色信息的指针。 随着我们增加* pImg ++数组,每次我们收到uInt8_t类型的新颜色代码。在MCU磁贴内,必须注入这些颜色。 考虑到TFT屏幕的边界线在哪里,每个MCU块都按照给定的顺序着色和绘制。 它会自动创建JPEG图像。

//////////////////////////////////////////////////////////////
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <Adafruit_ImageReader.h> // Image-reading functions

// For the breakout, you can use any 2 or 3 pins
// These pins will also work for the 1.8" TFT shield
#define TFT_CS 16
#define TFT_RST 9  // you can also connect this to the Arduino reset
                      // set these to be whatever pins you like!
// in which case, set this #define pin to -1!
#define TFT_DC 17

// Option 2: use any pins but a little slower!
#define TFT_SCLK 5   // set these to be whatever pins you like!
#define TFT_MOSI 23   

Adafruit_ST7735 TFTscreen = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

//////////////////////////////////////////////////////////////

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

//////////////////////////////////////////////////////////////
// include the necessary libraries
#include <SPI.h>
#include <SD.h>
//////////////////////////////////////////////////////////////


// pin definition for the Mega
#define sd_cs  53
#define lcd_cs 49
#define dc     48
#define rst    47

#define TFT_WHITE 0xFFFF
#define TFT_BLACK 0x0000
#define TFT_RED   0xF800


 //////////////////////////////////////////////////////////////

 #define PIN_NUM_MISO 2
#define PIN_NUM_MOSI 15
#define PIN_NUM_CLK  14
#define PIN_NUM_CS   13



  Adafruit_ImageReader reader;
    ///////////////////////////////////////////////////////





String text="";

// this function determines the minimum of two numbers
#define minimum(a,b)     (((a) < (b)) ? (a) : (b))



  //====================================================================================
    //   setup
    //====================================================================================

void setup() {
  // initialize the GLCD and show a message
  // asking the user to open the serial line
  //////////////////////////////////////////////////////////////////  
   // Use this initializer if you're using a 1.8" TFT
  TFTscreen.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab
//////////////////////////////////////////////////////////////////

  TFTscreen.fillScreen(TFT_WHITE); // Alternative: 

TFTscreen.background(255, 255, 255);



TFTscreen.setTextColor(TFT_RED); // Alternative: TFTscreen.stroke(0, 0, 255);
  TFTscreen.println();
  TFTscreen.println(F("Arduino TFT Jpeg Example"));

  TFTscreen.setTextColor(TFT_BLACK); // Alternative: TFTscreen.stroke(0, 0, 0);
  TFTscreen.println(F("Open serial monitor"));
  TFTscreen.println(F("to run the sketch"));

  // initialize the serial port: it will be used to
  // print some diagnostic info
  Serial.begin(115200);
  while (!Serial) {
    // wait for serial port to connect. Needed for native USB port only
  }

  // clear the GLCD screen before starting
//  TFTscreen.background(255, 255, 255);
  TFTscreen.fillScreen(TFT_WHITE);

  // try to access the SD card. If that fails (e.g.
  // no card present), the setup process will stop.
  Serial.print(F("Initializing SD card..."));

//////////////////////////////////////////////////////////////////


  SPI.begin(PIN_NUM_CLK, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CS);

  if (!SD.begin(PIN_NUM_CS)) {
    Serial.println(F("failed!"));
    while (1); // SD initialisation failed so wait here
  }
  Serial.println(F("OK!"));
//////////////////////////////////////////////////////////////////


  // initialize and clear the GLCD screen
 // TFTscreen.begin();
  TFTscreen.fillScreen(TFT_WHITE); // Alternative: TFTscreen.background(255, 255, 255);

  // now that the SD card can be accessed, check the
  // image file exists.
  if (SD.exists("/EagleEye.jpg")) {
    Serial.println("EagleEye.jpg found on SD card.");
  } else {
    Serial.println("EagleEye.jpg not found on SD card.");
    while (1); // Image file missing so stay here
  }

}

//====================================================================================
//   Main loop
//====================================================================================
void loop() {

  // open the image file
  File jpgFile = SD.open( "/EagleEye.jpg", FILE_READ);

  // initialise the decoder to give access to image information
  JpegDec.decodeSdFile(jpgFile);

  // print information about the image to the serial port
  jpegInfo();

  // render the image onto the screen at coordinate 0,0
  renderJPEG(0, 0);

  // wait a little bit before clearing the screen to random color and drawing again
  delay(4000);

  // clear screen
  TFTscreen.fillScreen(random(0xFFFF));  // Alternative: TFTscreen.background(255, 255, 255);
}

//====================================================================================
//   Print information about the image
//====================================================================================

void jpegInfo() {
  Serial.println(F("==============="));
  Serial.println(F("JPEG image info"));
  Serial.println(F("==============="));
  Serial.print(F(  "Width      :")); Serial.println(JpegDec.width);
  Serial.print(F(  "Height     :")); Serial.println(JpegDec.height);
  Serial.print(F(  "Components :")); Serial.println(JpegDec.comps);
  Serial.print(F(  "MCU / row  :")); Serial.println(JpegDec.MCUSPerRow);
  Serial.print(F(  "MCU / col  :")); Serial.println(JpegDec.MCUSPerCol);
  Serial.print(F(  "Scan type  :")); Serial.println(JpegDec.scanType);
  Serial.print(F(  "MCU width  :")); Serial.println(JpegDec.MCUWidth);
  Serial.print(F(  "MCU height :")); Serial.println(JpegDec.MCUHeight);
  Serial.println(F("==============="));
}

//====================================================================================
//   Decode and paint onto the TFT screen
//====================================================================================
void renderJPEG(int xpos, int ypos) {

  // retrieve infomration about the image
  uint16_t *pImg;
  uint16_t mcu_w = JpegDec.MCUWidth;
  uint16_t mcu_h = JpegDec.MCUHeight;
  uint32_t max_x = JpegDec.width;
  uint32_t max_y = JpegDec.height;

  // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
  // Typically these MCUs are 16x16 pixel blocks
  // Determine the width and height of the right and bottom edge image blocks
  uint32_t min_w = minimum(mcu_w, max_x % mcu_w);
  uint32_t min_h = minimum(mcu_h, max_y % mcu_h);

  // save the current image block size
  uint32_t win_w = mcu_w;
  uint32_t win_h = mcu_h;

  // record the current time so we can measure how long it takes to draw an image
  uint32_t drawTime = millis();

  // save the coordinate of the right and bottom edges to assist image cropping
  // to the screen size
  max_x += xpos;
  max_y += ypos;

   char str[100];
  // read each MCU block until there are no more
  while ( JpegDec.read()) {

    // save a pointer to the image block
    pImg = JpegDec.pImage;

    // calculate where the image block should be drawn on the screen
    int mcu_x = JpegDec.MCUx * mcu_w + xpos;
    int mcu_y = JpegDec.MCUy * mcu_h + ypos;

    // check if the image block size needs to be changed for the right and bottom edges
    if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
    else win_w = min_w;
    if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
    else win_h = min_h;

    // calculate how many pixels must be drawn
    uint32_t mcu_pixels = win_w * win_h;

    // draw image block if it will fit on the screen
    if ( ( mcu_x + win_w) <= TFTscreen.width() && ( mcu_y + win_h) <= TFTscreen.height()) {
      // open a window onto the screen to paint the pixels into
      //TFTscreen.setAddrWindow(mcu_x, mcu_y, mcu_x + win_w - 1, mcu_y + win_h - 1);
      //TFTscreen.setAddrWindow(mcu_x, mcu_y, mcu_x + win_w - 1, mcu_y + win_h - 1);
      // push all the image block pixels to the screen


 ////////////////////////////////////////////////////////

 // while (mcu_pixels--)
 // {
 // TFTscreen.pushColor(*pImg++);
 // } // Send to TFT 16 bits at a time



 ////////////////////////////////////////////////////////



 //  Write all MCU pixels to the TFT window





  for (int jj=mcu_y;jj<mcu_y + win_h;jj++)
     {
        for (int ii=mcu_x;ii<mcu_x + win_w ;ii++)
        {
            // TFTscreen.drawPixel(ii,jj,tft.color565(255,0,0));
             TFTscreen.drawPixel(ii,jj,*pImg++ ); 
        }
     } 


 ////////////////////////////////////////////////////////

    }

    // stop drawing blocks if the bottom of the screen has been reached
    // the abort function will close the file
    else if ( ( mcu_y + win_h) >= TFTscreen.height()) JpegDec.abort();

  }

  // calculate how long it took to draw the image
  drawTime = millis() - drawTime; // Calculate the time it took



 // print the results to the serial port
  Serial.print  ("Total render time was    : "); Serial.print(drawTime); Serial.println(" ms");
  Serial.println("=====================================");

}