TTGO T-Journal ESP32 камера: встроенный программатор, OLED, антенна и примеры проектов

Это руководство по началу работы с платой разработки TTGO T-Journal ESP32 Camera. TTGO T-Journal оснащена камерой OV2640, OLED-дисплеем, несколькими GPIO для подключения периферии и встроенным программатором, что упрощает загрузку кода. Мы рассмотрим плату камеры и научимся программировать её с помощью Arduino IDE.

TTGO T-Journal ESP32 камера: встроенный программатор, OLED, антенна и примеры проектов

Где купить?

Вы можете перейти на страницу TTGO T-Journal на Maker Advisor, чтобы сравнить плату в разных магазинах.

TTGO T-Journal ESP32 Camera Dev Board

Знакомство с TTGO T-Journal ESP32 Camera

TTGO T-Journal — это плата разработки ESP32 Camera стоимостью $12-$15 с камерой OV2640, антенной, I2C OLED-дисплеем SSD1306 0.91 дюйма, несколькими открытыми GPIO и интерфейсом micro-USB, который упрощает и ускоряет загрузку кода на плату.

Для полного обзора этой платы вы можете посмотреть следующее видео или прочитать эту статью: TTGO T-Journal ESP32 Camera Dev Board Review.

Характеристики TTGO T-Journal ESP32

Вот краткое описание характеристик TTGO T-Journal:

  • Чипсет ESPRESSIF-ESP32-PCIO-D4 240 МГц Xtensa* single-/dual-core 32-bit LX6 микропроцессор

  • FLASH QSPI flash/SRAM, до 4 x 16 МБ SRAM 520 КБ SRAM

  • Кнопка сброса и кнопка на GPIO 32

  • OLED-дисплей SSD1306 0.91 дюйма

  • Красный светодиод индикатора питания

  • USB to TTL CP2104 (можно загружать код через USB-кабель)

  • Камера OV2640 2 мегапикселя

  • Рулевой двигатель аналоговый серво (поставляется с двумя наборами пинов, идеальных для подключения сервоприводов)

  • Рабочее напряжение: 2.3В-3.6В

  • Рабочий ток: около 160 мА

  • Размер: 64.57 мм x 23.98 мм

Характеристики питания:

  • Питание USB 5В/1А

  • Ток зарядки 1А

  • Батарея 3.7В литиевый аккумулятор

Прочитайте наш подробный обзор: TTGO T-Journal ESP32 Camera Development Board

Распиновка платы TTGO T-Journal ESP32

Наличие правильной распиновки для вашей платы камеры очень важно. Если вы не назначите правильные пины камеры в своём коде, камера не будет работать. На следующем изображении показана распиновка платы TTGO T-Journal ESP32.

Распиновка TTGO T-Journal ESP32

Источник изображения

Подключение камеры TTGO T-Journal ESP32

TTGO T-Journal ESP32 OV2640 камера

Вот таблица подключений между ESP32 и камерой:

Камера OV2640

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

Таким образом, назначение пинов в вашем Arduino-скетче должно быть следующим:

#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

Поскольку эта плата использует ту же камеру, что и плата ESP32-CAM, примеры для ESP32-CAM (которые не используют microSD-карту) также должны работать с TTGO T-Journal при изменении определения пинов. Мы покажем вам пару примеров через мгновение.

Подключение OLED-дисплея TTGO T-Journal ESP32

Подключение OLED-дисплея TTGO T-Journal ESP32 SCL SDA I2C

Эта плата поставляется с I2C OLED-дисплеем SSD1306 0.91 дюйма. Для взаимодействия с дисплеем можно использовать библиотеки Adafruit SSD1306, oled-ssd1306 или другие совместимые библиотеки. Мы обычно используем Adafruit SSD1306 вместе с Adafruit_GFX для взаимодействия с OLED-дисплеями.

OLED взаимодействует с ESP32 через следующие пины:

OLED

ESP32

SDA

GPIO 14

SCL

GPIO 13

Управление OLED-дисплеем TTGO T-Journal ESP32

В этом разделе мы покажем вам краткие советы по управлению OLED-дисплеем платы TTGO T-Journal ESP32.

Установка библиотек

Для управления OLED-дисплеем мы будем использовать библиотеки Adafruit SSD1306 и Adafruit GFX. Эти библиотеки можно установить через менеджер библиотек Arduino IDE.

В Arduino IDE перейдите в Sketch > Include Library > Manage Libraries. Затем найдите библиотеку по имени и установите её.

I2C пины OLED и размер дисплея

