espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.77k stars 7.44k forks source link

Crash when using external interrupt #954

Closed Manuauto closed 6 years ago

Manuauto commented 6 years ago

Hardware:

Board: DOIT ESP32 Devkit v1 Core Installation/update date: 10. Dec. 2017 IDE name: Arduino IDE 1.8.5 / Visual Micro Flash Frequency: 80Mhz Upload Speed: 921600

Description:

I am using an external hall effect sensor in order do measure the rotation of a wheel. To get the most accurate results, I am using an interrupt which triggers on the falling edge of the hall-sensor input.

In most cases the measurement goes just fine and I get reasonable results. In some cases, which cannot be distinguished by an obvious factor by myself, the ESP32 crashes. (Maybe and only maybe multiple interrupts happens in a very short period of time and this causes the issue? But why?)

For testing I have replaced the hall sensor with a simple push button, the pin is pulled high by an external resistor (and an internal one too, initially I only used the internal one; same problem).

I have implemented a Screen-Lock in software. The device can be unlocked via RFID. If an interrupt occurs while the device is locked, it instantly crashes.

Sketch:

(the whole sketch is attached too)

All variables accessed by the interrupt function are declared as volatile.

//Libraries
#include <require_cpp11.h>  //RFID
#include <MFRC522Hack.h>    //RFID
#include <MFRC522Extended.h>//RFID
#include <MFRC522Debug.h>   //RFID
#include <MFRC522.h>        //RFID
#include <deprecated.h>     //RFID

#include <WiFi.h>           //OTA
#include <ESPmDNS.h>        //OTA
#include <WiFiUdp.h>        //OTA
#include <ArduinoOTA.h>     //OTA

#include <TFT_eSPI.h>       //Display Library
#include <string.h>     //String Library for Time Enhancement
#include <math.h>           //Maths Library
#include <ESP32_Servo.h>    //Servo Library for Speed Control with VESC
#include <SPI.h>            //For Display
#include <Adafruit_GFX.h>   //For Display

const byte speed_hall_pin = 27; //Hall Effect Pin for Speed measurement

void setup() {
//other stuff
pinMode(speed_hall_pin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(speed_hall_pin), hall_interrupt, FALLING);
}

void loop() {
//other stuff
}

void hall_interrupt() {
    hall_millis_current = millis(); //Current time for Reference

    if (hall_millis_current > hall_millis_last + 100) {
        time_for_rotation = hall_millis_current - hall_millis_last; //Calculate Time of Rotation
        kmph = (56.8 * float(circumference)) / float(time_for_rotation);
        kmph = kmph * 1.60934; //Convert to km/h
        hall_millis_last = hall_millis_current; //(Re)Set reference Point
    }
}

Debug Messages:

serial Output:
Opening port

ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:812
load:0x40078000,len:0
load:0x40078000,len:10212
entry 0x40078a00
Card detected, UID: 1891469948 <output of my code
Access granted! <output of my code
Unlocked - Started <output of my code

<me pressing the button>

Guru Meditation Error: Core  1 panic'ed (Coprocessor exception
[ESP32_InterruptException.zip](https://github.com/espressif/arduino-esp32/files/1592923/ESP32_InterruptException.zip)

)
Register dump:
PC      : 0x400d187b  PS      : 0x00060031  A0      : 0x800811f8  A1      : 0x3ffc0b10  
A2      : 0x3ffc34f4  A3      : 0x00000001  A4      : 0x0000006e  A5      : 0x00000000  
A6      : 0x00000001  A7      : 0x00000001  A8      : 0x3ffc34fc  A9      : 0x3ffc0bb0  
A10     : 0x0000006e  A11     : 0x3ffce980  A12     : 0x00000020  A13     : 0x80000020  
A14     : 0x00000008  A15     : 0x00000000  SAR     : 0x00000013  EXCCAUSE: 0x00000004  
EXCVADDR: 0x00000000  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  

Backtrace: 0x400d187b:0x3ffc0b10 0x400811f5:0x3ffc0bf0 0x40082a41:0x3ffc0c10 0x400ea7f3:0x00000000

Rebooting...

Decoded with EspExceptionDecoder:

