我有带凸轮的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图像渲染的代码吗?
发射机的草图-
#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()
{
}
答案 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,首先我们需要在TFT上解码并创建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("=====================================");
}