Crash in Interrupt handling function

Hi,
My interrupt handling function randomly crash. I can't capture its crashlog as well, it always show as corrupted. My chip is ESP32

16:19:02.996 -> Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
16:19:03.037 -> 
16:19:03.037 -> Core  1 register dump:
16:19:03.037 -> PC      : 0x40084934  PS      : 0x00050d31  A0      : 0x80081300  A1      : 0x3ffbf31c  
16:19:03.037 -> A2      : 0x00000040  A3      : 0x00018040  A4      : 0x000637ff  A5      : 0x3ffbf2ec  
16:19:03.037 -> A6      : 0x00000000  A7      : 0x3ffbdbd4  A8      : 0x00000001  A9      : 0x4008821a  
16:19:03.037 -> A10     : 0x3ffc40ac  A11     : 0x80000001  A12     : 0x00000001  A13     : 0x3ffbf2cc  
16:19:03.037 -> A14     : 0x00000000  A15     : 0x00000000  SAR     : 0x00000004  EXCCAUSE: 0x0000001c  
16:19:03.078 -> EXCVADDR: 0x8008130c  LBEG    : 0x40086030  LEND    : 0x4008603a  LCOUNT  : 0x00000000  
16:19:03.078 -> 
16:19:03.078 -> 
16:19:03.078 -> Backtrace: 0x40084931:0x3ffbf31c |<-CORRUPTED

Here's the function which is called when interrupt occurred:

#define BUFFER_SIZE  16//16
volatile uint8_t byte_buffer[BUFFER_SIZE];
volatile byte buffer_idx = 0;
volatile byte sig_seen = 0;
volatile unsigned short shift_register = 0;
volatile byte bit_count = 0;
volatile int scount=1;
volatile bool crcpass=false;
volatile int device_id=0;
volatile int teststep=0;
volatile int collectedcount=0;
volatile bool parseDone=false;
void rfInterruptProcessor(unsigned long ctime,unsigned long ltime){

   unsigned long ccount = 0;
   int duration=ctime-ltime;
  
   int newVal=0;
  
  if(duration>=5){
    ccount++;
    newVal += digitalRead(RXPIN) ? 1 : 0;
    if(ccount==10 || duration>50){
      newVal=(newVal+ccount/2)/(ccount);
      ccount=0;
    }else{
      ccount++;
      return;
    }
  }else{
    return;
  }

  now = micros();

  if(transition_t <= now)
    duration = now - transition_t;
  else
    duration = (~transition_t) + now;

  if(newVal != val) {  // then transitioning state
    /*
     *  We update the transition time for the pulse, and
     *  change the current state of the input value.
     */
    transition_t = now;
    val = newVal;

     //teststep=duration;
    if(duration < (SHORT_PULSE - SHORT_MARGIN) 
        || duration > (LONG_PULSE + LONG_MARGIN)) {
      // Meaningless pulse
      return;
    }
    /*
     *  If we reach here, then we have seen a potentially
     *  valid pulse. Shift the bit into the register.
     */
    if(newVal == 1) {
      // rising edge of a pulse (0 -> 1)
    } else {
      // falling edge of a pulse (1 -> 0)
      if( duration >= (SHORT_PULSE - SHORT_MARGIN) && duration <= (SHORT_PULSE + SHORT_MARGIN) ) {
        // short pulse is binary '1'
        shift_register = (shift_register << 1) | 0x01;
        bit_count++;

      } else if(duration >= (LONG_PULSE - LONG_MARGIN) && duration <= (LONG_PULSE + LONG_MARGIN)) {
        // long pulse is binary '0'
        shift_register = (shift_register << 1);
        bit_count++;

      }
    }
    // Look for signature of 0xfa (4 bits 0xf0 pre-amble + 0xa)
    if((shift_register & 0xff) == 0xfa && buffer_idx == 0) {
      // Found signature - discard pre-amble and leave 0x0a.
      shift_register = 0x0a;
      bit_count = 4;
      sig_seen = 1;  // Flag that the signature has been seen.

    } else if(bit_count == 8 && sig_seen) {
      // Got a byte, so store it if we have room.
      if(buffer_idx < BUFFER_SIZE)
        byte_buffer[buffer_idx++] = (byte)(shift_register & 0xff);
      else{
       // Serial.println("Overflow on byte");
      }
      shift_register = 0;
      bit_count = 0;
    }
  } else {
      /*
       *  Have we reached timeout on duration? If so, process any
       *  bytes present in the buffer and then reset the state
       *  variables.
       */    
      if(duration > 5000) {
        if (buffer_idx > 0) {
          /*
           *  Dump the bytes to the Serial.
           */
    
          /*
           *  If we have enough bytes, then verify the checksum.
           */
          
          crcpass=false;
          if(buffer_idx >= 10 && _crc8(byte_buffer, 9) == byte_buffer[9]) {
             crcpass=true;
            scount++;
            
          }
          device_id = (byte_buffer[0] << 4 & 0xf0) | (byte_buffer[1] >> 4);
          int battery_low = (byte_buffer[8] >> 4) == 1;
          temp_raw    = ((byte_buffer[1] & 0x03) << 8) | byte_buffer[2]; // only 10 bits, discard top bits
          temperature = (temp_raw - 400) * 0.1f;
           humidity = byte_buffer[3];
          int direction_deg = wind_dir_degr[byte_buffer[8] & 0x0f];
      
          buffer_idx = 0;
          parseDone=true;

            String dataString = "";
        // read three sensors and append to the string:
          dataString=dataString+"Device ID="+String(device_id);
          dataString += ",";
          
        }

        shift_register = 0;
        bit_count = 0;
        sig_seen = 0;
      }

    } 
}
void RECEIVE_ATTR handleInterrupt2() {

static unsigned long lastTime2 = 0;
  const long time = micros();

    rfInterruptProcessor(time,lastTime2);
    lastTime2 = time;
}
void setup() {
attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
}

