TTGO T-Journal ESP32 Camera: Built-in Programmer, OLED, Antenna and Project Examples

This is a getting started guide for the TTGO T-Journal ESP32 Camera Development Board. The TTGO T-Journal features an OV2640 camera, an OLED display, several GPIOs to connect peripherals and a built-in programmer, which makes it easy to upload code. We’ll take a quick look at the camera dev board and learn how to program it using Arduino IDE.

TTGO T-Journal ESP32 Camera: Built-in Programmer, OLED, Antenna and Project Examples

Where to Buy?

You can go to the TTGO T-Journal page on Maker Advisor to compare the board on different stores.

TTGO T-Journal ESP32 Camera + OLED +Antenna + Built-in Programmer

Introducing the TTGO T-Journal ESP32 Camera

The TTGO T-Journal is a $12-$15 ESP32 Camera Development Board with an OV2640 camera, an antenna, an I2C SSD1306 0.91 inch OLED display, some exposed GPIOs, and a micro-USB interface that makes it easy and quick to upload code to the board.

For a complete overview of this board you can watch the following video or read this article: TTGO T-Journal ESP32 Camera Dev Board Review.

TTGO T-Journal ESP32 Features

Here’s a summary of the TTGO T-Journal features:

  • Chipset ESPRESSIF-ESP32-PCIO-D4 240MHz Xtensa® single-/dual-core 32-bit LX6 microprocessor
  • FLASH QSPI flash/SRAM, up to 4 x 16 MBSRAM 520 kB SRAM
  • Reset button and button on GPIO 32
  • 0.91 inch SSD1306 OLED display
  • Power indicator red LED
  • USB to TTL CP2104 (you can upload code via USB cable);
  • Camera OV2640  2 Megapixel
  • Steering engine analog servo (comes with two sets of pins ideal to connect servos)
  • Working voltage: 2.3V-3.6V
  • Working current: about 160mA
  • Size: 64.57mm x 23.98mm

Power supply specifications: 

  • Power Supply USB 5V/1A
  • Charging current 1A
  • Battery 3.7V lithium battery

Read our in-depth review: TTGO T-Journal ESP32 Camera Development Board

TTGO T-Journal ESP32 Board Pinout

Having the right pinout for your camera board is very important. If you don’t assign the right camera pins in your code, the camera will not work. The following image shows the TTGO T-Journal ESP32 board pinout.

TTGO T-Journal ESP32 Camera Connections

TTGO T-Journal ESP32 OV2640 Camera

Here’s a table with the connections between the ESP32 and the camera:

OV2640 CameraESP32
D2GPIO 17
D3GPIO 35
D4GPIO 34
D5GPIO 5
D6GPIO 39
D7GPIO 18
D8GPIO 36
D9GPIO 19
SIOCGPIO 23
SIODGPIO 25
XCLKGPIO 27
VSYNCGPIO 22
HREFGPIO 26
PCLKGPIO 21
RSTGPIO 15
PWDNGPIO 0

So, the pin assignment in your Arduino sketch should be as follows:

#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23
#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM       5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

Because this board uses the same camera used in the ESP32-CAM board, the examples for the ESP32-CAM (that don’t use microSD card) should also work with the TTGO T-Journal by changing the pin definition. We’ll show you a couple of examples in a moment.

TTGO T-Journal ESP32 Board OLED Connections

TTGO T-Journal ESP32 Board OLED Connections Pins SCL SDA I2C

This board comes with an I2C SSD1306 0.91 inch OLED display. To interact with the display you can use the Adafruit SSD1306, the oled-ssd1306 or other compatible libraries. We usually use the Adafruit SSD1306 along with the Adafruit_GFX to interact with OLED displays.

The OLED communicates with the ESP32 using the following pins:

OLEDESP32
SDAGPIO 14
SCLGPIO 13

TTGO T-Journal ESP32 Board Control OLED Display

In this section, well show you quick tips on how to control the OLED display of the TTGO T-Journal ESP32 board.

Installing Libraries

To control the OLED display, we’ll use the Adafruit SSD1306 and Adafruit GFX libraries. These libraries can be installed through the Arduino IDE Library Manager.

