ESP32-CAM: отправка изображений на локальный или облачный сервер с помощью PHP (менеджер фото)
Узнайте, как отправлять HTTP POST запросы с платы ESP32-CAM в Arduino IDE для загрузки фотографий на сервер. Мы покажем, как отправить изображение JPG/JPEG на локальный сервер (LAMP-сервер Raspberry Pi) или на облачный сервер (к которому можно получить доступ из любой точки мира). Фотографии будут отображаться в галерее, где их можно просматривать или удалять. Для сохранения изображений на сервере и создания галереи мы будем использовать PHP-скрипты.
Обновлено 27 марта 2023
Для реализации этого проекта необходимо выполнить следующие шаги. Следуйте инструкциям для LAMP-сервера или хостинг-сервера в зависимости от того, хотите ли вы получать доступ к фотографиям локально или из любой точки мира.
Размещение вашего PHP-приложения
PHP-скрипты для сохранения и отображения фотографий на сервере
1. Размещение вашего PHP-приложения
Цель этого проекта – иметь локальный или облачный сервер для хранения и доступа к фотографиям ESP32-CAM.
1. Локальный сервер Raspberry Pi:
С LAMP-сервером Raspberry Pi, вы можете получить доступ к своим изображениям локально (как показано ниже).
Вы можете запустить LAMP-сервер (Linux, Apache, MySQL, PHP) на Raspberry Pi для доступа к данным в вашей локальной сети. LAMP-сервер Raspberry Pi: локальный Linux-сервер, который вы используете для локального доступа к изображениям.
Настройка локального RPi LAMP-сервера >>
2. Облачный сервер (хостинг Bluehost)
Вы также можете просматривать фотографии ESP32-CAM из любой точки мира, обращаясь к собственному серверу + домену. Вот общая схема работы:
Bluehost (удобный с cPanel): бесплатное доменное имя при подписке на 3-летний план. Рекомендую выбрать вариант с неограниченным количеством сайтов; Обратите внимание, что любой хостинг-сервис с поддержкой PHP подойдёт для этого руководства. Если у вас нет хостинг-аккаунта, рекомендую зарегистрироваться на Bluehost.
Получить хостинг и доменное имя на Bluehost >>
При покупке хостинг-аккаунта вам также потребуется приобрести доменное имя. Именно это делает проект интересным: вы сможете перейти по вашему доменному имени (http://example.com) и увидеть фотографии ESP32-CAM. Если вам нравятся наши проекты, рассмотрите возможность регистрации на Bluehost, так как вы поддержите нашу работу.
Метод HTTP POST запроса
Протокол передачи гипертекста (HTTP) работает как протокол «запрос-ответ» между клиентом и сервером. Вот пример:
ESP32 (клиент) отправляет HTTP-запрос серверу (например: локальный RPi LAMP-сервер или example.com);
Сервер возвращает ответ ESP32 (клиенту);
HTTP POST используется для отправки данных на сервер с целью создания/обновления ресурса. Например, публикация изображения на сервере.
POST /upload.php HTTP/1.1
Host: example.com
Content-Type: image/jpeg
2.1. Подготовка файлов .php и папки uploads (LAMP-сервер Raspberry Pi)
Этот раздел подготавливает ваши файлы .php и папку uploads для LAMP-сервера Raspberry Pi. Если вы используете собственный сервер + доменное имя, перейдите к следующему разделу.
Имея Raspberry Pi с Apache и PHP, в окне терминала Raspberry Pi перейдите в директорию /var/www/html/:
pi@raspberrypi:~ $ cd /var/www/html/
Создайте новую папку с именем uploads:
pi@raspberrypi:/var/www/html $ mkdir uploads
pi@raspberrypi:/var/www/html $ ls
uploads
На данный момент /var/www/html принадлежит root, используйте следующие команды, чтобы сменить владельца на пользователя pi и дать ему все разрешения, чтобы можно было сохранять фотографии с помощью PHP-скрипта позже.
sudo chown -R pi:pi /var/www/html
chmod -R 777 /var/www/html/
Наконец, создайте новый файл upload.php:
pi@raspberrypi:/var/www/html $ nano upload.php
Этот PHP-скрипт отвечает за приём входящих изображений от ESP32-CAM, переименование изображений с меткой времени и сохранение их в папке uploads. Отредактируйте только что созданный файл (upload.php) и скопируйте следующий фрагмент:
<?php
// Rui Santos
// Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
// Code Based on this example: w3schools.com/php/php_file_upload.asp
$target_dir = "uploads/";
$datum = mktime(date('H')+0, date('i'), date('s'), date('m'), date('d'), date('y'));
$target_file = $target_dir . date('Y.m.d_H:i:s_', $datum) . basename($_FILES["imageFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["imageFile"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
}
else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["imageFile"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
}
else {
if (move_uploaded_file($_FILES["imageFile"]["tmp_name"], $target_file)) {
echo "The file ". basename( $_FILES["imageFile"]["name"]). " has been uploaded.";
}
else {
echo "Sorry, there was an error uploading your file.";
}
}
?>
Ваш файл upload.php должен выглядеть так. Сохраните файл и выйдите (Ctrl+X, Y и Enter):
Затем создайте новый файл gallery.php:
pi@raspberrypi:/var/www/html $ nano gallery.php
Отредактируйте только что созданный файл (gallery.php) и скопируйте следующий фрагмент:
<!--
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
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.
-->
<!DOCTYPE html>
<html>
<head>
<title>ESP32-CAM Photo Gallery</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.flex-container {
display: flex;
flex-wrap: wrap;
}
.flex-container > div {
text-align: center;
margin: 10px;
}
</style>
</head><body>
<h2>ESP32-CAM Photo Gallery</h2>
<?php
// Image extensions
$image_extensions = array("png","jpg","jpeg","gif");
// Check delete HTTP GET request - remove images
if(isset($_GET["delete"])){
$imageFileType = strtolower(pathinfo($_GET["delete"],PATHINFO_EXTENSION));
if (file_exists($_GET["delete"]) && ($imageFileType == "jpg" || $imageFileType == "png" || $imageFileType == "jpeg") ) {
echo "File found and deleted: " . $_GET["delete"];
unlink($_GET["delete"]);
}
else {
echo 'File not found - <a href="gallery.php">refresh</a>';
}
}
// Target directory
$dir = 'uploads/';
if (is_dir($dir)){
echo '<div class="flex-container">';
$count = 1;
$files = scandir($dir);
rsort($files);
foreach ($files as $file) {
if ($file != '.' && $file != '..') {?>
<div>
<p><a href="gallery.php?delete=<?php echo $dir . $file; ?>">Delete file</a> - <?php echo $file; ?></p>
<a href="<?php echo $dir . $file; ?>">
<img src="<?php echo $dir . $file; ?>" style="width: 350px;" alt="" title=""/>
</a>
</div>
<?php
$count++;
}
}
}
if($count==1) { echo "<p>No images found</p>"; }
?>
</div>
</body>
</html>
Этот PHP-скрипт отвечает за отображение изображений в галерее. Ваш файл gallery.php должен выглядеть так. Сохраните файл и выйдите (Ctrl+X, Y и Enter):
2.2. Подготовка файлов .php и папки uploads (хостинг-сервис)
Если вы предпочитаете запускать сервер удалённо и получать доступ к фотографиям из любой точки мира, вам нужен хостинг-аккаунт. После регистрации хостинг-аккаунта и настройки доменного имени, вы можете войти в свою cPanel или аналогичную панель управления. После этого откройте File Manager.
Откройте вкладку «Advanced» и выберите «File Manager»:
Затем выберите опцию public_html. Нажмите кнопку «+ File» для создания нового файла upload.php и нового файла gallery.php. Затем нажмите кнопку «+Folder» для создания папки Uploads.
После создания трёх элементов, отредактируйте файл upload.php:
Этот PHP-скрипт отвечает за приём входящих изображений от ESP32-CAM, переименование изображений с меткой времени и сохранение их в папке uploads. Отредактируйте только что созданный файл (upload.php) и скопируйте следующий фрагмент:
<?php
// Rui Santos
// Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
// Code Based on this example: w3schools.com/php/php_file_upload.asp
$target_dir = "uploads/";
$datum = mktime(date('H')+0, date('i'), date('s'), date('m'), date('d'), date('y'));
$target_file = $target_dir . date('Y.m.d_H:i:s_', $datum) . basename($_FILES["imageFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["imageFile"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".";
$uploadOk = 1;
}
else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["imageFile"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
}
else {
if (move_uploaded_file($_FILES["imageFile"]["tmp_name"], $target_file)) {
echo "The file ". basename( $_FILES["imageFile"]["name"]). " has been uploaded.";
}
else {
echo "Sorry, there was an error uploading your file.";
}
}
?>
Сохраните файл и выйдите.
Затем отредактируйте файл gallery.php и скопируйте следующий фрагмент. Он отвечает за отображение изображений в галерее.
<!--
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
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.
-->
<!DOCTYPE html>
<html>
<head>
<title>ESP32-CAM Photo Gallery</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.flex-container {
display: flex;
flex-wrap: wrap;
}
.flex-container > div {
text-align: center;
margin: 10px;
}
</style>
</head><body>
<h2>ESP32-CAM Photo Gallery</h2>
<?php
// Image extensions
$image_extensions = array("png","jpg","jpeg","gif");
// Check delete HTTP GET request - remove images
if(isset($_GET["delete"])){
$imageFileType = strtolower(pathinfo($_GET["delete"],PATHINFO_EXTENSION));
if (file_exists($_GET["delete"]) && ($imageFileType == "jpg" || $imageFileType == "png" || $imageFileType == "jpeg") ) {
echo "File found and deleted: " . $_GET["delete"];
unlink($_GET["delete"]);
}
else {
echo 'File not found - <a href="gallery.php">refresh</a>';
}
}
// Target directory
$dir = 'uploads/';
if (is_dir($dir)){
echo '<div class="flex-container">';
$count = 1;
$files = scandir($dir);
rsort($files);
foreach ($files as $file) {
if ($file != '.' && $file != '..') {?>
<div>
<p><a href="gallery.php?delete=<?php echo $dir . $file; ?>">Delete file</a> - <?php echo $file; ?></p>
<a href="<?php echo $dir . $file; ?>">
<img src="<?php echo $dir . $file; ?>" style="width: 350px;" alt="" title=""/>
</a>
</div>
<?php
$count++;
}
}
}
if($count==1) { echo "<p>No images found</p>"; }
?>
</div>
</body>
</html>
Сохраните файл и выйдите. Вот и всё! Ваш сервер готов.
3. ESP32-CAM: HTTP POST изображений/фотографий на сервер
Теперь, когда ваш сервер готов (LAMP-сервер Raspberry Pi или облачный сервер), пришло время подготовить ESP32-CAM с кодом для публикации нового изображения на сервер каждые 30 секунд. Прежде чем продолжить это руководство, убедитесь, что вы выполнили следующие предварительные требования.
Необходимые компоненты
Для этого руководства вам потребуются следующие компоненты:
ESP32-CAM с OV2640 – читайте Лучшие платы ESP32-CAM
Локальный сервер:
Облачный сервер (альтернатива): Bluehost
Arduino IDE
Мы будем программировать ESP32-CAM в Arduino IDE, поэтому убедитесь, что у вас установлен аддон ESP32.
Проверка PHP URL
Вы должны попробовать открыть локальный IP-адрес Raspberry Pi или внешнее доменное имя example.com, с добавлением /upload.php, что должно вернуть:
Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.
Если вы видите это сообщение, сохраните ваш URL/доменное имя и путь, ваш сервер должен быть готов и вы можете продолжить это руководство.
Дополнительно попробуйте получить доступ к пути /gallery.php. Вы должны увидеть что-то подобное:
Код ESP32-CAM
Если вы используете локальный сервер без TLS/SSL, или облачный сервер, который не поддерживает HTTPS, используйте код HTTP POST запроса.
Если вы используете облачный сервер, требующий HTTPS-запросов, используйте этот код: код HTTPS POST запроса.
ESP32-CAM HTTP POST запрос
Следующий скетч отправляет изображение на сервер с помощью HTTP POST. Скопируйте код ниже в Arduino IDE.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
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 <Arduino.h>
#include <WiFi.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
String serverName = "192.168.1.XXX"; // REPLACE WITH YOUR Raspberry Pi IP ADDRESS
//String serverName = "example.com"; // OR REPLACE WITH YOUR DOMAIN NAME
String serverPath = "/upload.php"; // The default serverPath should be upload.php
const int serverPort = 80;
WiFiClient client;
// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
const int timerInterval = 30000; // time between each HTTP POST image
unsigned long previousMillis = 0; // last time image was sent
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
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;
// init with high specs to pre-allocate larger buffers
if(psramFound()){
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_CIF;
config.jpeg_quality = 12; //0-63 lower number means higher quality
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);
delay(1000);
ESP.restart();
}
sendPhoto();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= timerInterval) {
sendPhoto();
previousMillis = currentMillis;
}
}
String sendPhoto() {
String getAll;
String getBody;
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
}
Serial.println("Connecting to server: " + serverName);
if (client.connect(serverName.c_str(), serverPort)) {
Serial.println("Connection successful!");
String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--RandomNerdTutorials--\r\n";
uint32_t imageLen = fb->len;
uint32_t extraLen = head.length() + tail.length();
uint32_t totalLen = imageLen + extraLen;
client.println("POST " + serverPath + " HTTP/1.1");
client.println("Host: " + serverName);
client.println("Content-Length: " + String(totalLen));
client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
client.println();
client.print(head);
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n=0; n<fbLen; n=n+1024) {
if (n+1024 < fbLen) {
client.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen%1024>0) {
size_t remainder = fbLen%1024;
client.write(fbBuf, remainder);
}
}
client.print(tail);
esp_camera_fb_return(fb);
int timoutTimer = 10000;
long startTimer = millis();
boolean state = false;
while ((startTimer + timoutTimer) > millis()) {
Serial.print(".");
delay(100);
while (client.available()) {
char c = client.read();
if (c == '\n') {
if (getAll.length()==0) { state=true; }
getAll = "";
}
else if (c != '\r') { getAll += String(c); }
if (state==true) { getBody += String(c); }
startTimer = millis();
}
if (getBody.length()>0) { break; }
}
Serial.println();
client.stop();
Serial.println(getBody);
}
else {
getBody = "Connection to " + serverName + " failed.";
Serial.println(getBody);
}
return getBody;
}
ESP32-CAM HTTPS POST запрос
Следующий скетч отправляет изображение на сервер с помощью HTTPS POST. Скопируйте код ниже в Arduino IDE.
/*
Rui Santos
Complete project details at:
https://RandomNerdTutorials.com/esp32-cam-http-post-php-arduino/
https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
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 <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
String serverName = "example.com"; //REPLACE WITH YOUR DOMAIN NAME
String serverPath = "/upload.php"; // The default serverPath should be upload.php
const int serverPort = 443; //server port for HTTPS
WiFiClientSecure client;
// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
const int timerInterval = 30000; // time between each HTTP POST image
unsigned long previousMillis = 0; // last time image was sent
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
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;
// init with high specs to pre-allocate larger buffers
if(psramFound()){
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_CIF;
config.jpeg_quality = 12; //0-63 lower number means higher quality
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);
delay(1000);
ESP.restart();
}
sendPhoto();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= timerInterval) {
sendPhoto();
previousMillis = currentMillis;
}
}
String sendPhoto() {
String getAll;
String getBody;
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
}
Serial.println("Connecting to server: " + serverName);
client.setInsecure(); //skip certificate validation
if (client.connect(serverName.c_str(), serverPort)) {
Serial.println("Connection successful!");
String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--RandomNerdTutorials--\r\n";
uint32_t imageLen = fb->len;
uint32_t extraLen = head.length() + tail.length();
uint32_t totalLen = imageLen + extraLen;
client.println("POST " + serverPath + " HTTP/1.1");
client.println("Host: " + serverName);
client.println("Content-Length: " + String(totalLen));
client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
client.println();
client.print(head);
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n=0; n<fbLen; n=n+1024) {
if (n+1024 < fbLen) {
client.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen%1024>0) {
size_t remainder = fbLen%1024;
client.write(fbBuf, remainder);
}
}
client.print(tail);
esp_camera_fb_return(fb);
int timoutTimer = 10000;
long startTimer = millis();
boolean state = false;
while ((startTimer + timoutTimer) > millis()) {
Serial.print(".");
delay(100);
while (client.available()) {
char c = client.read();
if (c == '\n') {
if (getAll.length()==0) { state=true; }
getAll = "";
}
else if (c != '\r') { getAll += String(c); }
if (state==true) { getBody += String(c); }
startTimer = millis();
}
if (getBody.length()>0) { break; }
}
Serial.println();
client.stop();
Serial.println(getBody);
}
else {
getBody = "Connection to " + serverName + " failed.";
Serial.println(getBody);
}
return getBody;
}
Подробнее о HTTPS-запросах с ESP32: ESP32 HTTPS Requests (Arduino IDE).
Ввод сетевых учётных данных, камеры и данных сервера
Перед загрузкой кода вам нужно ввести ваши сетевые учётные данные в следующие переменные:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Убедитесь, что выбрана правильная модель камеры. В данном случае мы используем модель AI-THINKER. Если вы используете другую модель камеры, можете прочитать это Руководство по платам ESP32-CAM: назначение выводов и GPIO.
Добавьте IP-адрес вашего Raspberry Pi или используйте доменное имя сервера:
String serverName = "192.168.1.XXX"; // REPLACE WITH YOUR Raspberry Pi IP ADDRESS
//String serverName = "example.com"; // OR REPLACE WITH YOUR DOMAIN NAME
String serverPath = "/upload.php"; // The default serverPath should be upload.php
Загрузка кода в ESP32-CAM
Теперь вы можете загрузить код на плату ESP32-CAM. Подключите плату ESP32-CAM к компьютеру с помощью FTDI программатора.
Следуйте следующей схеме подключения:
Многие FTDI программаторы имеют перемычку, позволяющую выбрать 3.3В или 5В. Убедитесь, что перемычка установлена в правильное положение для выбора 5В.
Важно: GPIO 0 должен быть подключён к GND, чтобы можно было загрузить код.
ESP32-CAM |
FTDI программатор |
|---|---|
GND |
GND |
5V |
VCC (5V) |
U0R |
TX |
U0T |
RX |
GPIO 0 |
GND |
Для загрузки кода выполните следующие шаги:
Перейдите в Tools > Board и выберите AI-Thinker ESP32-CAM.
Перейдите в Tools > Port и выберите COM-порт, к которому подключён ESP32.
Затем нажмите кнопку загрузки для загрузки кода.
Когда вы начнёте видеть точки в окне отладки, как показано ниже, нажмите встроенную кнопку RST на ESP32-CAM.
Через несколько секунд код должен быть успешно загружен на вашу плату.
Если у вас возникли проблемы с загрузкой кода, прочитайте наше Руководство по устранению неполадок ESP32-CAM.
Как работает код
Вот краткое объяснение того, как работает код:
Импортирует все библиотеки;
Определяет необходимые переменные;
Определяет выводы камеры;
В setup() устанавливается Wi-Fi соединение и инициализируется камера ESP32.
В loop() есть таймер, который вызывает функцию sendPhoto() каждые 30 секунд. Вы можете изменить это время задержки в переменной timerInterval.
Функция sendPhoto() – это часть, которая фактически делает снимок и отправляет его на ваш сервер. Вы можете использовать эту функцию в других ваших проектах, которые требуют снятия и публикации фотографии на сервер.
4. Тестирование и финальная демонстрация
После загрузки кода на плату откройте монитор последовательного порта Arduino IDE, и вы должны увидеть подобное сообщение, выводимое каждые 30 секунд:
The file esp32-cam.jpg has been uploaded.
Если вы перейдёте по URL локального сервера http://IP-Address/uploads, или по URL облачного сервера https://example.com/uploads, вы должны увидеть папку со всеми сохранёнными фотографиями.
Вы можете открыть каждую ссылку для просмотра полноразмерного изображения на новой странице:
Теперь, если вы перейдёте по URL локального сервера http://IP-Address/gallery.php, или по URL облачного сервера https://example.com/gallery.php, вы сможете получить доступ к странице галереи, где можно просматривать и удалять фотографии.
Чтобы удалить любую фотографию, просто нажмите на ссылку «Delete file» рядом с каждым изображением.
Заключение
Вот и всё! Теперь вы можете отправлять фотографии ESP32-CAM на любой сервер с помощью HTTP POST. Модифицируйте этот проект под свои нужды. Например, можно делать фото и отправлять его на сервер при обнаружении движения.
Другие руководства по ESP32, которые могут вас заинтересовать: