Handling Multiple Interrupts on ESP32

Andrew919
Posts: 1
Joined: Mon May 19, 2025 7:44 pm

Handling Multiple Interrupts on ESP32

Postby Andrew919 » Mon May 19, 2025 8:36 pm

Hi, I’m a beginner in ESP32 coding and I’m working on a project that’s been quite challenging. I hope someone can help me.

Let me explain my project:
I’m using 4 ultrasonic receiver sensors, which I’ll call Rx1, Rx2, Rx3, and Rx4. These sensors are connected to a custom PCB that I made. Each sensor has its own receiver circuit on the board.

There is also one ultrasonic transmitter sensor which I’ll call Tx1, which is placed in front of the receiver sensors and points directly at them.

I want to use the ESP32 to detect the first rising edge from each receiver sensor (Rx1, Rx2, Rx3, and Rx4). When the ESP32 detects this rising edge, it should record the exact time in microseconds.

This is the main goal of my project.

Now, let me explain the problem I’m facing:
The ESP32 is recording wrong timestamps for when the rising edge happens.

Below, I’ll show you the results I’m getting from the ESP32 and compare them to the correct times I should be getting based on theory and calculations.

The result I am getting from the ESP32:

13:15:06.265 -> Waiting for signals...
13:15:06.758 -> Waiting for signals...
13:15:07.276 -> Tx1 was the sender.
13:15:07.276 -> Rx1 was Received First at 0.00 µs
13:15:07.276 -> Rx3 was Received Second at 24941.00 µs
13:15:07.276 -> Rx2 was Received Third at 38334.00 µs
13:15:07.276 -> Rx4 was Received Last at 40562.00 µs
13:15:07.276 -> Time Difference Of Arrival:
13:15:07.276 -> Between Rx1 and Rx3 is 24941.00 µs.
13:15:07.276 -> Between Rx1 and Rx2 is 38334.00 µs.
13:15:07.276 -> Between Rx1 and Rx4 is 40562.00 µs.
13:15:07.323 -> ---------End Of This Cycle----------


The results that I must be getting based on Theoretical calculations:

13:15:05.759 -> Waiting for signals...
13:15:06.265 -> Waiting for signals...
13:15:06.758 -> Waiting for signals...
13:15:07.276 -> Tx1 was the sender.
13:15:07.276 -> Rx1 was Received First at 600.23 µs
13:15:07.276 -> Rx3 was Received Second at 617.52 µs
13:15:07.276 -> Rx2 was Received Third at 617.88 µs
13:15:07.276 -> Rx4 was Received Last at 650.25 µs
13:15:07.276 -> Time Difference Of Arrival:
13:15:07.276 -> Between Rx1 and Rx3 is 17.19 µs.
13:15:07.276 -> Between Rx1 and Rx2 is 17.65 µs.
13:15:07.276 -> Between Rx1 and Rx4 is 50.02 µs.
13:15:07.323 -> ---------End Of This Cycle----------

Below I will add the code that I am using:

Code: Select all

#include <Arduino.h>

// Function prototypes
void IRAM_ATTR ISR_Rx1_Receive();
void IRAM_ATTR ISR_Rx2_Receive();
void IRAM_ATTR ISR_Rx3_Receive();
void IRAM_ATTR ISR_Rx4_Receive();

// Shared variables
volatile uint32_t TOA_Rx1 = 0;
volatile uint32_t TOA_Rx2 = 0;
volatile uint32_t TOA_Rx3 = 0;
volatile uint32_t TOA_Rx4 = 0;
volatile uint8_t  Rx1State = LOW;
volatile uint8_t  Rx2State = LOW;
volatile uint8_t  Rx3State = LOW;
volatile uint8_t  Rx4State = LOW;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

// Pin assignments
const int Rx1Pin = 34;
const int Rx2Pin = 35;
const int Rx3Pin = 25;
const int Rx4Pin = 26;

void setup() {
  Serial.begin(115200);

  pinMode(Rx1Pin, INPUT);
  pinMode(Rx2Pin, INPUT);
  pinMode(Rx3Pin, INPUT);
  pinMode(Rx4Pin, INPUT);

  attachInterrupt(digitalPinToInterrupt(Rx1Pin), ISR_Rx1_Receive, RISING);
  attachInterrupt(digitalPinToInterrupt(Rx2Pin), ISR_Rx2_Receive, RISING);
  attachInterrupt(digitalPinToInterrupt(Rx3Pin), ISR_Rx3_Receive, RISING);
  attachInterrupt(digitalPinToInterrupt(Rx4Pin), ISR_Rx4_Receive, RISING);
}