As this is not an actual introductory tutorial I have moved it here.

You need to post all your code.

You can't declare volatile variables inside an ISR they have to be global.

Yes, all volatile variables are global. This is a RF433 receiver so the interrupt can be very quick--within a few micro seconds

interrupt handler code should be as short as possible and that in post 1 is very long
One way on the ESP32 is to set a volatile flag to indicate an interrupt has occured and then process the information in loop(), e.g. loop() checks for change on state of pin 16

#define RXPIN 16

volatile int pin = 0;

// interrupt handler
void IRAM_ATTR handleInterrupt2() {
  pin = digitalRead(RXPIN);
}

void setup() {
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
}

void loop() {
  static int lastPin = -1;
  // if had inperrupt on pin 16 display value
  if (pin != lastPin) {
    lastPin = pin;
    Serial.println(pin);
  }
}

as I change the state of pin 16 serial monitor displays

1
0
1
0
1
0

@chamroeun168
You code contains a lot of weird operators..

For example:

is equivalent to
newVal += digitalRead(RXPIN) ;

The part of code below is perfectly useless, because the local variable dataString will be destroyed just after creation at the end of code block.

Yea, I need to refactor. I am trying to make thing work first. This code was originally based on loop but I need to switch it to interrupt based instead. Somehow, it randomly crash

Some pulse is less than 50 micro seconds, which can't let the loop handle it-the main loop need to handle other task as well. At least, I need to record the the time it happened.

In that case you will not be able to handle it at all. The interrupt is run on the same core as your main code and not "in parallel", so the moving the task to the interrupt do not create additional time comparing to the loop.

Um, not exactly.   digitalRead returns a HIGH or a LOW.  These are not necessarily the same things as an integer value of one or zero.

In general, somewhere on the Earth, you are right...but actually in arduino core HIGH is 1 and LOW is zero...

can you note the time as well? e.g.

#define RXPIN 16

volatile int pin = 0;
volatile long timer=0;

// interrupt handler
void IRAM_ATTR handleInterrupt2() {
  pin = digitalRead(RXPIN);
  timer=millis();
}

void setup() {
  Serial.begin(115200);
  pinMode(RXPIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
}

void loop() {
  static int lastPin = -1;
  // if had inperrupt on pin 16 display value
  if (pin != lastPin) {
    lastPin = pin;
    Serial.print(timer);
    Serial.print(' ');
    Serial.println(pin);
  }
}

a runs outputs

12901 0
14362 1
14365 0
14365 1
14581 0
91533 1
94281 0
98693 1
98694 0
98695 1
101604 0
106322 1
108577 0
111197 1
111283 0
111373 1
113799 0

clearly, as @b707 says, if you cannot do your calculations within the time period the ESP32 is not suitable
perhaps you need someting faster, e.g. a Texas DSP processor?

Edit: the above code is using millis() which is only accurate to a millisecond - perhaps you should use micros() - not sure how accurate this is though!

Yes, for now. I, and I expect many others, treat that as if it will always be the case.

So much would break, source code-wise, that to change that woukd be inconceivable.

But strictly speaking, that line should be (I think)

   newVal += (digitalRead(RXPIN) == HIGH) ? 1 : 0;

Yeah, me neither.

a7

1 Like

attempt to use an ESP32 to calculate pulse width in microseconds averaged over 10 seconds

// ESP32 determine pulse width in microseconds

#define RXPIN 16

volatile int pin = 0;
volatile long timer = 0;
long average = 0.0;

// interrupt handler - noet pin value and time in microseconds
void IRAM_ATTR handleInterrupt2() {
  pin = digitalRead(RXPIN);
  timer = micros();
}

void setup() {
  Serial.begin(115200);
  pinMode(RXPIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
}

// calculat pulse widtch in microseconds
void loop() {
  static int lastPin = -1;
  static long lastTimer = 0, counter = 0;
  static long printer = millis();
  // if had inperrupt on pin 16 get pulse widtch time
  if (pin != lastPin) {
    lastPin = pin;
    // add pulse width time in microseconds to average and increment counter
    if (lastTimer) average += (timer - lastTimer);
    counter++;
    lastTimer = timer;
  }
  // after 10 seconds print average pulse widtch time
  if (millis() - printer > 10000) {
    Serial.println((float)average / counter);  // print average pulse width
    lastTimer =  counter = average= 0;  // reset initialse values
    printer = millis();
  }
}

serial monitor output 100microsecond period square wave

50.00
50.00
50.00
50.00
50.00
50.00
50.00

serial monitor output 25microsecond period square wave

12.74
12.74
12.74
12.74
12.74
12.74
12.74

goto 10microsecond period square wave and output is garbage - clearly missing changes

Remove the last part after (if duration>5000)and let the main loop handle it solved the crash. Anyhow, I don't know the real reason.. Thanks;

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.