Hi, I'm using an ESP Cam board, when I take a photo, I get one two shots old... If I take three photos then I get the latest shot!
Is this standard practice? Are there some intermediary hardware / software buffer chains? Is it possible to just take one shot and get that?
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
//#include <ESPAsyncWebServer.h>
#include <ESPAsyncWebSrv.h>
#include "esp_camera.h"
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
//#include "driver/rtc_io.h"
#include <EEPROM.h> // read and write from flash memory
#include "FS.h"
#include "SD_MMC.h" // SD Card ESP32
//#include "camdraw.h"
const char* ssid = "ssid"; //Write your own Wi-Fi name here
const char* password = "pass"; //Write your own password here
IPAddress local_IP(192, 168, 1, 200);
IPAddress gateway(192, 168, 1, 255);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8);
IPAddress secondaryDNS(8, 8, 4, 4);
AsyncWebServer server(80); //object created on default port 80
// define the number of bytes you want to access
#define EEPROM_SIZE 1
// 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
char webpage[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>CCam</title>
<style>
body(background-color:rgba(128,128,128, 0.8)}
</style>
</head>
<body>
<div>
<div style="width:90%">
<h2>CCam</h2>
<button onclick="photo()">Photo</button><br /><br />
</div>
<div>
<div id="" style="float:left;padding:15px;">
<b>Files</b><br />
<div>
<button onclick="fm_refresh()">Refresh</button>
</div>
<div id="div_dir" style="width:250px;height:300px;overflow:scroll;"></div>
</div>
<div id="" style="float:left;padding:15px;width:50%;">
<img src='' id='img1' name='img1' style="width:50%;">
</div>
</div>
<div style="width:90%;clear:left;">
<h2>FUNCTIONS</h2>
<button onclick="delete_all()">Delete All Images</button><br />
<button onclick="reset_count()">Reset Count</button><br />
</div>
</div>
<script>
function photo()
{
var req = new XMLHttpRequest();
req.onreadystatechange = function()
{
if(this.readyState == 4 && this.status == 200)
{
//document.getElementById("div_dir2").innerHTML = this.responseText;
}
};
req.open("GET", "photo", true);
req.send();
}
function img_set(fn) {
var pic = document.getElementById("img1");
//pic.src = "img?fn=picture4.jpg";
pic.src = "img?fn=" + fn;
}
function fm_refresh()
{
var req = new XMLHttpRequest();
req.onreadystatechange = function()
{
if(this.readyState == 4 && this.status == 200)
{
var data = JSON.parse(this.responseText);
var ss = "<ul>";
var len = data.length;
for(var i=0;i<len;i++ ){
ss += "<li onclick=\"img_set('" + data[i][1] + "')\">" + data[i][1] + "</li>";
}
ss += "</ul>";
document.getElementById("div_dir").innerHTML = ss;
}
};
req.open("GET", "ls", true);
req.send();
}
function delete_all()
{
var req = new XMLHttpRequest();
req.onreadystatechange = function()
{
if(this.readyState == 4 && this.status == 200)
{
//document.getElementById("div_dir2").innerHTML = this.responseText;
}
};
req.open("GET", "delete_all", true);
req.send();
}
function reset_count()
{
var req = new XMLHttpRequest();
req.onreadystatechange = function()
{
if(this.readyState == 4 && this.status == 200)
{
//document.getElementById("div_dir2").innerHTML = this.responseText;
}
};
req.open("GET", "reset_count", true);
req.send();
}
</script>
</body>
</html>)=====";
void init_wifi()
{
// Configures static IP address
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS))
{
Serial.println("Static IP Fail");
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("Connecting WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
void init_sd()
{
//if(!SD_MMC.begin())
if(!SD_MMC.begin("/sdcard", true)) // disables the flash!!! yeah!
{
Serial.println("Initialization Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("SD card is not present!");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
uint64_t card_free = SD_MMC.usedBytes(); // / (1024 * 1024);
//Serial.printf("SD Card Used: %lluMB\n", card_free);
Serial.printf("SD Card Used: %lluB\n", card_free);
}
int fs_file_exist(fs::FS &fs, const char * path){
File file = fs.open(path);
if(!file){
return 0; // file not exist
}
return 1; // file exists
}
String dir_list(fs::FS &fs, const char * dirname)
{
String s = "";
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return s;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return s;
}
s += "[";
File file = root.openNextFile();
while(file){
if(!file.isDirectory())
{
String fn = file.name();
if(fn.endsWith(".jpg"))
{
s += "[\"f\",\"" + fn + "\"],";
}
}
file = root.openNextFile();
}
s = s.substring(0, s.length()-1);
s += "]";
return s;
}
void cam_setup()
{
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; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
//pictureNumber = EEPROM.read(0) + 1;
//cam_pic();
}
void cam_pic()
{
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
// Take Picture with Camera
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
int pictureNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".jpg";
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n", path.c_str());
File file = fs.open(path.c_str(), FILE_WRITE);
if(!file){
Serial.println("Failed to open file in writing mode");
}
else {
file.write(fb->buf, fb->len); // payload (image), payload length
//file.write(rfb.data, sizeof(rfb.data)/sizeof(uint8_t)); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
file.close();
esp_camera_fb_return(fb);
}
/*
void cam_pic2()
{
// https://stackoverflow.com/questions/56384828/text-overlay-on-video-esp32
// 1- get the image from the camera:
camera_fb_t *fb = esp_camera_fb_get();
// 2- get the image matrix:
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
// 3- Change the format of the image stored in the image_matrix:
fmt2rgb888(fb->buf, fb->len, fb->format, image_matrix->item);
// 4- print the text of your choice over that matrix (FF is red color, you can use any uint32_t. Also, take a look at the rgb_print function posted in @sashok1337 response; you can modify it to send the position of the string):
rgb_print(image_matrix, 0x000000FF, "test");
// 4- Define a buffer and convert the martrix to jpg(you can play with the settings here):
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
bool jpeg_converted = fmt2jpg(image_matrix->item, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len);
// Once you finished working with the image_matrix, remember to free the memory used by the dl_matrix3du_alloc in the second point above (thank you, @fuenfundachtzig):
dl_matrix3du_free(image_matrix);
// The "frame" with the text over it will be stored in the _jpg_buf array
esp_camera_fb_return(fb);
}
void cam_pic3()
{
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
// Take Picture with Camera
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
// Take Picture with Camera
//fb = esp_camera_fb_get();
//if(!fb) {
// Serial.println("Camera capture failed");
// return;
//}
size_t out_len, out_width, out_height;
uint8_t *out_buf;
bool s;
out_len = fb->width * fb->height * 3;
out_width = fb->width;
out_height = fb->height;
out_buf = (uint8_t*)malloc(out_len);
if (!out_buf) {
Serial.println("out_buf malloc failed");
}
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf);
esp_camera_fb_return(fb);
if (!s) {
free(out_buf);
Serial.println("Camera capture failed: to rgb888 failed");
}
fb_data_t rfb;
rfb.width = fb->width;
rfb.height = fb->height;
rfb.data = fb->buf;
//rfb.bytes_per_pixel = 2;
//rfb.format = FB_RGB565; // 2
rfb.bytes_per_pixel = 3;
rfb.format = FB_BGR888;
//rgb_print(fb_data_t *fb, uint32_t color, const char *str)
rgb_print(&rfb, FACE_COLOR_RED, "HELLO");
//s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk);
free(out_buf);
int pictureNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".jpg";
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n", path.c_str());
File file = fs.open(path.c_str(), FILE_WRITE);
if(!file){
Serial.println("Failed to open file in writing mode");
}
else {
//file.write(fb->buf, fb->len); // payload (image), payload length
file.write(rfb.data, sizeof(rfb.data)/sizeof(uint8_t)); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
file.close();
esp_camera_fb_return(fb);
}
*/
void eeprom_reset_count()
{
EEPROM.write(0, 0); // addr, val
}
void sd_delete_file(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\n", path);
if(fs.remove(path)){
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void sd_delete_all_images(fs::FS &fs, const char * dirname)
{
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(!file.isDirectory())
{
String fn = file.name();
if(fn.endsWith(".jpg"))
{
fn = "/" + fn;
sd_delete_file(fs, fn.c_str());
}
}
file = root.openNextFile();
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
init_sd();
init_wifi();
cam_setup();
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/
request->send(200, "text/html", webpage);
});
server.on("/img", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/img?fn=picture4.jpg
bool flag = true;
if(request->hasParam("fn", false)) // for GET
{
String fn = request->getParam("fn", false)->value(); // for GET
fn = "/" + fn;
if(fs_file_exist(SD_MMC, fn.c_str())){
flag = false;
// RETURN REQUESTED IMAGE
request->send(SD_MMC, fn.c_str(), "image/jpeg");
}
}
if(flag)
{
// RETURN DEFAULT IMAGE (e.g. latest, first)
request->send(SD_MMC, "/picture3.jpg", "image/jpeg");
}
});
server.on("/ls", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/ls
//String msg = "[[\"f\", \"picture4.jpg\"],[\"f\",\"picture3.jpg\"]]";
String msg = dir_list(SD_MMC, "/");
Serial.println(msg);
request->send(200, "application/json", msg);
});
server.on("/photo", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/photo
cam_pic();
request->send(200, "text/html", "ok");
});
server.on("/delete_all", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/delete_all
//cam_pic();
sd_delete_all_images(SD_MMC, "/");
request->send(200, "text/html", "ok");
});
server.on("/reset_count", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/delete_all
//cam_pic();
eeprom_reset_count();
request->send(200, "text/html", "ok");
});
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request)
{
// http://192.168.1.200/favicon.ico
//String ico = "<svg height=\"210\" width=\"500\"><polygon points=\"100,10 40,198 190,78 10,78 160,198\" style=\"fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;\"/></svg>";
String ico = "<svg height=\"100\" width=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><ellipse cx=\"50\" cy=\"50\" rx=\"45\" ry=\"45\" style=\"fill:yellow;stroke:purple;stroke-width:2\" /><text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" fill=\"red\">CCam</text></svg>";
request->send(200, "image/svg+xml", ico);
});
server.begin();
}
void loop() {
// put your main code here, to run repeatedly:
}
On a side note...
Can anyone finish the cam_pic3() function? It is attempting to draw text and lines on an image and then save it. You'll need the extra src for this task if you decide to accept it
#include "esp_camera.h"
#include "img_converters.h"
#include "fb_gfx.h"
#define FACE_COLOR_WHITE 0x00FFFFFF
#define FACE_COLOR_BLACK 0x00000000
#define FACE_COLOR_RED 0x000000FF
#define FACE_COLOR_GREEN 0x0000FF00
#define FACE_COLOR_BLUE 0x00FF0000
#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN)
#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN)
#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED)
static void rgb_print(fb_data_t *fb, uint32_t color, const char *str)
{
fb_gfx_print(fb, (fb->width - (strlen(str) * 14)) / 2, 10, color, str);
}
static int rgb_printf(fb_data_t *fb, uint32_t color, const char *format, ...)
{
char loc_buf[64];
char *temp = loc_buf;
int len;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg);
va_end(copy);
if (len >= sizeof(loc_buf))
{
temp = (char *)malloc(len + 1);
if (temp == NULL)
{
return 0;
}
}
vsnprintf(temp, len + 1, format, arg);
va_end(arg);
rgb_print(fb, color, temp);
if (len > 64)
{
free(temp);
}
return len;
}
Thanks
REFS
ESP Project: CameraWebServer