Управление этим OLED-дисплеем аналогично управлению обычным OLED-дисплеем 0.96 дюйма, подключённым к ESP32. Единственное отличие — способ инициализации дисплея.

Вам нужно учитывать I2C пины, используемые этим дисплеем (так как он не использует стандартные I2C пины), и размер дисплея.

  • I2C пины:

    • SDA (GPIO 14)

    • SCL (GPIO 13)

  • Размер дисплея:

    • Ширина: 128 пикселей

    • Высота: 32 пикселя

Arduino-скетч

Для управления OLED-дисплеем сначала нужно импортировать необходимые библиотеки:

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

Определите размер OLED:

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

Определите I2C пины:

#define I2C_SDA 14
#define I2C_SCL 13

Затем создайте объект Adafruit_SSD1306 с именем display следующим образом:

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

В setup() нужно инициализировать I2C-связь на ранее определённых I2C пинах:

Wire.begin(I2C_SDA, I2C_SCL);

Затем инициализируйте OLED-дисплей следующим образом:

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

После правильной инициализации дисплея вы можете использовать обычные функции для вывода текста и отображения фигур на OLED. Прочитайте наш урок по OLED с ESP32, чтобы узнать больше о взаимодействии с OLED-дисплеем.

Для тестирования вы можете загрузить следующий код на свою плату. Он просто выводит «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 управление OLED-дисплеем пример Hello World

Проекты с камерой TTGO T-Journal ESP32

Мы модифицировали некоторые из наших существующих проектов ESP32-CAM, чтобы они были совместимы с TTGO T-Journal.

Веб-сервер потокового видео

Следующий код создаёт веб-сервер потокового видео на IP-адресе камеры. Таким образом, вы можете создать IP-камеру, которую можно интегрировать в платформы домашней автоматизации, такие как Home Assistant или 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);
}

Посмотреть исходный код

Пример веб-сервера потокового видео ESP32-CAM

Подробнее об этом проекте: Веб-сервер потокового видео ESP32-CAM

Фотографирование и отображение на веб-сервере

Следующий код создаёт веб-сервер, к которому вы можете обращаться для съёмки и отображения фотографий. IP-адрес веб-сервера отображается на 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 <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(200, "text/html", index_html);
  });

  server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) {
    takeNewPhoto = true;
    request->send(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 );
}

Посмотреть исходный код

ESP32-CAM фотографирование и отображение на веб-сервере

Подробнее об этом проекте: ESP32-CAM: фотографирование и отображение на веб-сервере

Пример CameraWebServer

Вы также можете запустить стандартный пример CameraWebServer, который поставляется с Arduino IDE. В Arduino IDE перейдите в File > Examples > ESP32 > Camera и откройте пример CameraWebServer.

Вы можете нажать на следующую ссылку, чтобы скачать .zip с финальным кодом:

В противном случае, вам нужно добавить распиновку TTGO T-Journal во вкладку camera_pins.h.

Пример CameraWebServer ESP32-CAM для Arduino IDE

Скопируйте следующий код в файл camera_pins.h.

#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

Затем во вкладке CameraWebServer закомментируйте все существующие модели камер и добавьте свою камеру следующим образом:

// 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

Поскольку эта камера не имеет PSRAM, функции распознавания и обнаружения лиц этого проекта не работают с этой камерой. Все остальные функции работают хорошо.

Пример CameraWebServer ESP32-CAM для Arduino IDE веб-сервер

Загрузка кода в TTGO T-Journal ESP32 Camera

Чтобы загрузить код, просто подключите плату к компьютеру, затем в Arduino IDE перейдите в Tools > Port и выберите COM-порт, к которому она подключена.

Затем вам также нужно выбрать модель платы. TTGO T-Journal недоступна в моделях ESP32. Поэтому выберите следующие настройки:

  • Board: «ESP32 Wrover Module»

  • Partition Scheme: «Huge APP (3MB No OTA)»

Board ESP32 Wrover Module и Partition Scheme Huge APP (3MB No OTA)

Затем просто нажмите кнопку загрузки в Arduino IDE, и всё готово!

Кнопка компиляции и загрузки скетча в Arduino IDE

Заключение

Это руководство было кратким введением в работу с платой разработки TTGO T-Journal ESP32 Camera. Вы узнали, как управлять OLED-дисплеем и как адаптировать существующие проекты ESP32 камеры для вашей платы.

Мы надеемся, что это руководство было для вас полезным. Чтобы узнать больше об этой плате, вы можете прочитать наш полный обзор на Maker Advisor, а также нашу статью сравнения плат разработки ESP32-CAM:

Вас также могут заинтересовать другие платы разработки ESP32: