Skip to content

Commit 44042e0

Browse files
committed
Fix OTA and seperate camera init
1 parent 31c381d commit 44042e0

File tree

1 file changed

+166
-155
lines changed

1 file changed

+166
-155
lines changed

esp32-cam-webserver.ino

Lines changed: 166 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <esp_task_wdt.h>
44
#include <WiFi.h>
55
#include <DNSServer.h>
6+
#include <WiFiUdp.h>
67
#include <ArduinoOTA.h>
78
#include "src/parsebytes.h"
89
#include "time.h"
@@ -53,6 +54,9 @@
5354
// Pin Mappings
5455
#include "camera_pins.h"
5556

57+
// Camera config structure
58+
camera_config_t config;
59+
5660
// Internal filesystem (SPIFFS)
5761
// used for non-volatile camera settings
5862
#include "storage.h"
@@ -303,6 +307,142 @@ void calcURLs() {
303307
#endif
304308
}
305309

310+
void StartCamera() {
311+
// Populate camera config structure with hardware and other defaults
312+
config.ledc_channel = LEDC_CHANNEL_0;
313+
config.ledc_timer = LEDC_TIMER_0;
314+
config.pin_d0 = Y2_GPIO_NUM;
315+
config.pin_d1 = Y3_GPIO_NUM;
316+
config.pin_d2 = Y4_GPIO_NUM;
317+
config.pin_d3 = Y5_GPIO_NUM;
318+
config.pin_d4 = Y6_GPIO_NUM;
319+
config.pin_d5 = Y7_GPIO_NUM;
320+
config.pin_d6 = Y8_GPIO_NUM;
321+
config.pin_d7 = Y9_GPIO_NUM;
322+
config.pin_xclk = XCLK_GPIO_NUM;
323+
config.pin_pclk = PCLK_GPIO_NUM;
324+
config.pin_vsync = VSYNC_GPIO_NUM;
325+
config.pin_href = HREF_GPIO_NUM;
326+
config.pin_sscb_sda = SIOD_GPIO_NUM;
327+
config.pin_sscb_scl = SIOC_GPIO_NUM;
328+
config.pin_pwdn = PWDN_GPIO_NUM;
329+
config.pin_reset = RESET_GPIO_NUM;
330+
config.xclk_freq_hz = xclk * 1000000;
331+
config.pixel_format = PIXFORMAT_JPEG;
332+
config.grab_mode = CAMERA_GRAB_LATEST;
333+
// Pre-allocate large buffers
334+
if(psramFound()){
335+
config.frame_size = FRAMESIZE_UXGA;
336+
config.jpeg_quality = 10;
337+
config.fb_count = 2;
338+
} else {
339+
config.frame_size = FRAMESIZE_SVGA;
340+
config.jpeg_quality = 12;
341+
config.fb_count = 1;
342+
}
343+
344+
#if defined(CAMERA_MODEL_ESP_EYE)
345+
pinMode(13, INPUT_PULLUP);
346+
pinMode(14, INPUT_PULLUP);
347+
#endif
348+
349+
// camera init
350+
esp_err_t err = esp_camera_init(&config);
351+
if (err != ESP_OK) {
352+
delay(100); // need a delay here or the next serial o/p gets missed
353+
Serial.printf("\r\n\r\nCRITICAL FAILURE: Camera sensor failed to initialise.\r\n\r\n");
354+
Serial.printf("A full (hard, power off/on) reboot will probably be needed to recover from this.\r\n");
355+
Serial.printf("Meanwhile; this unit will reboot in 1 minute since these errors sometime clear automatically\r\n");
356+
// Reset the I2C bus.. may help when rebooting.
357+
periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly in case that is the problem
358+
periph_module_disable(PERIPH_I2C1_MODULE);
359+
periph_module_reset(PERIPH_I2C0_MODULE);
360+
periph_module_reset(PERIPH_I2C1_MODULE);
361+
// And set the error text for the UI
362+
critERR = "<h1>Error!</h1><hr><p>Camera module failed to initialise!</p><p>Please reset (power off/on) the camera.</p>";
363+
critERR += "<p>We will continue to reboot once per minute since this error sometimes clears automatically.</p>";
364+
// Start a 60 second watchdog timer
365+
esp_task_wdt_init(60,true);
366+
esp_task_wdt_add(NULL);
367+
} else {
368+
Serial.println("Camera init succeeded");
369+
370+
// Get a reference to the sensor
371+
sensor_t * s = esp_camera_sensor_get();
372+
373+
// Dump camera module, warn for unsupported modules.
374+
switch (s->id.PID) {
375+
case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); break;
376+
case OV7725_PID: Serial.println("WARNING: OV7725 camera module is not properly supported, will fallback to OV2640 operation"); break;
377+
case OV2640_PID: Serial.println("OV2640 camera module detected"); break;
378+
case OV3660_PID: Serial.println("OV3660 camera module detected"); break;
379+
default: Serial.println("WARNING: Camera module is unknown and not properly supported, will fallback to OV2640 operation");
380+
}
381+
382+
// OV3660 initial sensors are flipped vertically and colors are a bit saturated
383+
if (s->id.PID == OV3660_PID) {
384+
s->set_vflip(s, 1); //flip it back
385+
s->set_brightness(s, 1); //up the blightness just a bit
386+
s->set_saturation(s, -2); //lower the saturation
387+
}
388+
389+
// M5 Stack Wide has special needs
390+
#if defined(CAMERA_MODEL_M5STACK_WIDE)
391+
s->set_vflip(s, 1);
392+
s->set_hmirror(s, 1);
393+
#endif
394+
395+
// Config can override mirror and flip
396+
#if defined(H_MIRROR)
397+
s->set_hmirror(s, H_MIRROR);
398+
#endif
399+
#if defined(V_FLIP)
400+
s->set_vflip(s, V_FLIP);
401+
#endif
402+
403+
// set initial frame rate
404+
#if defined(DEFAULT_RESOLUTION)
405+
s->set_framesize(s, DEFAULT_RESOLUTION);
406+
#else
407+
s->set_framesize(s, FRAMESIZE_SVGA);
408+
#endif
409+
410+
/*
411+
* Add any other defaults you want to apply at startup here:
412+
* uncomment the line and set the value as desired (see the comments)
413+
*
414+
* these are defined in the esp headers here:
415+
* https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h#L149
416+
*/
417+
418+
//s->set_framesize(s, FRAMESIZE_SVGA); // FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)]);
419+
//s->set_quality(s, val); // 10 to 63
420+
//s->set_brightness(s, 0); // -2 to 2
421+
//s->set_contrast(s, 0); // -2 to 2
422+
//s->set_saturation(s, 0); // -2 to 2
423+
//s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
424+
//s->set_whitebal(s, 1); // aka 'awb' in the UI; 0 = disable , 1 = enable
425+
//s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
426+
//s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
427+
//s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
428+
//s->set_aec2(s, 0); // 0 = disable , 1 = enable
429+
//s->set_ae_level(s, 0); // -2 to 2
430+
//s->set_aec_value(s, 300); // 0 to 1200
431+
//s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable
432+
//s->set_agc_gain(s, 0); // 0 to 30
433+
//s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6
434+
//s->set_bpc(s, 0); // 0 = disable , 1 = enable
435+
//s->set_wpc(s, 1); // 0 = disable , 1 = enable
436+
//s->set_raw_gma(s, 1); // 0 = disable , 1 = enable
437+
//s->set_lenc(s, 1); // 0 = disable , 1 = enable
438+
//s->set_hmirror(s, 0); // 0 = disable , 1 = enable
439+
//s->set_vflip(s, 0); // 0 = disable , 1 = enable
440+
//s->set_dcw(s, 1); // 0 = disable , 1 = enable
441+
//s->set_colorbar(s, 0); // 0 = disable , 1 = enable
442+
}
443+
// We now have camera with default init
444+
}
445+
306446
void WifiSetup() {
307447
// Feedback that we are now attempting to connect
308448
flashLED(300);
@@ -529,148 +669,15 @@ void setup() {
529669
delay(200); // a short delay to let spi bus settle after SPIFFS init
530670
}
531671

532-
// Create camera config structure; and populate with hardware and other defaults
533-
camera_config_t config;
534-
config.ledc_channel = LEDC_CHANNEL_0;
535-
config.ledc_timer = LEDC_TIMER_0;
536-
config.pin_d0 = Y2_GPIO_NUM;
537-
config.pin_d1 = Y3_GPIO_NUM;
538-
config.pin_d2 = Y4_GPIO_NUM;
539-
config.pin_d3 = Y5_GPIO_NUM;
540-
config.pin_d4 = Y6_GPIO_NUM;
541-
config.pin_d5 = Y7_GPIO_NUM;
542-
config.pin_d6 = Y8_GPIO_NUM;
543-
config.pin_d7 = Y9_GPIO_NUM;
544-
config.pin_xclk = XCLK_GPIO_NUM;
545-
config.pin_pclk = PCLK_GPIO_NUM;
546-
config.pin_vsync = VSYNC_GPIO_NUM;
547-
config.pin_href = HREF_GPIO_NUM;
548-
config.pin_sscb_sda = SIOD_GPIO_NUM;
549-
config.pin_sscb_scl = SIOC_GPIO_NUM;
550-
config.pin_pwdn = PWDN_GPIO_NUM;
551-
config.pin_reset = RESET_GPIO_NUM;
552-
config.xclk_freq_hz = xclk * 1000000;
553-
config.pixel_format = PIXFORMAT_JPEG;
554-
config.grab_mode = CAMERA_GRAB_LATEST;
555-
// Pre-allocate large buffers
556-
if(psramFound()){
557-
config.frame_size = FRAMESIZE_UXGA;
558-
config.jpeg_quality = 10;
559-
config.fb_count = 2;
560-
} else {
561-
config.frame_size = FRAMESIZE_SVGA;
562-
config.jpeg_quality = 12;
563-
config.fb_count = 1;
564-
}
672+
// Start (init) the camera
673+
StartCamera();
565674

566-
#if defined(CAMERA_MODEL_ESP_EYE)
567-
pinMode(13, INPUT_PULLUP);
568-
pinMode(14, INPUT_PULLUP);
569-
#endif
570-
571-
// camera init
572-
esp_err_t err = esp_camera_init(&config);
573-
if (err != ESP_OK) {
574-
delay(100); // need a delay here or the next serial o/p gets missed
575-
Serial.printf("\r\n\r\nCRITICAL FAILURE: Camera sensor failed to initialise.\r\n\r\n");
576-
Serial.printf("A full (hard, power off/on) reboot will probably be needed to recover from this.\r\n");
577-
Serial.printf("Meanwhile; this unit will reboot in 1 minute since these errors sometime clear automatically\r\n");
578-
// Reset the I2C bus.. may help when rebooting.
579-
periph_module_disable(PERIPH_I2C0_MODULE); // try to shut I2C down properly in case that is the problem
580-
periph_module_disable(PERIPH_I2C1_MODULE);
581-
periph_module_reset(PERIPH_I2C0_MODULE);
582-
periph_module_reset(PERIPH_I2C1_MODULE);
583-
// And set the error text for the UI
584-
critERR = "<h1>Error!</h1><hr><p>Camera module failed to initialise!</p><p>Please reset (power off/on) the camera.</p>";
585-
critERR += "<p>We will continue to reboot once per minute since this error sometimes clears automatically.</p>";
586-
// Start a 60 second watchdog timer
587-
esp_task_wdt_init(60,true);
588-
esp_task_wdt_add(NULL);
675+
// Now load and apply any saved preferences
676+
if (filesystem) {
677+
delay(200); // a short delay to let spi bus settle after camera init
678+
loadPrefs(SPIFFS);
589679
} else {
590-
Serial.println("Camera init succeeded");
591-
592-
// Get a reference to the sensor
593-
sensor_t * s = esp_camera_sensor_get();
594-
595-
// Dump camera module, warn for unsupported modules.
596-
switch (s->id.PID) {
597-
case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); break;
598-
case OV7725_PID: Serial.println("WARNING: OV7725 camera module is not properly supported, will fallback to OV2640 operation"); break;
599-
case OV2640_PID: Serial.println("OV2640 camera module detected"); break;
600-
case OV3660_PID: Serial.println("OV3660 camera module detected"); break;
601-
default: Serial.println("WARNING: Camera module is unknown and not properly supported, will fallback to OV2640 operation");
602-
}
603-
604-
// OV3660 initial sensors are flipped vertically and colors are a bit saturated
605-
if (s->id.PID == OV3660_PID) {
606-
s->set_vflip(s, 1); //flip it back
607-
s->set_brightness(s, 1); //up the blightness just a bit
608-
s->set_saturation(s, -2); //lower the saturation
609-
}
610-
611-
// M5 Stack Wide has special needs
612-
#if defined(CAMERA_MODEL_M5STACK_WIDE)
613-
s->set_vflip(s, 1);
614-
s->set_hmirror(s, 1);
615-
#endif
616-
617-
// Config can override mirror and flip
618-
#if defined(H_MIRROR)
619-
s->set_hmirror(s, H_MIRROR);
620-
#endif
621-
#if defined(V_FLIP)
622-
s->set_vflip(s, V_FLIP);
623-
#endif
624-
625-
// set initial frame rate
626-
#if defined(DEFAULT_RESOLUTION)
627-
s->set_framesize(s, DEFAULT_RESOLUTION);
628-
#else
629-
s->set_framesize(s, FRAMESIZE_SVGA);
630-
#endif
631-
632-
/*
633-
* Add any other defaults you want to apply at startup here:
634-
* uncomment the line and set the value as desired (see the comments)
635-
*
636-
* these are defined in the esp headers here:
637-
* https://github.com/espressif/esp32-camera/blob/master/driver/include/sensor.h#L149
638-
*/
639-
640-
//s->set_framesize(s, FRAMESIZE_SVGA); // FRAMESIZE_[QQVGA|HQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA|QXGA(ov3660)]);
641-
//s->set_quality(s, val); // 10 to 63
642-
//s->set_brightness(s, 0); // -2 to 2
643-
//s->set_contrast(s, 0); // -2 to 2
644-
//s->set_saturation(s, 0); // -2 to 2
645-
//s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
646-
//s->set_whitebal(s, 1); // aka 'awb' in the UI; 0 = disable , 1 = enable
647-
//s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
648-
//s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
649-
//s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
650-
//s->set_aec2(s, 0); // 0 = disable , 1 = enable
651-
//s->set_ae_level(s, 0); // -2 to 2
652-
//s->set_aec_value(s, 300); // 0 to 1200
653-
//s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable
654-
//s->set_agc_gain(s, 0); // 0 to 30
655-
//s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6
656-
//s->set_bpc(s, 0); // 0 = disable , 1 = enable
657-
//s->set_wpc(s, 1); // 0 = disable , 1 = enable
658-
//s->set_raw_gma(s, 1); // 0 = disable , 1 = enable
659-
//s->set_lenc(s, 1); // 0 = disable , 1 = enable
660-
//s->set_hmirror(s, 0); // 0 = disable , 1 = enable
661-
//s->set_vflip(s, 0); // 0 = disable , 1 = enable
662-
//s->set_dcw(s, 1); // 0 = disable , 1 = enable
663-
//s->set_colorbar(s, 0); // 0 = disable , 1 = enable
664-
665-
// We now have camera with default init
666-
// check for saved preferences and apply them
667-
668-
if (filesystem) {
669-
delay(200); // a short delay to let spi bus settle after camera init
670-
loadPrefs(SPIFFS);
671-
} else {
672-
Serial.println("No Internal Filesystem, cannot load or save preferences");
673-
}
680+
Serial.println("No Internal Filesystem, cannot load or save preferences");
674681
}
675682

676683
/*
@@ -700,27 +707,31 @@ void setup() {
700707
}
701708
ArduinoOTA
702709
.onStart([]() {
703-
String type;
704-
if (ArduinoOTA.getCommand() == U_FLASH)
705-
type = "sketch";
706-
else // U_SPIFFS
707-
type = "filesystem";
708-
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
709-
Serial.println("Start updating " + type);
710+
String type;
711+
if (ArduinoOTA.getCommand() == U_FLASH)
712+
type = "sketch";
713+
else // U_SPIFFS
714+
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
715+
type = "filesystem";
716+
Serial.println("Start updating " + type);
717+
// Stop the camera since OTA will crash the module if it is running.
718+
// the unit will need rebooting to restart it, either by OTA on success, or manually by the user
719+
Serial.println("Stopping Camera");
720+
esp_err_t err = esp_camera_deinit();
710721
})
711722
.onEnd([]() {
712-
Serial.println("\r\nEnd");
723+
Serial.println("\r\nEnd");
713724
})
714725
.onProgress([](unsigned int progress, unsigned int total) {
715-
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
726+
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
716727
})
717728
.onError([](ota_error_t error) {
718-
Serial.printf("Error[%u]: ", error);
719-
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
720-
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
721-
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
722-
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
723-
else if (error == OTA_END_ERROR) Serial.println("End Failed");
729+
Serial.printf("Error[%u]: ", error);
730+
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
731+
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
732+
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
733+
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
734+
else if (error == OTA_END_ERROR) Serial.println("End Failed");
724735
});
725736
ArduinoOTA.begin();
726737
} else {

0 commit comments

Comments
 (0)