Interrupt driven ESP32-CAM panics when taking picture - Page 1
Forum section: electronics / beginners

author: YurkshireLad
author_rank: Frequent Contributor
timestamp: April 03, 2021, 07:44:20 pm
content: I'm trying to write a simple sketch that will take a picture using an ESP32-CAM when motion is detected via an RCWL-0516 Microwave Radar Occupancy Sensor Module, using interrupts. The code is:Code:#include "esp_camera.h"#include "Arduino.h"#include "soc/soc.h"#include "soc/rtc_cntl_reg.h"#include "driver/rtc_io.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 22camera_config_t config;int sensor_state = LOW;volatile bool is_triggered = false;void ICACHE_RAM_ATTR ISR_HANDLER() {is_triggered = true;}void loop_forever() {while (1) {delay(500);yield();}}void setup() {WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detectorSerial.begin(115200);Serial.setDebugOutput(true);Serial.println();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|UXGAconfig.jpeg_quality = 10;config.fb_count = 2;} else {config.frame_size = FRAMESIZE_SVGA;config.jpeg_quality = 12;config.fb_count = 1;}pinMode(13, INPUT);attachInterrupt(digitalPinToInterrupt(13), ISR_HANDLER, RISING);}void take_picture() {// Init Cameraesp_err_t err = esp_camera_init(&config);if (err != ESP_OK) {loop_forever();}camera_fb_t * fb = NULL;// Take Picture with Camerafb = esp_camera_fb_get();if (!fb) {loop_forever();}esp_camera_fb_return(fb);// Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4pinMode(4, OUTPUT);digitalWrite(4, LOW);}void loop() {if (is_triggered) {take_picture();is_triggered = false;}}If I remove the interrupt code and simply take a picture when the ESP32-CAM boots, it works. The above code results in the following exception. I'm confused as to what the problem might be - I'd appreciate suggestions from more knowledgeable people. Thanks.Code:Rebooting...ets Jun 8 2016 00:22:57rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)configsip: 0, SPIWP:0xeeclk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00mode:DIO, clock div:1load:0x3fff0018,len:4load:0x3fff001c,len:1216ho 0 tail 12 room 4load:0x40078000,len:9720ho 0 tail 12 room 4load:0x40080400,len:6352entry 0x400806b8ISR UpdateInitialising cameraGuru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.Core 1 register dump:PC : 0x400f50eb PS : 0x00060a33 A0 : 0x800eda17 A1 : 0x3ffb1ec0A2 : 0x00000000 A3 : 0x3ffb8058 A4 : 0x00000001 A5 : 0x00000001A6 : 0x00060a20 A7 : 0x00000000 A8 : 0x800ed379 A9 : 0x3ffb1e90A10 : 0x02000000 A11 : 0x400ecf5c A12 : 0x3ffb1ea8 A13 : 0x00000001A14 : 0x00060e20 A15 : 0x00000000 SAR : 0x00000007 EXCCAUSE: 0x0000001cEXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0x00000000Backtrace: 0x400f50eb:0x3ffb1ec0 0x400eda14:0x3ffb1ee0 0x400ef5f7:0x3ffb1f10 0x400ef767:0x3ffb1f40 0x400d152a:0x3ffb1f70 0x400d1593:0x3ffb1f90 0x400d2c8d:0x3ffb1fb0 0x4008ce2d:0x3ffb1fd0

author: mikerj
author_rank: Super Contributor
timestamp: April 04, 2021, 10:54:24 am
content: Code:EXCVADDR: 0x00000000This shows something was trying to access memory address 0x00000000, which is a classic symptom of something trying to use a null pointer.

author: YurkshireLad
author_rank: Frequent Contributor
timestamp: April 04, 2021, 07:14:33 pm
content: It is; it's happening inside the esp_camera_init function. I'm not sure how to resolve or even how to debug this.If I remove any non-camera code, so the sketch simply takes a photo when the ESP32-CAM boots up, it works. Is it something to do with the pin I'm using to read the RCWL-0516?

author: YurkshireLad
author_rank: Frequent Contributor
timestamp: April 08, 2021, 11:32:45 am
content: According to a developer of the library, the ESP32-CAM library doesn't support the use of attachInterrupt. It does work with the native ESP32 interrupts though.