In your Arduino IDE, go to Sketch > Include Library > Manage Libraries. Then, search for the library name and install it.

OLED I2C Pins and Display Size

Controlling this OLED display is similar to control a regular 0.96 OLED display connected to an ESP32. The only difference is the way you initialize the display.

You need to take into account the I2C pins used by this display (because it doesn’t use the default I2C pins), and the size of the display.

  • I2C pins:
    • SDA (GPIO 14)
    • SCL (GPIO 13)
  • Display size:
    • Width: 128 px
    • Height: 32 px

Arduino Sketch

To control the OLED display, first, you need to import the required libraries:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Define the OLED size:

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

Define the I2C pins:

#define I2C_SDA 14
#define I2C_SCL 13

Next create an Adafruit_SSD1306 object called display as follows:

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

In the setup(), you need to initialize an I2C communication on the I2C pins you’ve defined earlier as follows:

Wire.begin(I2C_SDA, I2C_SCL);

Then, initialize the OLED display as follows:

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);
}

After properly initializing the display, you can use the usual functions to write text and display shapes on the OLED. Read our OLED tutorial with the ESP32 to learn more on how to interact with the OLED display.

For testing purposes, you can upload the following code to your board. It simply displays “Hello World”.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/ttgo-t-journal-esp32-camera-getting-started/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define I2C_SDA 14
#define I2C_SCL 13

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  
  Wire.begin(I2C_SDA, I2C_SCL);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.print("Hello World!");;
  display.display();
}

void loop() {
  // put your main code here, to run repeatedly:
}

View raw code

TTGO T-Journal ESP32 Board Control OLED Display Sample Message Hello World

TTGO T-Journal ESP32 Camera Projects

We’ve modified some of our existing ESP32-CAM projects to be compatible with the TTGO T-Journal.

Video Streaming Web Server

The following code creates a video streaming web server on the camera IP address. So, you can create an IP CAM that can be integrated in Home Automation platforms like Home Assistant or Node-RED.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/ttgo-t-journal-esp32-camera-getting-started/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h"  //disable brownout problems
#include "esp_http_server.h"

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define I2C_SDA 14
#define I2C_SCL 13

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

#define PART_BOUNDARY "123456789000000000000987654321"

// OV2640 camera module pins (CAMERA_MODEL_TTGO-T-Journal)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23
#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM       5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

httpd_handle_t stream_httpd = NULL;

static esp_err_t stream_handler(httpd_req_t *req){
  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;
  size_t _jpg_buf_len = 0;
  uint8_t * _jpg_buf = NULL;
  char * part_buf[64];

  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  if(res != ESP_OK){
    return res;
  }

  while(true){
    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      res = ESP_FAIL;
    } else {
      if(fb->width > 400){
        if(fb->format != PIXFORMAT_JPEG){
          bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
          esp_camera_fb_return(fb);
          fb = NULL;
          if(!jpeg_converted){
            Serial.println("JPEG compression failed");
            res = ESP_FAIL;
          }
        } else {
          _jpg_buf_len = fb->len;
          _jpg_buf = fb->buf;
        }
      }
    }
    if(res == ESP_OK){
      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
    }
    if(fb){
      esp_camera_fb_return(fb);
      fb = NULL;
      _jpg_buf = NULL;
    } else if(_jpg_buf){
      free(_jpg_buf);
      _jpg_buf = NULL;
    }
    if(res != ESP_OK){
      break;
    }
    //Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
  }
  return res;
}

void startCameraServer(){
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 80;

  httpd_uri_t index_uri = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = stream_handler,
    .user_ctx  = NULL
  };
  
  //Serial.printf("Starting web server on port: '%d'\n", config.server_port);
  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(stream_httpd, &index_uri);
  }
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
 
  Serial.begin(115200);
  Serial.setDebugOutput(false);
  
  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(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  
  // 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;
  }
  // Wi-Fi connection
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  Serial.print("Camera Stream Ready! Go to: http://");
  Serial.print(WiFi.localIP());

  // Init OLED
  Wire.begin(I2C_SDA, I2C_SCL);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(5, 5);
  display.print(WiFi.localIP());;
  display.display();
  
  // Start streaming web server
  startCameraServer();
}