void loop() {
  uint8_t  s1, s2, s3, s4;
  uint32_t t1, t2, t3, t4;

  portENTER_CRITICAL(&mux);
    s1 = Rx1State;  t1 = TOA_Rx1;
    s2 = Rx2State;  t2 = TOA_Rx2;
    s3 = Rx3State;  t3 = TOA_Rx3;
    s4 = Rx4State;  t4 = TOA_Rx4;
  portEXIT_CRITICAL(&mux);

  if (s1==HIGH || s2==HIGH || s3==HIGH || s4==HIGH) {
    struct { uint8_t idx; uint32_t t; } arr[4] = {
      {1, t1}, {2, t2}, {3, t3}, {4, t4}
    };

    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3 - i; j++) {
        if (arr[j].t > arr[j+1].t) {
          auto tmp = arr[j];
          arr[j] = arr[j+1];
          arr[j+1] = tmp;
        }
      }
    }

    Serial.print("Tx");
    Serial.print(arr[0].idx);
    Serial.println(" was the sender.");

    for (int i = 0; i < 4; i++) {
      float us = arr[i].t - arr[0].t;  
      Serial.print("Rx");
      Serial.print(arr[i].idx);
      Serial.print(" was Received ");
      switch(i){
        case 0: Serial.print("First");  break;
        case 1: Serial.print("Second"); break;
        case 2: Serial.print("Third");  break;
        case 3: Serial.print("Last");   break;
      }
      Serial.print(" at ");
      Serial.print(us, 2);
      Serial.println(" µs");
    }

    Serial.println("Time Difference Of Arrival:");
    for (int i = 1; i < 4; i++) {
      float tdoa = arr[i].t - arr[0].t;
      Serial.print("Between Rx");
      Serial.print(arr[0].idx);
      Serial.print(" and Rx");
      Serial.print(arr[i].idx);
      Serial.print(" is ");
      Serial.print(tdoa, 2);
      Serial.println(" µs.");
    }

    Serial.println("---------End Of This Cycle----------\n");

    portENTER_CRITICAL(&mux);
      Rx1State = Rx2State = Rx3State = Rx4State = LOW;
      TOA_Rx1 = TOA_Rx2 = TOA_Rx3 = TOA_Rx4 = 0;
    portEXIT_CRITICAL(&mux);

    delay(1000); // delay to detect only the first rising edge (debounce)

    attachInterrupt(digitalPinToInterrupt(Rx1Pin), ISR_Rx1_Receive, RISING);
    attachInterrupt(digitalPinToInterrupt(Rx2Pin), ISR_Rx2_Receive, RISING);
    attachInterrupt(digitalPinToInterrupt(Rx3Pin), ISR_Rx3_Receive, RISING);
    attachInterrupt(digitalPinToInterrupt(Rx4Pin), ISR_Rx4_Receive, RISING);
  }
  else {
    Serial.println("Waiting for signals...");
    delay(500);
  }
}

// ISR Implementations using micros()
void IRAM_ATTR ISR_Rx1_Receive() {
  detachInterrupt(digitalPinToInterrupt(Rx1Pin));
  portENTER_CRITICAL_ISR(&mux);
    Rx1State = HIGH;
    TOA_Rx1 = micros();
  portEXIT_CRITICAL_ISR(&mux);
}

void IRAM_ATTR ISR_Rx2_Receive() {
  detachInterrupt(digitalPinToInterrupt(Rx2Pin));
  portENTER_CRITICAL_ISR(&mux);
    Rx2State = HIGH;
    TOA_Rx2 = micros();
  portEXIT_CRITICAL_ISR(&mux);
}

void IRAM_ATTR ISR_Rx3_Receive() {
  detachInterrupt(digitalPinToInterrupt(Rx3Pin));
  portENTER_CRITICAL_ISR(&mux);
    Rx3State = HIGH;
    TOA_Rx3 = micros();
  portEXIT_CRITICAL_ISR(&mux);
}

void IRAM_ATTR ISR_Rx4_Receive() {
  detachInterrupt(digitalPinToInterrupt(Rx4Pin));
  portENTER_CRITICAL_ISR(&mux);
    Rx4State = HIGH;
    TOA_Rx4 = micros();
  portEXIT_CRITICAL_ISR(&mux);
}

lbernstone
Posts: 1132
Joined: Mon Jul 22, 2019 3:20 pm

Re: Handling Multiple Interrupts on ESP32

Postby lbernstone » Wed May 21, 2025 7:50 am

Detaching (and reattaching) the interrupts is likely taking far more time than anything else going on there. You aren't doing anything that needs exclusive control. Keep your ISRs simple.

Code: Select all

void IRAM_ATTR ISR_Rx1_Receive() {
   if (Rx1State != HIGH) {
    Rx1State = HIGH;
    TOA_Rx1 = micros();
   }
}
Last edited by lbernstone on Wed May 21, 2025 6:35 pm, edited 1 time in total.

lbernstone
Posts: 1132
Joined: Mon Jul 22, 2019 3:20 pm

Re: Handling Multiple Interrupts on ESP32

Postby lbernstone » Wed May 21, 2025 6:24 pm

One other comment- if you will be doing this for a long time (hours), you should use uint64_t esp_timer_get_time() instead of Arduino micros(), which is an unsigned long.

Who is online

Users browsing this forum: No registered users and 5 guests