Decoding 4 results
0x400d187b: hall_interrupt() at E:\Documents\Arduino\Arduino Sketches\E-BIKE\EBikeMainProject_V2_ESP32/EBikeMainProject_V2_ESP32.ino line 961
0x400811f5: __onPinInterrupt at E:\Documents\Arduino\hardware\espressif\esp32\cores\esp32/esp32-hal-gpio.c line 211
0x40082a41: _xt_lowint1 at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./xtensa_vectors.S line 1105
0x400ea7f3: esp_vApplicationIdleHook at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/./freertos_hooks.c line 62
stickbreaker commented 6 years ago

@Manuauto

a couple of changes, you need to tell the compiler to always have the ISR in RAM. This processor ESP32 loads program from FLASH into RAM where it executes it. It only has 128Kbytes of RAM for ICACHE (instruction cache) so it switches out unused code on the fly. When an interrupt is triggered it bypasses the Cache load operation and jumps right to the specified code (ISR) location. If that ISR is not in RAM it panics.

void IRAM_ATTR hall_interrupt() {
    hall_millis_current = millis(); //Current time for Reference
// this if() will cause integer rollover problems, if millis_last + 100 in at or
// near maximum int value, and millis() has already rolled over it will miss.
// and never trigger until aproximately 49 days later! (millis() 32bit millisecond rollover)
//  if (hall_millis_current > hall_millis_last + 100) {
// change it to this order, the integer rollover will be correctly handled
    if(hall_millis_current - hall_millis_last >100){
        time_for_rotation = hall_millis_current - hall_millis_last; //Calculate Time of Rotation
        kmph = (56.8 * float(circumference)) / float(time_for_rotation);
        kmph = kmph * 1.60934; //Convert to km/h
        hall_millis_last = hall_millis_current; //(Re)Set reference Point
    }
}

Chuck.

Manuauto commented 6 years ago

@stickbreaker Thanks for taking the time and going over my code! Many thanks for an actual explanation of what I did wrong / what caused the issue. This was really helpful!

I have implemented the changes and the issue seems to be gone!

Manuauto commented 6 years ago

Today I went on a test ride and the issue seems to be still there.

In order to decode the debug output I tried to compile the code with the Arduino IDE. This does not work with the changes suggested by @stickbreaker.

'hall_interrupt' was not declared in this scope

Previously I used VisualMicro to compile which worked fine for some reason.


Now I used Visual Micro to compile (with apparent fix) and uploaded it. i reproduced the error and decoded it with the Arduino IDE (Here I compiled the code Without the Fix, otherwise it would not compile) The Output is as follows:

Decoding 4 results 0x40080ed6: analogInit at E:\Documents\Arduino\hardware\espressif\esp32\cores\esp32/esp32-hal-adc.c line 98 0x400812e9: pinMode at E:\Documents\Arduino\hardware\espressif\esp32\cores\esp32/esp32-hal-gpio.c line 115 0x40082b35: _xt_medint3 at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./xtensa_vectors.S line 1256 0x400ea783: esp_event_process_default at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/./event_default_handlers.c line 136


Might my pin-selection be unfortunate?

guillaume-dorczynski commented 6 years ago

Hi, I just wanted to say that I had a similar problem where an external interrupt (generated every second by a DS3231 RTC) was causing OTA updates to fail. I added IRAM_ATTR to the ISR as suggested by stickbreaker and it seems that it solved the issue :)

beegee-tokyo commented 6 years ago

I had a lots of problems with OTA as well. What I learned to get OTA working is stop all

in ArduinoOTA.onStart()

everslick commented 6 years ago

@Manuauto Can we close this issue?

Manuauto commented 6 years ago

Yes, I'll close it. The reason for crashes even after adding IRAM_ATTR was that I did "a lot" of relatively complicated calculations in the ISR. By offloading those to the main loop() I resolved this issue completely. Thanks for the help.

MacLeod-D commented 4 years ago

If hall_millis_current == hall_millis_current then you get a divide by zero error! I think this is the problem. Test time_for_rotation !=0 and do the computation only if it is true !

time_for_rotation = hall_millis_current - hall_millis_last; //Calculate Time of Rotation if (time_for_rotation != 0) kmph = (56.8 * float(circumference)) / float(time_for_rotation);

Or use micros() insted of millis(). A miilisecond is a long time ;)