void loop() {
  delay(1);
}

View raw code

ESP32-CAM Video Streaming Web Server Example

Learn more about this project: ESP32-CAM Video Streaming Web Server

Take Photo and Display in Web Server

The following code creates a web server that you can access to take and display photos. The web server IP address is displayed on the OLED.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/ttgo-t-journal-esp32-camera-getting-started/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include <ESPAsyncWebServer.h>
#include <StringArray.h>
#include <SPIFFS.h>
#include <FS.h>

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define I2C_SDA 14
#define I2C_SCL 13

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

boolean takeNewPhoto = false;

// Photo File Name to save in SPIFFS
#define FILE_PHOTO "/photo.jpg"

// OV2640 camera module pins (CAMERA_MODEL_TTGO-T-Journal)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23
#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM       5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body { text-align:center; }
    .vert { margin-bottom: 10%; }
    .hori{ margin-bottom: 0%; }
  </style>
</head>
<body>
  <div id="container">
    <h2>ESP32-CAM Last Photo</h2>
    <p>It might take more than 5 seconds to capture a photo.</p>
    <p>
      <button onclick="rotatePhoto();">ROTATE</button>
      <button onclick="capturePhoto()">CAPTURE PHOTO</button>
      <button onclick="location.reload();">REFRESH PAGE</button>
    </p>
  </div>
  <div><img src="saved-photo" id="photo" width="70%"></div>
</body>
<script>
  var deg = 0;
  function capturePhoto() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', "/capture", true);
    xhr.send();
  }
  function rotatePhoto() {
    var img = document.getElementById("photo");
    deg += 90;
    if(isOdd(deg/90)){ document.getElementById("container").className = "vert"; }
    else{ document.getElementById("container").className = "hori"; }
    img.style.transform = "rotate(" + deg + "deg)";
  }
  function isOdd(n) { return Math.abs(n % 2) == 1; }
</script>
</html>)rawliteral";

void setup() {
  // Serial port for debugging purposes
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  if (!SPIFFS.begin(true)) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    ESP.restart();
  }
  else {
    delay(500);
    Serial.println("SPIFFS mounted successfully");
  }

  // Print ESP32 Local IP Address
  Serial.print("IP Address: http://");
  Serial.println(WiFi.localIP());

  // Init OLED
  Wire.begin(I2C_SDA, I2C_SCL);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(5, 5);
  display.print(WiFi.localIP());;
  display.display();


  // Turn-off the 'brownout detector'
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

  // OV2640 camera module
  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 (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    ESP.restart();
  }

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", index_html);
  });

  server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) {
    takeNewPhoto = true;
    request->send_P(200, "text/plain", "Taking Photo");
  });

  server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, FILE_PHOTO, "image/jpg", false);
  });

  // Start server
  server.begin();

}

void loop() {
  if (takeNewPhoto) {
    capturePhotoSaveSpiffs();
    takeNewPhoto = false;
  }
  delay(1);
}

// Check if photo capture was successful
bool checkPhoto( fs::FS &fs ) {
  File f_pic = fs.open( FILE_PHOTO );
  unsigned int pic_sz = f_pic.size();
  return ( pic_sz > 100 );
}

// Capture Photo and Save it to SPIFFS
void capturePhotoSaveSpiffs( void ) {
  camera_fb_t * fb = NULL; // pointer
  bool ok = 0; // Boolean indicating if the picture has been taken correctly

  do {
    // Take a photo with the camera
    Serial.println("Taking a photo...");

    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      return;
    }

    // Photo file name
    Serial.printf("Picture file name: %s\n", FILE_PHOTO);
    File file = SPIFFS.open(FILE_PHOTO, FILE_WRITE);

    // Insert the data in the photo file
    if (!file) {
      Serial.println("Failed to open file in writing mode");
    }
    else {
      file.write(fb->buf, fb->len); // payload (image), payload length
      Serial.print("The picture has been saved in ");
      Serial.print(FILE_PHOTO);
      Serial.print(" - Size: ");
      Serial.print(file.size());
      Serial.println(" bytes");
    }
    // Close the file
    file.close();
    esp_camera_fb_return(fb);

    // check if file has been correctly saved in SPIFFS
    ok = checkPhoto(SPIFFS);
  } while ( !ok );
}