author: FBMinis
author_rank: Regular Contributor
timestamp: October 24, 2023, 12:57:32 pm
content: Quote from: YurkshireLad on April 08, 2021, 11:32:45 amI was googling for other stuff and came across this thread. I'd like to add I've had no problem executing an action using that interrupt method on an ESP32-CAM.The code below is also working inside a larger project that includes capturing a photo and video and sending it to Telegram, at the same time it reads a BME280 sensor an sends its values to Thingspeak. The reed switch is part of the tipping bucket mechanism to measure rainfall.Code:/*WriteSingleFieldDescription: Writes a value to a channel on ThingSpeak when a reed switch is closed.Hardware: ESP32 based boards!!! IMPORTANT - Modify the secrets.h file for this project with your network connection and ThingSpeak channel details. !!!Note:- Requires installation of EPS32 core. See https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md for details.- Select the target hardware from the Tools->Board menu- This example is written for a network using WPA encryption. For WEP or WPA, change the WiFi.begin() call accordingly.ThingSpeak ( https://www.thingspeak.com ) is an analytic IoT platform service that allows you to aggregate, visualize, andanalyze live data streams in the cloud. Visit https://www.thingspeak.com to sign up for a free account and create a channel.Documentation for the ThingSpeak Communication Library for Arduino is in the README.md folder where the library was installed.See https://www.mathworks.com/help/thingspeak/index.html for the full ThingSpeak documentation.For licensing information, see the accompanying license file.Copyright 2020, The MathWorks, Inc.*//*//https://www.programmersought.com/article/7556268640/// https://lastminuteengineers.com/handling-esp32-gpio-interrupts-tutorial/// https://techtutorialsx.com/2017/09/30/esp32-arduino-external-interrupts/// https://www.lucadentella.it/en/2017/02/25/esp32-12-io-e-interrupts/// https://github.com/lucadentella/esp32-tutorial/blob/master/07_gpio_interrupts/main/main.c// https://techtutorialsx.com/2017/05/06/esp32-arduino-creating-a-task/*/#include <WiFi.h>#include "ThingSpeak.h" // always include thingspeak header file after other header files and custom macroschar ssid[] = "***************"; // your network SSID (name)char pass[] = "***************"; // your network passwordWiFiClient client;unsigned long myChannelNumber = 000000000000;const char * myWriteAPIKey = "***************";float depth_per_toggle = 0.2; // depth of rainfall per m² of rainfall in mmstruct ReedSwitch {const uint8_t PIN;uint32_t numberToggles;volatile bool toggled;};ReedSwitch reedswitch = {2, 0, false};//variables to keep track of the timing of recent interruptsunsigned long reed_time = 0;unsigned long last_reed_time = 0;void IRAM_ATTR isr() {reed_time = millis();if (reed_time - last_reed_time > 250) {reedswitch.numberToggles++;reedswitch.toggled = true;last_reed_time = reed_time;}}void setup() {Serial.begin(115200); //Initialize serialwhile (!Serial) {; // wait for serial port to connect. Needed for Leonardo native USB port only}pinMode(reedswitch.PIN, INPUT_PULLUP);attachInterrupt(reedswitch.PIN, isr, RISING);WiFi.mode(WIFI_STA);// Connect or reconnect to WiFiif (WiFi.status() != WL_CONNECTED) {Serial.print("Attempting to connect to SSID: ");// Serial.println(SECRET_SSID);while (WiFi.status() != WL_CONNECTED) {WiFi.begin(ssid, pass); // Connect to WPA/WPA2 network. Change this line if using open or WEP networkSerial.print(".");delay(5000);}Serial.println("\nConnected.");}ThingSpeak.begin(client); // Initialize ThingSpeak}void loop() {if (reedswitch.toggled) {Serial.printf("reed has been toggled %u times\n", reedswitch.numberToggles);// Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different// pieces of information in a channel. Here, we write to field 1.int x = ThingSpeak.writeField(myChannelNumber, 7, depth_per_toggle, myWriteAPIKey);if (x == 200) {Serial.println("Channel update successful.");}else {Serial.println("Problem updating channel. HTTP error code " + String(x));}reedswitch.toggled = false;}}This also works if you want to have more control over your interrupts:Code:static void IRAM_ATTR reedswitch_ISR(void* arg);int reedswitchpin = 2;uint32_t numberToggles;volatile bool toggled = false;unsigned long reed_time = 0;unsigned long last_reed_time = 0;static void setupinterrupts() {pinMode(reedswitchpin, INPUT_PULLUP);// Install the GPIO ISR servicegpio_install_isr_service(0); // You can use a different number (0, 1, 2) if neededesp_err_t err = gpio_isr_handler_add((gpio_num_t)reedswitchpin, &reedswitch_ISR, (void*) NULL);if (err != ESP_OK) {Serial.printf("gpio_isr_handler_add failed (%x)", err);}gpio_set_intr_type((gpio_num_t)reedswitchpin, GPIO_INTR_NEGEDGE);}static void IRAM_ATTR reedswitch_ISR(void* arg) {reed_time = millis();if (reed_time - last_reed_time > 250) {numberToggles++;toggled = true;last_reed_time = reed_time;}}void setup() {Serial.begin(115200);delay(100);setupinterrupts();delay(100);Serial.println("waiting...");}void loop() {if (toggled) {Serial.printf("reed has been toggled %u times\n", numberToggles);Serial.println(" ");}toggled = false;}