Das ESP32-CAM-Board besitzt einen Slot für den Zugriff auf eine Mikro-SD-Karte von max. 4GB Größe. Hierauf können z.B. aufgenommene Fotos gespeichert werden. Eine andere Möglichkeit ist, die aufgenommenen Fotos auf einem Server zu sichern.
Der grundsätzliche Aufbau und die Einrichtung, um das ESP32-CAM-Board programmieren zu können,
wird in dem Beitrag ESP32-CAM - Video Streaming Server beschrieben.
Zusätzlich dazu sollte die verwendete SD-Karte mit dem PC als FAT32
formatiert werden.
Größere SD-Karten als 4GB können angeblich auch verwendet werden, allerdings werden nur die ersten 4GB verwendet.
Dies habe ich aber nicht getestet.
Der folgende Sketch initialisiert zunächst die Kamera und speichert dann in loop()
alle 5 Sekunden
ein Foto auf der SD-Karte. Bei einem Reset des ESP32-CAM werden die zuvor gespeicherten Fotos überschrieben.
#include "esp_camera.h"
#include "FS.h"
#include "SD_MMC.h"
// Pin definition for 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
int imageNumber = 1;
char fileNumber[6];
void setup()
{
Serial.begin(115200);
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 = 10;
config.fb_count = 1;
}
Serial.print("Camera init...");
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("error 0x%x", err);
while (true) {}
}
Serial.println("ok");
Serial.print("SD-Card init...");
if (!SD_MMC.begin()) {
Serial.println("failed!");
return;
}
Serial.println("ok");
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_MMC) {
Serial.println("Typ: MMC");
} else if (cardType == CARD_SD) {
Serial.println("Typ: SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("Typ: SDHC");
} else if (cardType == CARD_NONE) {
Serial.println("No SD-Card attached!");
return;
} else {
Serial.println("Typ: UNKNOWN");
return;
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD-Card Size: %lluMB\n", cardSize);
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
}
void loop()
{
camera_fb_t * frameBuffer = NULL;
// Take Picture with Camera
frameBuffer = esp_camera_fb_get();
if (!frameBuffer) {
Serial.println("Camera capture failed");
return;
}
sprintf(fileNumber, "%05d", imageNumber++);
String imageFilename = "/image"+String(fileNumber)+".jpg";
fs::FS &fs = SD_MMC;
File imgFile = fs.open(imageFilename.c_str(), FILE_WRITE);
if (!imgFile) {
Serial.println("Failed to open file in writing mode");
} else {
imgFile.write(frameBuffer->buf, frameBuffer->len);
Serial.println("Saved " + imageFilename);
imageNumber++;
}
imgFile.close();
esp_camera_fb_return(frameBuffer);
delay(5000);
}
Die aufgenommenen Standbilder können auch anstatt lokal auf der SD-Karte gespeichert zu werden auf einen externen WWW-Server hochgeladen werden, wo auf sie sofort und von jedem Ort der Welt zugegriffen werden könnten. Zunächst wird ein Sketch benötigt, der die Bilder von der ESP32-CAM aufnimmt und an eine Gegenstelle im WWW verschickt (per HTTP Post), in den folgenden Fall passiert dies alle 10 Sekunden:
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#define WIFI_SSID "mySSID"
#define WIFI_PASSWORD "myPassword"
#define SERVER_HOSTNAME "esp32cam.example.com"
#define SERVER_PATHNAME "/esp32cam-upload.php"
#define SERVER_PORT 80
// Pin definition for 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
WiFiClient client;
void setup()
{
Serial.begin(115200);
Serial.print("WiFi init...");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println("ok");
Serial.print("Camera init...");
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;
// 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;
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("error 0x%x", err);
while (true) {}
}
Serial.println("ok");
}
void loop()
{
uploadImage();
delay(10000);
}
void uploadImage()
{
String getAll;
String getBody;
Serial.print("Capturing image...");
camera_fb_t * frameBuffer = NULL;
// Take Picture with Camera
frameBuffer = esp_camera_fb_get();
if (!frameBuffer) {
Serial.println("failed!");
return;
}
Serial.println("ok");
Serial.print("Connect server...");
if (!client.connect(SERVER_HOSTNAME, SERVER_PORT)) {
Serial.println("failed!");
return;
}
Serial.println("ok");
Serial.print("Uploading image...");
String head = "--ESP32CAM\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"image.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--ESP32CAM--\r\n";
uint32_t totalLen = frameBuffer->len + head.length() + tail.length();
client.println("POST " + String(SERVER_PATHNAME) + " HTTP/1.1");
client.println("Host: " + String(SERVER_HOSTNAME));
client.println("Content-Length: " + String(totalLen));
client.println("Content-Type: multipart/form-data; boundary=ESP32CAM");
client.println();
client.print(head);
uint8_t *fbBuf = frameBuffer->buf;
size_t fbLen = frameBuffer->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);
Serial.println("ok");
esp_camera_fb_return(frameBuffer);
int timoutTimer = 10000;
long startTimer = millis();
boolean state = false;
while ((startTimer + timoutTimer) > millis()) {
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;
}
}
client.stop();
Serial.println("Result:" + String(getBody));
}
Auf dem WWW-Server muss nun ein Programm vorhanden sein, welches die hochgeladenen Bilder in Empfang nimmt und abspeichert. Dies wird in diesem Falle mit einem PHP-Script gelöst:
<?php
define('IMAGES_PATH', 'esp32cam-uploads/');
$imageFiletype = strtolower(pathinfo($_FILES['imageFile']['name'], PATHINFO_EXTENSION));
$imageFilename = IMAGES_PATH . 'img-' . time() . '.' . $imageFiletype;
if (isset($_POST['submit'])) {
$check = getimagesize($_FILES['imageFile']['tmp_name']);
if ($check === false) {
logAction('ERROR: Uploaded file is not an image!');
}
}
if (file_exists($imageFilename)) {
logAction('ERROR: Uploaded file already exists!');
}
if ($_FILES['imageFile']['size'] > 500000) {
logAction('ERROR: Uploaded too large!');
}
if (!in_array($imageFiletype, ['jpg', 'jpeg', 'png'])) {
logAction('ERROR: Only JPG, JPEG and PNG formats are allowed! Give');
}
if (move_uploaded_file($_FILES['imageFile']['tmp_name'], $imageFilename)) {
logAction('SUCCESS: Uploaded and saves ' . basename($_FILES['imageFile']['name']) . ' » ' . $imageFilename);
} else {
logAction('ERROR: Server error on uploading file!');
}
function logAction($message) {
$message = date('c', time()) . ' - ' . $message . ' (IP: ' . $_SERVER['REMOTE_ADDR'] . ')' . PHP_EOL;
error_log($message, 3, IMAGES_PATH . 'actions.log');
exit($message);
}
zurück