View raw code

ESP32-CAM Take Photo and Display in Web Server

Learn more about this project: ESP32-CAM Take Photo and Display in Web Server

CameraWebServer Example

You can also run the default CameraWebServer example that comes with the Arduino IDE. In your Arduino IDE, go to File Examples ESP32 Camera and open the CameraWebServer example.

You can click the next link to download the .zip with the final code:

Otherwise, you need to add the TTGO T-Journal pinout to the camera_pins.h tab.

CameraWebServer Example ESP32-CAM for Arduino IDE

Copy the following to the camera_pins.h file.

#if defined(CAMERA_MODEL_T_JOURNAL)
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23

#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM       5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

Then, in the CameraWebServer tab, comment all the existing camera models, and add your camera, as follows:

// Select camera model
#define CAMERA_MODEL_T_JOURNAL
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
//#define CAMERA_MODEL_AI_THINKER

Because this camera doesn’t have PSRAM, the face recognition and detection features of this project don’t work with this camera. All the other functionalities work well.

CameraWebServer Example ESP32-CAM for Arduino IDE Web Server

Upload Code to the TTGO T-Journal ESP32 Camera

To upload code, you just need to connect the board to your computer, then in the Arduino IDE, go to Tools > Port and select the COM port it is connected to.

Then, you also need to select a Board model. The TTGO T-Journal is not available on the ESP32 models. So, select the following settings:

  • Board: “ESP32 Wrover Module”
  • Partition Scheme: “Huge APP (3MB No OTA)”
Board ESP32 Wrover Module and Partition Scheme Huge APP (3MB No OTA)

Then, simply click the Arduino IDE upload button and it is done!

Arduino IDE Compile and upload sketch button

Learn how to program and build 17 projects with the ESP32-CAM using Arduino IDE DOWNLOAD »

Learn how to program and build 17 projects with the ESP32-CAM using Arduino IDE DOWNLOAD »

Wrapping Up

This tutorial was a quick getting started guide for the TTGO T-Journal ESP32 Camera Development board. You’ve learned how to control the OLED display and how to adapt existing ESP32 camera projects to your board.

We hope you’ve found this tutorial useful. To learn more about this board, you can read our complete overview on Maker Advisor as well as our ESP32-CAM development boards comparison post:

You may also be interested in other ESP32 development boards:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

