Luc 3ffb7c3b4c
Fix FTP not listing on some shared SD (#958)
* Fix Rename not working on GlobalFS

* esp3d_log refactoring
 - Rename Debug to Log
 - Allow now 4 level of log: None, Error, Debug, Verbose
 - Change original verbose log to error log when it is an error

* Update configuration.h to add debug level expected

* Add log for critical steps in FTP module
2023-10-27 12:40:22 +08:00

384 lines
12 KiB
C++

/*
camera.cpp - camera functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with This code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../../include/esp3d_config.h"
#ifdef CAMERA_DEVICE
#include <WebServer.h>
#include <esp_camera.h>
#include <soc/rtc_cntl_reg.h>
#include <soc/soc.h> //not sure this one is needed
#include "../../core/esp3d.h"
#include "../../core/esp3doutput.h"
#include "camera.h"
#if defined(SD_DEVICE)
#include "../filesystem/esp_sd.h"
#endif // SD_DEVICE
#define DEFAULT_FRAME_SIZE FRAMESIZE_SVGA
#define JPEG_COMPRESSION 80
Camera esp3d_camera;
bool Camera::handle_snap(WebServer *webserver, const char *path,
const char *filename) {
log_esp3d("Camera stream reached");
if (!_initialised) {
log_esp3d_e("Camera not started");
if (webserver) {
webserver->send(500, "text/plain", "Camera not started");
}
return false;
}
sensor_t *s = esp_camera_sensor_get();
if (webserver) {
if (webserver->hasArg("framesize")) {
if (s->status.framesize != webserver->arg("framesize").toInt()) {
command("framesize", webserver->arg("framesize").c_str());
}
}
if (webserver->hasArg("hmirror")) {
command("hmirror", webserver->arg("hmirror").c_str());
}
if (webserver->hasArg("vflip")) {
command("vflip", webserver->arg("vflip").c_str());
}
if (webserver->hasArg("wb_mode")) {
command("wb_mode", webserver->arg("wb_mode").c_str());
}
#ifdef ESP_ACCESS_CONTROL_ALLOW_ORIGIN
webserver->enableCrossOrigin(true);
#endif // ESP_ACCESS_CONTROL_ALLOw_ORIGIN
}
camera_fb_t *fb = NULL;
bool res_error = false;
size_t _jpg_buf_len = 0;
uint8_t *_jpg_buf = NULL;
if (webserver) {
webserver->sendHeader(String(F("Content-Type")), String(F("image/jpeg")),
true);
webserver->sendHeader(String(F("Content-Disposition")),
String(F("inline; filename=capture.jpg")), true);
webserver->setContentLength(CONTENT_LENGTH_UNKNOWN);
webserver->send(200);
}
log_esp3d("Camera capture ongoing");
fb = esp_camera_fb_get();
if (!fb) {
log_esp3d_e("Camera capture failed");
if (webserver) {
webserver->send(500, "text/plain", "Capture failed");
}
res_error = true;
} else {
if (fb->format != PIXFORMAT_JPEG) {
bool jpeg_converted =
frame2jpg(fb, JPEG_COMPRESSION, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if (!jpeg_converted) {
log_esp3d_e("JPEG compression failed");
res_error = true;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
if (!res_error) {
if (webserver) {
webserver->sendContent_P((const char *)_jpg_buf, _jpg_buf_len);
}
#if defined(SD_DEVICE)
if (filename != nullptr && path != nullptr) {
if (!ESP_SD::accessFS()) {
res_error = true;
log_esp3d_e("SD not available");
} else {
if (ESP_SD::getState(true) == ESP_SDCARD_NOT_PRESENT) {
res_error = true;
log_esp3d_e("No SD");
} else {
ESP_SD::setState(ESP_SDCARD_BUSY);
String wpath = path[0] == '/' ? path : String("/") + path;
if (!ESP_SD::exists(wpath.c_str())) {
res_error = !ESP_SD::mkdir(wpath.c_str());
}
if (!res_error) {
if (wpath[wpath.length() - 1] != '/') {
wpath += "/";
}
wpath += filename;
ESP_SDFile f = ESP_SD::open(wpath.c_str(), ESP_FILE_WRITE);
if (f) {
f.write((const uint8_t *)_jpg_buf, _jpg_buf_len);
f.close();
log_esp3d("Camera capture done");
} else {
res_error = true;
log_esp3d_e("Failed to open file for writing");
}
}
}
ESP_SD::releaseFS();
}
}
#endif // SD_DEVICE
if (!webserver && filename == nullptr && path == nullptr) {
log_esp3d_e("No output defined");
res_error = true;
}
}
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if (_jpg_buf) {
free(_jpg_buf);
_jpg_buf = NULL;
}
if (webserver) {
webserver->sendContent("");
}
return !res_error;
}
Camera::Camera() {
_started = false;
_initialised = false;
}
Camera::~Camera() { end(); }
int Camera::command(const char *param, const char *value) {
log_esp3d("Camera: %s=%s\n", param, value);
int res = 0;
int val = atoi(value);
sensor_t *s = esp_camera_sensor_get();
if (s == nullptr) {
res = -1;
}
#if CAM_LED_PIN != -1
if (!strcmp(param, "light")) {
digitalWrite(CAM_LED_PIN, val == 1 ? HIGH : LOW);
} else
#endif // CAM_LED_PIN
if (!strcmp(param, "framesize")) {
if (s->pixformat == PIXFORMAT_JPEG) {
res = s->set_framesize(s, (framesize_t)val);
}
} else if (!strcmp(param, "quality")) {
res = s->set_quality(s, val);
} else if (!strcmp(param, "contrast")) {
res = s->set_contrast(s, val);
} else if (!strcmp(param, "brightness")) {
res = s->set_brightness(s, val);
} else if (!strcmp(param, "saturation")) {
res = s->set_saturation(s, val);
} else if (!strcmp(param, "gainceiling")) {
res = s->set_gainceiling(s, (gainceiling_t)val);
} else if (!strcmp(param, "colorbar")) {
res = s->set_colorbar(s, val);
} else if (!strcmp(param, "awb")) {
res = s->set_whitebal(s, val);
} else if (!strcmp(param, "agc")) {
res = s->set_gain_ctrl(s, val);
} else if (!strcmp(param, "aec")) {
res = s->set_exposure_ctrl(s, val);
} else if (!strcmp(param, "hmirror")) {
res = s->set_hmirror(s, val);
} else if (!strcmp(param, "vflip")) {
res = s->set_vflip(s, val);
} else if (!strcmp(param, "awb_gain")) {
res = s->set_awb_gain(s, val);
} else if (!strcmp(param, "agc_gain")) {
res = s->set_agc_gain(s, val);
} else if (!strcmp(param, "aec_value")) {
res = s->set_aec_value(s, val);
} else if (!strcmp(param, "aec2")) {
res = s->set_aec2(s, val);
} else if (!strcmp(param, "dcw")) {
res = s->set_dcw(s, val);
} else if (!strcmp(param, "bpc")) {
res = s->set_bpc(s, val);
} else if (!strcmp(param, "wpc")) {
res = s->set_wpc(s, val);
} else if (!strcmp(param, "raw_gma")) {
res = s->set_raw_gma(s, val);
} else if (!strcmp(param, "lenc")) {
res = s->set_lenc(s, val);
} else if (!strcmp(param, "special_effect")) {
res = s->set_special_effect(s, val);
} else if (!strcmp(param, "wb_mode")) {
res = s->set_wb_mode(s, val);
} else if (!strcmp(param, "ae_level")) {
res = s->set_ae_level(s, val);
} else {
res = -1;
}
return res;
}
bool Camera::initHardware() {
_initialised = false;
log_esp3d("Disable brown out");
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
stopHardware();
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;
config.jpeg_quality = 5;
config.fb_count = 1;
config.frame_size = DEFAULT_FRAME_SIZE;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.grab_mode = CAMERA_GRAB_LATEST;
if (!psramFound()) {
_initialised = false;
log_esp3d_e("psram is not enabled");
return false;
}
log_esp3d("Init camera");
#if CAM_PULLUP1 != -1
pinMode(CAM_PULLUP1, INPUT_PULLUP);
#endif // CAM_PULLUP1
#if CAM_PULLUP2 != -1
pinMode(CAM_PULLUP2, INPUT_PULLUP);
#endif // CAM_PULLUP2
#if CAM_LED_PIN != -1
pinMode(CAM_LED_PIN, OUTPUT);
digitalWrite(CAM_LED_PIN, LOW);
#endif // CAM_LED_PIN
// initialize the camera
// https://github.com/espressif/esp32-camera/issues/66#issuecomment-526283681
#if CAMERA_DEVICE == CAMERA_MODEL_AI_THINKER
log_esp3d("Specific config for CAMERA_MODEL_AI_THINKER");
gpio_config_t gpio_pwr_config;
gpio_pwr_config.pin_bit_mask = (1ULL << 32);
gpio_pwr_config.mode = GPIO_MODE_OUTPUT;
gpio_pwr_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_pwr_config.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_pwr_config.intr_type = GPIO_INTR_DISABLE;
gpio_config(&gpio_pwr_config);
gpio_set_level(GPIO_NUM_32, 0);
#endif // CAMERA_DEVICE == CAMERA_MODEL_AI_THINKER
delay(500);
log_esp3d("Init camera config");
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
log_esp3d_e("Camera init failed with error 0x%x", err);
} else {
_initialised = true;
}
return _initialised;
}
bool Camera::stopHardware() { return true; }
// need to be call by device and by network
bool Camera::begin() {
end();
log_esp3d("Begin camera");
if (!_initialised) {
log_esp3d("Init hardware not done");
return false;
}
log_esp3d("Init camera sensor settings");
sensor_t *s = esp_camera_sensor_get();
if (s != nullptr) {
// initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_brightness(s, 1); // up the blightness just a bit
s->set_saturation(s, -2); // lower the saturation
}
s->set_framesize(s, DEFAULT_FRAME_SIZE);
#if defined(CAMERA_DEVICE_FLIP_HORIZONTALY)
s->set_hmirror(s, 1);
#endif // CAMERA_DEVICE_FLIP_HORIZONTALY
#if defined(CAMERA_DEVICE_FLIP_VERTICALY)
s->set_vflip(s, 1);
#endif // CAMERA_DEVICE_FLIP_VERTICALY
} else {
log_esp3d("Cannot access camera sensor");
}
_started = _initialised;
return _started;
}
void Camera::end() { _started = false; }
void Camera::handle() {
// nothing to do
}
uint8_t Camera::GetModel() { return CAMERA_DEVICE; }
const char *Camera::GetModelString() {
#if defined(CUSTOM_CAMERA_NAME)
return CUSTOM_CAMERA_NAME;
#else
switch (CAMERA_DEVICE) {
case CAMERA_MODEL_WROVER_KIT:
return "WROVER Kit";
break;
case CAMERA_MODEL_ESP32S3_EYE:
case CAMERA_MODEL_ESP_EYE:
return "ESP Eye";
break;
case CAMERA_MODEL_M5STACK_WIDE:
case CAMERA_MODEL_M5STACK_V2_PSRAM:
case CAMERA_MODEL_M5STACK_PSRAM:
return "M5Stack";
break;
case CAMERA_MODEL_ESP32_CAM_BOARD:
case CAMERA_MODEL_ESP32S2_CAM_BOARD:
case CAMERA_MODEL_ESP32S3_CAM_LCD:
case CAMERA_MODEL_AI_THINKER:
return "ESP32 Cam";
break;
default:
return "Unknow Camera";
}
#endif // CUSTOM_CAMERA_NAME
}
#endif // CAMERA_DEVICE