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.
Where to Buy?
You can go to the TTGO T-Journal page on Maker Advisor to compare the board on different stores.
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
Here’s a table with the connections between the ESP32 and the camera:
OV2640 Camera | ESP32 |
D2 | GPIO 17 |
D3 | GPIO 35 |
D4 | GPIO 34 |
D5 | GPIO 5 |
D6 | GPIO 39 |
D7 | GPIO 18 |
D8 | GPIO 36 |
D9 | GPIO 19 |
SIOC | GPIO 23 |
SIOD | GPIO 25 |
XCLK | GPIO 27 |
VSYNC | GPIO 22 |
HREF | GPIO 26 |
PCLK | GPIO 21 |
RST | GPIO 15 |
PWDN | GPIO 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
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:
OLED | ESP32 |
SDA | GPIO 14 |
SCL | GPIO 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:
}
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_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_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);
}
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_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_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 );
}
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.
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.
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)”
Then, simply click the Arduino IDE upload button and it is done!
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:
- TTGO T-Journal ESP32 Camera Board Review and Documentation (Pinout, Features, etc…)
- ESP32 Camera Dev Boards Review and Comparison (Best ESP32-CAM)
You may also be interested in other ESP32 development boards:
- $7 ESP32-CAM with OV2640 Camera (Review)
- ESP32 with Built-in SX1276 LoRa and SSD1306 OLED Display (Review)
- $11 TTGO T-Call ESP32 with SIM800L GSM/GPRS (Overview)
- 10 IoT Development Boards You Need to Get
- Best ESP32 Development Boards
- Build ESP32-CAM Projects (eBook)
- Read all our ESP32-CAM Projects, Tutorials and Guides
Thanks for reading.
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.
Hi Jean.
TTGO is simply the name of the brand. At least, that’s what I think.
Regards,
Sara
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
Hi Daniel.
That’s great!
Yes, you can include the pins in a .h and include it in your sketch.
See the “CameraWebServer Example” section in the post. We show how to do that.
Regards,
Sara
Heh!, just goes to show I didn’t read the rest of the code closely enough 🙂
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.
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
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
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
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
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????
Hi.
Which board are you using?
With the TTGO T-Journal, you don’t need an FTDI programmer.
With the ESP32-CAM, you need an FTDI programmer: https://randomnerdtutorials.com/program-upload-code-esp32-cam/
Regards,
Sara
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.
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
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!
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
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
Hi Rob.
I haven’t tested it, but video streaming will probably lag sometimes and the board will heat up very fast.
Regards,
Sara
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.
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!
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.
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
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.
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://");
Serial.print(WiFi.localIP());
// Init OLED
Thanks
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
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
Hi Rob.
That is very weird. In the Serial Monitor are you still getting a lot of dots ….??
This repository contains the original example: https://github.com/lewisxhe/ESP32-Camera-Arduino
Regards,
Sara
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.
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!
You’re welcome Dennis! I’m glad it was helpful. You can find all our projects here:
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. 🙂
Hi.
You can change the camera settings with the methods shown in this tutorial: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
Take a look at set_hmirror() and set_vflip().
Regards,
Sara
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.
Hi.
Try setting #define PWDN_GPIO_NUM to 0.
And then, try to use GPIO 32 for the button.
Regards,
Sara
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.)
Hi.
We have this article about the external antenna that you might find useful: https://randomnerdtutorials.com/esp32-cam-connect-external-antenna/
Regards,
Sara
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
Hi.
What is exactly the board that you are using?
And what code are you trying to run?
Regards,
Sara
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:
Hi.
Take a look at this discussion and see if it helps: https://github.com/espressif/esp-idf/issues/6288
Regards,
Sara
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.
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
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
Hi.
Yes, the ESP32 chip comes with Wi-Fi and Bluetooth.
Regards,
Sara
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.
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?
Hi.
What board did you select in Tools > Board?
Regards,
Sara
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.