ESP32-CAM Project: ESP32-CAM with TFT to display picture (ST7735)

This ESP32-CAM Project covers how to use ESP32-CAM with a TFT display to show the picture captured by the cam. We have covered several times how to use ESP32-CAM in different projects and we have described how to use ESP32-CAM in Machine Learning projects. Even if we can use ESP32-CAM with a Web server to show pictures, in this post we want to cover how to show a picture on a TFT screen (ST7735). Therefore, we would like to visualize the picture taken by the ESP32-CAM directly on the display. In this case, we use an ST7735s display, anyway, you can select a different TFT if you like.

ESP32-CAM with TFT project overview

To build this project you will need the following components:

  • ESP32-CAM
  • TFT display (ST7735s)
  • Button (to take the picture)
  • FTDI232 to program the ESP32-CAM

The project works in this way:

  • Using the button we take the picture (as it happens in a real camera)
  • as soon as the picture is taken, the ESP32 displays it on the TFT

How to connect ESP32-CAM to TFT display

This is ESP32-cam with TFT schematic:

ESP32-CAM with TFT display to show the picture

This table shows the ESP32-CAM Pins and the ST7735s Pins:

ESP32-CAMTFT (ST7735s)
13MOSI
14CLK
15CS
2DC
12RESET

Display picture from ESP32-CAM to TFT display

This is the source:

#include <Arduino.h> #include <Wire.h> #include <SPI.h> #include <Adafruit_GFX.h> // Core graphics library #include <Adafruit_ST7735.h> // Hardware-specific library for ST7735 #include <TJpg_Decoder.h> #include "esp_camera.h" #define CAMERA_MODEL_AI_THINKER // Has PSRAM #include "camera_pins.h" #define TFT_MOSI 13 #define TFT_SCLK 14 #define TFT_CS 15 // Chip select control pin #define TFT_DC 2 // Data Command control pin #define TFT_RST 12 #define PIN_BTN 4 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST); bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) { // Stop further decoding as image is running off bottom of screen if ( y >= tft.height() ) return 0; tft.drawRGBBitmap(x, y, bitmap, w, h); // Return 1 to decode next block return 1; } void init_camera() { camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; // if PSRAM IC present, init with UXGA resolution and higher JPEG quality // for larger pre-allocated frame buffer. if(psramFound()){ config.frame_size = FRAMESIZE_QQVGA; config.jpeg_quality = 10; config.fb_count = 1; Serial.println("PSRAM"); } else { config.frame_size = FRAMESIZE_QQVGA; config.jpeg_quality = 12; config.fb_count = 1; } #if defined(CAMERA_MODEL_ESP_EYE) pinMode(13, INPUT_PULLUP); pinMode(14, INPUT_PULLUP); #endif // camera init esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } sensor_t * s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated if (s->id.PID == OV3660_PID) { Serial.println("PID"); s->set_vflip(s, 1); // flip it back s->set_brightness(s, 2); // up the brightness just a bit s->set_saturation(s, 0); } } void setup() { Serial.begin(9600); Serial.println("ESP32-CAM Picture"); tft.initR(INITR_BLACKTAB); tft.setRotation(1); tft.fillScreen(ST77XX_GREEN); init_camera(); pinMode(PIN_BTN, INPUT); TJpgDec.setJpgScale(1); // The decoder must be given the exact name of the rendering function above TJpgDec.setCallback(tft_output); } void take_picture(){ Serial.println("Taking picture.."); camera_fb_t * fb = NULL; fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); } uint16_t w = 0, h = 0; TJpgDec.getJpgSize(&w, &h, fb->buf, fb->len); Serial.print("- Width = "); Serial.print(fb->width); Serial.print(", height = "); Serial.println(fb->height); Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h); // Draw the image, top left at 0,0 TJpgDec.drawJpg(0, 0, fb->buf, fb->len); } void loop() { while (true) { int state = digitalRead(PIN_BTN); if (state == HIGH) { Serial.println("Button pressed"); take_picture(); } delay(200); } }
Code language: C++ (cpp)

If you use PlatformIO to develop this project, this the platformio.ini:

[env:esp32cam] platform = espressif32 board = esp32cam framework = arduino lib_deps = adafruit/Adafruit GFX Library @ ^1.10.5 adafruit/Adafruit ST7735 and ST7789 Library @ ^1.6.0 adafruit/Adafruit BusIO @ ^1.7.2 bodmer/TJpg_Decoder @ ^0.2.0

Therefore we will use the following libraries:

  • Adafruit GFX Library
  • Adafruit ST7735 and ST7789
  • Adafruit BusIO
  • TJpg_Decoder (link)

You should already know how to take a picture using an ESP32-CAM therefore we will focus on two aspects only:

  • How to connect the ESP32-CAM to TFT display
  • How to show the picture on the TFT display

Initializing the TFT display

This is simple and requies only a few lines of code:

#define TFT_MOSI 13 #define TFT_SCLK 14 #define TFT_CS 15 // Chip select control pin #define TFT_DC 2 // Data Command control pin #define TFT_RST 12 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
Code language: PHP (php)

next, in the setup() method, the code initializes the display:

tft.initR(INITR_BLACKTAB); tft.setRotation(1); tft.fillScreen(ST77XX_GREEN);
Code language: C++ (cpp)

How to display the picture on the TFT

This is the most interesting part because here we will show the picture taken by the ESP32-CAM on the TFT display. To do it, we will use the TJpg_Decoder library because it simplifies our work. First of all, we use a low-resolution such as 120×160 so that the picture fits in the TFT.

The code below is derived from the examples provided with the library. In the setup() method, the code initializes the library:

TJpgDec.setJpgScale(1); // The decoder must be given the exact name of the rendering function above TJpgDec.setCallback(tft_output);
Code language: PHP (php)

defining the scale and the callback method used to render the picture:

bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) { // Stop further decoding as image is running off bottom of screen if ( y >= tft.height() ) return 0; tft.drawRGBBitmap(x, y, bitmap, w, h); // Return 1 to decode next block return 1; }
Code language: C++ (cpp)

Finally, when we take the picture, the code runs the rendering process:

uint16_t w = 0, h = 0; TJpgDec.getJpgSize(&w, &h, fb->buf, fb->len); Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h); // Draw the image, top left at 0,0 TJpgDec.drawJpg(0, 0, fb->buf, fb->len);
Code language: PHP (php)

remember that fb->buf contains the picture acquired while fb->len is the array length.

More useful resources:
How to monitor Pulse Heart rate with ESP32
How to measure CO2 with ESP32

Handling the button to take the picture

The last step is the easiest where we will handle the button:

void loop() { while (true) { int state = digitalRead(PIN_BTN); if (state == HIGH) { Serial.println("Button pressed"); take_picture(); } delay(200); } }
Code language: C++ (cpp)

Testing ESP32-CAM with TFT display

Now it is time to test the ESP32-CAM project. Let us capture a picture. The result is shown below:

ESP32-CAM picture with TFT T7735 to display the image captured

Wrapping up..

At the end of this tutorial, you have learned how to use ESP32-CAM with TFT display. In this project we have integrated ESP32-CAM with ST7735 to show the image captured. We have build a simple camera machine using ESP32-CAM.