48 thoughts on “TTGO T-Journal ESP32 Camera: Built-in Programmer, OLED, Antenna and Project Examples”

  1. HI,
    Thanks a lot for this project.
    Very interesting. I’ll compare it with the AiThinker one.

    But I’ve a (stupid perhaps) question.
    What does “TTGO” mean?
    As exemple: TTGo Lora … and Lora …
    Thanks again.

    Reply
  2. Great post!, something I’ve been waiting for for some time since I got tired of running the sample code 🙂

    Couple of thoughts:
    1. Need not be an either/or since both boards are cheap enough. Thinking of using the ESP32-CAM for unlocking a door through face recognition, which the T-Journal doesn’t have. The T-Journal, OTOH, in conjunction with a RCWL-0516 for motion detection, can take a photo of potential intruders and send it through sftp to a Beaglebone Black.
    2. Can’t you define the T-Journal pins in a .h which you would then #include in your sketch?
    3. One more diff between the two boards is the obvious size difference, although I’m guessing you didn’t mention that since it might not matter in your own projects. Besides, both boards are small anyway.
    4. There are other TTGO boards, which is why I made sure to explicitly cite the T-Journal

    Reply
  3. Hello again, Rui and Sarah. Have you tried accessing GPIO2 and GPIO4 in a sketch? I tried both “#define GPIO2 2” and “#define GPIO2 4” — even if I didn’t think the latter would work. Couldn’t get as simple a thing as blinking an LED.

    Or are there .h files specific to T-Journal or ESP32 that #define the GPIOs?

    Thanks in advance for any guidance.

    Reply
    • Hi Daniel.
      In theory, you should be able to blink an LED with the usual blink an LED sketch.
      Can you show me your code to blink an LED?
      Regards,
      Sara

      Reply
      • Hi Sara, thanks for getting back to me.

        First, my code was faulty, set pinMode to INPUT instead of OUTPUT .

        Then, it was my wiring through my breadboard. Connected the LED+ to IO2, LED- to GND directly, and my LED’s happily blinking away 🙂

        As an aside, I may try using IO4, which will probably mean #define outputPin 4 instead of 2.

        Which brings me to another question: does the TTGO family have header files somewhere — or maybe a library or something — where the pin names are defined, much like LED_BUILTIN is defined for the Arduino family?

        Code’s embarrassingly simple, but you asked for it:

        #define outputPin 2

        void setup() {
        pinMode(outputPin, OUTPUT);
        }

        void loop() {
        digitalWrite(outputPin, HIGH);
        delay (1000);
        digitalWrite(outputPin, LOW);
        delay (1000);
        }

        Warmest regards, and thanks again

        Reply
        • Hehe, pseudo-HTML tags disappeared, trying again, just because

          First, my code was faulty, <face-palm>set pinMode to INPUT instead of OUTPUT </face-palm>.

          On another, slightly related topic, I check the button to “Notify me of follow-up comments by email.” but don’t get anything. Not that it really matters, since I’d check manually anyways, just wanted to let you know.

          Regards

          Reply
        • Hi Daniel.
          I’m glad it is working now.
          I don’t know if there are any header files.
          But you can always define whenever pin you want and give it the name you want 😀
          I think you get notification by email when I answer your question (at least it should work like that).
          Anyway, thanks for telling me that.
          Regards,
          Sara

          Reply
  4. I am great fan of your fine and many well documented examples how to use ESP32.
    I have purchased a number of these including the ESP32-CAm all from Banggood.
    However I cannot duplicate Your efforts because I never can make Arduino -IDe nor the PlatformIO to recognize the boards – ie. no COM port is visible.
    It seem sit is necessary to by a FTDI board for a serial port setup and connect this to the ESP32 boards – but You never mentions this????

    Reply
  5. I’ve followed these excellent instructions to the letter but I cannot get the sketch into the TTGO module. I’ve installed the CP210x drivers and can see a port (15) connected to the computer but it always fails with error message:

    serial.serialutil.SerialException: could not open port ‘COM15’: WindowsError(5, ‘Access is denied.’)
    Failed to execute script esptool
    the selected serial port Failed to execute script esptool
    does not exist or your board is not connected

    Any suggestions?
    Thank you.

    Reply
    • Hi Michael.
      Check that you have selected the COM15 in Tools > Port.
      Also, check that you don’t have any serial monitor window open.
      Regards,
      Sara

      Reply
  6. Looks like this T-Journal camera board can only get to 800×600 resolution with the factory loaded sketch and no PSRAM.

    Can the sketch be modified to get to 1600×1200 even without the face recognition features? If so could you post those sketch changes.

    Thanks!

    Reply
    • Hi some of our sketches have the following lines to reduce resolution when the boards don’t have PSRAM:

      if(psramFound()){
      config.frame_size = FRAMESIZE_UXGA;
      config.jpeg_quality = 10;
      config.fb_count = 2;
      } else {
      config.frame_size = FRAMESIZE_SVGA;
      config.jpeg_quality = 12;
      config.fb_count = 1;
      }

      Replace these lines with:

      config.frame_size = FRAMESIZE_UXGA;
      config.jpeg_quality = 10;
      config.fb_count = 1;

      Regards,
      Sara

      Reply
      • So you are saying that the TTGO T-Journal even without the PSRAM can still do UXGA 1600X1200 video resolution?
        What are the cons to doing this if any?
        Thanks

        Reply
        • Hi Rob.
          I haven’t tested it, but video streaming will probably lag sometimes and the board will heat up very fast.
          Regards,
          Sara

          Reply
  7. Hi Sara,
    I have been attempting to get the CameraWebServer.ino to function without success. I and using your link: “Download our CameraWebServer Example for the TTGO T-Journal Board”. I seem to be able to get the code to compile and load but I cannot get access to the wifi TTGO T-Journal camera board. I see there is a ssid and password but how do I connect with my phone without any IP address? I could use some assistance with this problem. I am attempting to get the camera control menu, live video stream and a few controls to function and be accessed via wifi IP.

    The TTGO original load had an IP address included and worked.

    Thanks for any help you can give.

    Reply
    • I think with this sample you need to connect your phone to the WiFi signal that the TTGO is putting out using the ssid and password and then open a browser.

      By the way, how on earth did you get your board to accept a program? I’ve struggled until I’ve currently given up!

      Reply
      • Hi Michael,

        Where are you located…USA? I’m in Michigan USA.
        I am no expert here but all I have done is the normal stuff on the Arduino IDE at 115200 with the Wrover board as shown just above on the screen clip.

        Reply
        • I have tried everything and cannot figure out how to get an IP address. It was mentioned that once the sketch is uploaded then you can turn ON the monitor screen, press the reset button and the IP should show itself in the reboot info but nothin!

          Here is a monitor text. The dots at bottom keep popping up and scroll across the screen as if is doing something.
          ——————————————————
          …..ets Jun 8 2016 00:22:57

          rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
          configsip: 0, SPIWP:0xee
          clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
          mode:DIO, clock div:1
          load:0x3fff0018,len:4
          load:0x3fff001c,len:1216
          ho 0 tail 12 room 4
          load:0x40078000,len:9720
          ho 0 tail 12 room 4
          load:0x40080400,len:6352
          entry 0x400806b8
          ……. <these dots
          ——————————————

          Getting frustrated

          Reply
      • This is what I’ve uncovered:
        You must have ALL code monitor windows closed that may be open in other instances if IDE in order to upload any sketches without error.

        Reply
  8. Hi Sara,

    I just cannot get your Video Streaming Web Server sketch to work for the TTGO T-Journal camera board. I have copied and pasted the code from your article above, changed the SSID “TTGO Camera” and password “12345678” but nothing. It will compile and load without issues but the code in the monitor seems to just cycle with dots occurring across the screen. I get no WIFI device shown on my phone listed under WIFI devices “TTGO Camera”.

    Could you please offer any assistance on getting your code to work?
    Thank you

    Here is the code upload check:
    Sketch uses 790862 bytes (37%) of program storage space. Maximum is 2097152 bytes.
    Global variables use 49600 bytes (15%) of dynamic memory, leaving 278080 bytes for local variables. Maximum is 327680 bytes.

    The Upload:
    esptool.py v2.6
    Serial port COM7
    Connecting…..
    Chip is ESP32D0WDQ6 (revision 1)
    Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
    MAC: 3c:71:bf:f0:3c:b0
    Uploading stub…
    Running stub…
    Stub running…
    Configuring flash size…
    Auto-detected Flash size: 4MB
    Compressed 8192 bytes to 47…

    Writing at 0x0000e000… (100 %)
    Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.0 seconds (effective 4681.1 kbit/s)…
    Hash of data verified.
    Compressed 17392 bytes to 11186…

    Writing at 0x00001000… (100 %)
    Wrote 17392 bytes (11186 compressed) at 0x00001000 in 1.0 seconds (effective 141.0 kbit/s)…
    Hash of data verified.
    Compressed 790976 bytes to 453169…

    Writing at 0x00010000… (3 %)
    Writing at 0x00014000… (7 %)
    Writing at 0x00078000… (96 %)
    Writing at 0x0007c000… (100 %)
    Wrote 790976 bytes (453169 compressed) at 0x00010000 in 40.2 seconds (effective 157.5 kbit/s)…
    Hash of data verified.
    Compressed 3072 bytes to 116…

    Writing at 0x00008000… (100 %)
    Wrote 3072 bytes (116 compressed) at 0x00008000 in 0.0 seconds (effective 744.7 kbit/s)…
    Hash of data verified.

    Leaving…
    Hard resetting via RTS pin…
    ____________________________________________________

    The Monitor Screen Example:
    ………ets Jun 8 2016 00:22:57

    rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1216
    ho 0 tail 12 room 4
    load:0x40078000,len:9720
    ho 0 tail 12 room 4
    load:0x40080400,len:6352
    entry 0x400806b8
    ……………………………………………………………………………… <<< These dots

    This screen shot of code seems to just cycle causing the dots below:
    // 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;
    }
    // Wi-Fi connection
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print("."); <<< These periods or dots I think are the ones
    }
    Serial.println("");
    Serial.println("WiFi connected"); <<<< Can't get this message

    Serial.print("Camera Stream Ready! Go to: http://&quot;);
    Serial.print(WiFi.localIP());

    // Init OLED

    Thanks

    Reply
    • Hi Rob.
      So, are you trying to run the CameraWebServer example with the TTGO T-Journal?
      That examples doesn’t create an access point. In that example, your boards acts as a station. So, in the SSID and password you need to insert your router network credentials, so that it connects to your router network.
      I hope this helps.
      Regards,
      Sara

      Reply
  9. Hi Sara,

    Thanks for your help. I have inserted my router ssid and password but still no luck. Would be nice to have it link directly over wifi to the phone or tablet. Would you be willing to provide an example link that replicates the original TTGO loaded code as when purchased so we can get back to the factory functionality?

    Regards

    Reply
  10. Hi @Rob, I can get the example code to connect to my router. I think it’s actually more useful to have the TTGO board connect to my local WiFi (as in this code) because then my phone or tablet doesn’t have to switch away from its normal internet connection.

    I did have a typo in my WiFi password at first, but once I fixed that, my router handed the TTGO an IP address which got displayed on the back.

    Reply
  11. I just discovered your website a few days ago after I found a TTGO T-Journal cam factory sealed in its little bag when I was cleaning out my lab and I wanted to do something other than the demo. Great site, thanks!

    Reply
  12. Hi Sara and Rui. Is it possible to rotate the camera image in the TTGO-T-Journal-ESP32-Camera-Video-Streaming-Web-Server sketch? Seems the image rotation needs to be done in software since the camera hardware does not support it. Thanks. 🙂

    Reply
  13. Hi, great site. I just got started with this board.
    One question, in your article, it states that GPIO32 is the button.
    However in camera pin out it’s #define PWDN_GPIO_NUM 32.

    I’m trying to get the button to work. Would appreciate help.

    Reply
  14. Troubles using PlatformIO?

    Using PlatformIO, and attempting to get the simple webserver sketch up onto my TTGO-T-Journal board. Compile error: In file included from .pio\libdeps\ttgo-t1\Adafruit GFX Library\Adafruit_GrayOLED.cpp:20:0:
    .pio\libdeps\ttgo-t1\Adafruit GFX Library\Adafruit_GrayOLED.h:30:32: fatal error: Adafruit_I2CDevice.h: No such file or directory

    I went to PIO Home / Libraries and did a search for Adafruit_I2CDevice.h, and found Adafruit BusIO library. Installed it, and bingo, it’s in business.

    (And the local network URL (10.0.0.147) is displayed in teeeeeny numbers on the screen.
    With its external antenna, this is a better camera platform than the regular ESP32 cams. Now, if attaching an external camera will make my other two ESP32 cameras of this TTGO quality, then I’m gonna figure out how to attach external antennas quick.)

    Reply
  15. Hallo und guten Tag,

    Prost Neujahr.
    Ich habe eine Frage: meine OV2640 V1.8 Cam hat keine IP Adresse und keine MAC Adresse? und meldet Fehler wenn ich auf den Reset Button drücke:
    11:18:54.939 -> ets Jun 8 2016 00:22:57
    11:18:54.939 ->
    11:18:54.939 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    11:18:54.939 -> configsip: 0, SPIWP:0xee
    11:18:54.939 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    11:18:54.939 -> mode:DIO, clock div:1
    11:18:54.939 -> load:0x3fff0018,len:4
    11:18:54.939 -> load:0x3fff001c,len:1216
    11:18:54.939 -> ho 0 tail 12 room 4
    11:18:54.939 -> load:0x40078000,len:9720
    11:18:54.939 -> ho 0 tail 12 room 4
    11:18:54.939 -> load:0x40080400,len:6352
    11:18:54.939 -> entry 0x400806b8
    11:18:55.588 ->
    11:18:55.654 -> [E][camera.c:1049] camera_probe(): Detected camera not supported.
    11:18:55.654 -> [E][camera.c:1249] esp_camera_init(): Camera probe failed with error 0x20004
    Wie kann ich den Fehler beheben?
    MFG
    Gert Hermanni

    Reply
  16. TTGO Journal just arrived and I am trying to run the CameraWebServer example
    on Arduino 1.8.14, I selected Partition Scheme “Huge App (3MB No OTH/1M SPIFFS)”
    It loads but seems to halt with the following message. I wasn’t expecting it to be looking for PSRAM as it is a TTBO Journal board.

    22:18:06.584 -> E (530) psram: PSRAM ID read error: 0xffffffff
    22:18:06.584 ->
    22:18:06.750 -> E (672) cam_hal: cam_dma_config(271): frame buffer malloc failed
    22:18:06.750 -> E (672) cam_hal: cam_config(355): cam_dma_config failed
    22:18:06.750 -> E (673) camera: Camera config failed with error 0xffffffff
    22:18:06.750 -> Camera init failed with error 0xffffffffGuru Meditation Error:

    Reply
  17. Thanks for the reply and suggestion however they seem to be directed to boards with PSRAM which the TTGO Journal doesn’t have. I have been able to get the original https://github.com/LilyGO/ESP32-Camera/blob/master/examples/ESP32_SoftAP_Cam/ESP32_SoftAP_Cam.ino to assemble and get work by substituting the ADAFRUIT SSD1306 library and code. So I know the board is still working. So it seem there must be something different in the difference in libraries.
    By switching to the ESP32-PICO-D4 which the TTGO has the line ” E (530) psram: PSRAM ID read error: 0xffffffff” no longer appears, but Board: “ESP32 Wrover Module” is what is called for in the article. So I am wondering if there is some other parameter still missing from the newer library for this board. I have purchased the ESP Camera book and ESP Python books and looking forward to moving past this very annoying issue. The Adafruit SSD1306 demo does work.

    Reply
    • Hi Gary.
      I’m not really sure that I understand your issue.
      Since you are one of our customers, can you please post your issue in our forum?
      Here’s the link: https://rntlab.com/forum/
      It’s easier for me to follow the issues there. I always give priority to our customers.
      Regards,
      Sara

      Reply
  18. Hi everybody,

    excellent tutorial, i managed to setup a working camera webserver basically without any experience with ESP32. Now i would like to try out some additional things…

    So maybe you know if this Board also comes with Bluetooth? I looked at the specs, also on the Suppliers website, but it does not list Bluetooth. The specs do not list Wifi either, so perhaps its clear to most experienced people that Wifi and (hopefully) Bluetooth is standard here?

    Thanks & best regards,
    Markus

    Reply
  19. My t-journal is working but after uploading the code it cn’t connect with the camera :

    E (185) psram: PSRAM ID read error: 0xffffffff
    E (328) cam_hal: cam_dma_config(280): frame buffer malloc failed
    E (328) cam_hal: cam_config(364): cam_dma_config failed
    E (329) camera: Camera config failed with error 0xffffffff
    Camera init failed with error 0xffffffff

    I’m wondering why it’s probing psram when there is none, i commented out the section of code referencing the PSRAM but still no detection of camera.

    Reply
  20. I received a LILYGO® TTGO T-Journal ESP32 Camera Module Development Board today. I went through your excellent guide but when I try to upload the TTGO-T-Journal example from above, I get the following error:

    ext section exceeds available space in board
    Sketch too big; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing it.
    Error compiling for board TTGO T1

    Any suggestions?

    Reply
      • Sara,,
        Thank you for your reply. Since I wrote the comment, I was able to successfully upload the sketch. I used the TTGO T1 board. I have a number of ports available:
        Serial ports
        /dev/cu.BLTH
        /dev/cu.Bluetooth-Incoming-Port
        /dev/cu.URT1
        /dev/cu.usbmodem53190187981
        /dev/cu.usbserial-143420
        /dev/cu.wchusbserial143420
        /dev/cu.wchusbserial53190187981
        and it only worked with the last one.

        Reply

Leave a Reply to Rob Cancel reply

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.