ESP32 INTERRUPTS

Hi to the members of the group! I am trying to use interrupts to count the rpm of my small car robot with esp 32 to the multi tasking program. I took readings from both two wheels of the smalll robot car with optocoupler sensor.The goal is every second to have new speed data and to keep distance value.I write this code and seems to work fine but now without doing nothing crash. Please help me to find the errors. CODE:

//#include <LiquidCrystal_I2C.h>         FOR NODE32S MCU
//LiquidCrystal_I2C lcd(0x27,16,2); 

#define PIN_D36                     36        
#define PIN_D39                     39
unsigned int interruptPin1 = PIN_D36;
unsigned int interruptPin2 = PIN_D39;

unsigned long start_time; // Δημιούργησε μια μεταβλητή τύπου unsigned long integer με όνομα start_time
unsigned int counter1,circles1 = 0;
unsigned int counter2,circles2 = 0;
 
// Float for number of slots in encoder disk
float diskslots = 40;  // Change to match value of encoder disk
float Sdistance=0; 

volatile float rotation1;
volatile float rotation2;
volatile float speed1;
volatile float speed2;

//void LCD1602_init(void) 
//{
//lcd.init(); 
//delay(10); 
//lcd.backlight(); 
//lcd.clear(); 
//}

hw_timer_t * timer = NULL;

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
 
void IRAM_ATTR handleInterrupt1() {
  portENTER_CRITICAL_ISR(&mux);
  counter1++;
  circles1++;
  portEXIT_CRITICAL_ISR(&mux);
}

 void IRAM_ATTR handleInterrupt2() {
  portENTER_CRITICAL_ISR(&mux);
   counter2++;
  circles2++;
  
  portEXIT_CRITICAL_ISR(&mux);
}

 void IRAM_ATTR count()  {  
 timerAlarmDisable(timer);
  rotation1 = (counter1 / diskslots) * 60.00;  // calculate RPM for Motor 1
  counter1 = 0;  //  reset counter to zero
  rotation2 = (counter2 / diskslots) * 60.00;  // calculate RPM for Motor 2
  counter2 = 0;  //  reset counter to zero
 Sdistance=(((circles1+circles2)/2) / diskslots)*0.208;   //0.208=π*δ , δ=diameter=2*radius
  speed1 = 2* rotation1 * 0.208; //*2 because of 0,5secinterrupt
 speed2 = 2* rotation2 * 0.208;  //radius=0.0333m
      
  timerAlarmEnable(timer);
       
  }



#define RXD2 16
#define TXD2 17


void setup() {
  Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  
  
  Serial.println("Monitoring interrupts: ");
  pinMode(interruptPin1, INPUT);
  pinMode(interruptPin2, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), handleInterrupt1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), handleInterrupt2, CHANGE);

   timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &count, true);
  
  timerAlarmWrite(timer, 500000, true);
  timerAlarmEnable(timer);
//LCD1602_init();
//lcd.setCursor(0, 0);
//lcd.print(" Wait Signal ");
  
}

    
 void loop() {
//start_time=millis();
//if (millis()-start_time>1000UL)
// {
Serial2.print(speed2);
 Serial2.print(",");
 Serial2.print(speed1);
 
 Serial2.print(",");
  Serial2.println(Sdistance);
      
//}
 delay(500);


}

I'm no expert in ESP32, but what pops out is you usage of critical sections. You are using the same mux in two ISR's and you are not using the mux when accessing the critical variables, pseudocode:

counter1, counter2, mux1, mux2;

ISR1 {
  enterCritical(mux1);
  counter1++;
  exitCritical(mux1);
}

ISR2 {
  enterCritical(mux2);
  counter2++;
  exitCritical(mux2);
}

loop {
  enterCritical(mux1);
  //do something with counter1
  exitCritical(mux1);

  enterCritical(mux2);
  //do something with counter2
  exitCritical(mux2);
}

Hope you understand :slight_smile:

The ESP32 has a PCNT module that runs independently of the CPU that can count pulses up to 40Mhz.

ESP32 PCNT API Pulse Counter - ESP32 - — ESP-IDF Programming Guide latest documentation

So, as I'm just starting to learn ESP32 and FreeRTOS, I wanted to look for a FreeRTOS-based solution even though (as @Idahowalker noted) the device has a capable hardware counter.

Anyway, I think, with proper use of FreeRTOS techniques, this could be done without the use of critical sections. Something like the code below (compiles but untested). Also, are you sure you want to use "CHANGE" in the attachInterrupt() calls. Seems like that would result in double counting. Why not FALLING?

#include "Arduino.h"

void computeSpeed(void *pvParameters);
void IRAM_ATTR handleInterrupt1();
void IRAM_ATTR handleInterrupt2();

QueueHandle_t counter1Queue;
QueueHandle_t counter2Queue;

void setup() {
  const uint8_t interruptPin1 = 36;
  const uint8_t interruptPin2 = 39;

  Serial.begin(115200);
  delay(1000);
  log_i("Starting");

  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);

  counter1Queue = xQueueCreate(1, sizeof(uint32_t));
  counter2Queue = xQueueCreate(1, sizeof(uint32_t));

  attachInterrupt(digitalPinToInterrupt(interruptPin1), handleInterrupt1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), handleInterrupt2, CHANGE);

  xTaskCreatePinnedToCore(computeSpeed, "Compute Speed", 10000, NULL, 2, NULL, 1);
}

void loop() {
}

void IRAM_ATTR handleInterrupt1() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount;

  pulseCount++;
  xQueueOverwriteFromISR(counter1Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void IRAM_ATTR handleInterrupt2() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount;

  pulseCount++;
  xQueueOverwriteFromISR(counter2Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void computeSpeed(void *pvParameters) {
  uint32_t lastCount1 = 0;
  uint32_t lastCount2 = 0;
  uint32_t incrementalCount1;
  uint32_t incrementalCount2;
  uint32_t lastMicros = 0;
  uint32_t currentCount1;
  uint32_t currentCount2;
  uint32_t currentMicros;
  uint32_t deltaT;
  uint32_t circles1 = 0;
  uint32_t circles2 = 0;

  BaseType_t peekSucceed1;
  BaseType_t peekSucceed2;

  const TickType_t xTimeInTicks = pdMS_TO_TICKS(500);

  for (;;) {
    vTaskDelay(xTimeInTicks);
    currentMicros = micros();
    peekSucceed1 = xQueuePeek(counter1Queue, &currentCount1, 0);
    peekSucceed2 = xQueuePeek(counter2Queue, &currentCount2, 0);
    if (peekSucceed1 && peekSucceed2 && (currentCount1 != lastCount1) && (currentCount2 != lastCount2)) {
      incrementalCount1 = currentCount1 - lastCount1;
      incrementalCount2 = currentCount2 - lastCount2;
      lastCount1 = currentCount1;
      lastCount2 = currentCount2;
      circles1 += incrementalCount1;
      circles2 += incrementalCount2;
      deltaT = currentMicros - lastMicros;

      // Compute Rotation speeds form "incrementalCount" values and and "deltaT"
      // Compute Distance from "circles" values

      lastMicros = currentMicros;
    }
  }
  vTaskDelete(nullptr);
}

Updated version with some corrections and simplifications.

#include "Arduino.h"

void computeSpeed(void *pvParameters);
void IRAM_ATTR handleInterrupt1();
void IRAM_ATTR handleInterrupt2();

QueueHandle_t counter1Queue;
QueueHandle_t counter2Queue;

void setup() {
  const uint8_t interruptPin1 = 36;
  const uint8_t interruptPin2 = 39;
  const uint32_t startingValue = 0;

  Serial.begin(115200);
  delay(1000);
  log_i("Starting");

  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);

  counter1Queue = xQueueCreate(1, sizeof(uint32_t));
  counter2Queue = xQueueCreate(1, sizeof(uint32_t));
  xQueueOverwrite(counter1Queue, &startingValue);
  xQueueOverwrite(counter2Queue, &startingValue);

  attachInterrupt(digitalPinToInterrupt(interruptPin1), handleInterrupt1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), handleInterrupt2, CHANGE);

  xTaskCreatePinnedToCore(computeSpeed, "Compute Speed", 10000, NULL, 2, NULL, 1);
}

void loop() {
}

void IRAM_ATTR handleInterrupt1() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount = 0;

  pulseCount++;
  xQueueOverwriteFromISR(counter1Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void IRAM_ATTR handleInterrupt2() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount = 0;

  pulseCount++;
  xQueueOverwriteFromISR(counter2Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void computeSpeed(void *pvParameters) {
  uint32_t lastCount1 = 0;
  uint32_t lastCount2 = 0;
  uint32_t incrementalCount1;
  uint32_t incrementalCount2;
  uint32_t lastMicros = 0;
  uint32_t currentCount1;
  uint32_t currentCount2;
  uint32_t currentMicros;
  uint32_t deltaT;
  uint32_t circles1 = 0;
  uint32_t circles2 = 0;

  const TickType_t xTimeInTicks = pdMS_TO_TICKS(500);

  for (;;) {
    vTaskDelay(xTimeInTicks);
    currentMicros = micros();
    xQueuePeek(counter1Queue, &currentCount1, 0);
    xQueuePeek(counter2Queue, &currentCount2, 0);
    incrementalCount1 = currentCount1 - lastCount1;
    incrementalCount2 = currentCount2 - lastCount2;
    if ((incrementalCount1 > 0) && (incrementalCount2 > 0)) {
      lastCount1 = currentCount1;
      lastCount2 = currentCount2;
      circles1 += incrementalCount1;
      circles2 += incrementalCount2;
      deltaT = currentMicros - lastMicros;
      lastMicros = currentMicros;

      // Compute Rotation speeds form "incrementalCount" values and and "deltaT"
      // Compute Distance from "circles" values

    }
  }
  vTaskDelete(nullptr);
}

THANK YOU BOTH ALL FRIENDS!! I tried Danois90 way but failed. gfvalvo way no the expect results. I add to gfvalvo code a few things the result was

01:42:25.242 -> 24.96,0.00,0.00,-43.00
01:42:32.436 -> 0.00,49.92,0.42,47.00
01:42:37.086 -> 24.96,0.00,0.42,11.00
01:42:44.289 -> 0.00,24.96,0.62,66.00
01:42:45.305 -> 0.00,0.00,0.62,57.00
01:42:46.864 -> 0.00,0.00,0.83,36.00
01:42:50.968 -> 0.00,0.00,0.83,67.00
01:42:52.550 -> 0.00,0.00,0.83,66.00
01:42:53.557 -> 0.00,0.00,1.04,43.00
01:42:54.084 -> 0.00,0.00,1.04,33.00
01:42:54.591 -> 0.00,0.00,1.04,25.00
01:42:55.600 -> 0.00,0.00,1.04,22.00
01:42:59.763 -> 0.00,0.00,1.04,45.00
01:43:07.490 -> 49.92,0.00,1.46,-35.00

and the modified code from gfvalvo

#include "Arduino.h"
float angle_left1;
float angle_right1;
float horizonangle;
float Sdistance;
float rotation1;  
float rotation2;
float speed1; 
float speed2; 
const uint8_t diskslots=40;
void computeSpeed(void *pvParameters);
void IRAM_ATTR handleInterrupt1();
void IRAM_ATTR handleInterrupt2();

QueueHandle_t counter1Queue;
QueueHandle_t counter2Queue;

void setup() {
  const uint8_t interruptPin1 = 23;
  const uint8_t interruptPin2 = 21;
  const uint32_t startingValue = 0;
  const uint8_t diskslots=40;
  Serial.begin(115200);
  delay(1000);
  log_i("Starting");

  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);

  counter1Queue = xQueueCreate(1, sizeof(uint32_t));
  counter2Queue = xQueueCreate(1, sizeof(uint32_t));
  xQueueOverwrite(counter1Queue, &startingValue);
  xQueueOverwrite(counter2Queue, &startingValue);

  attachInterrupt(digitalPinToInterrupt(interruptPin1), handleInterrupt1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), handleInterrupt2, CHANGE);

  xTaskCreatePinnedToCore(computeSpeed, "Compute Speed", 10000, NULL, 2, NULL, 1);
}

void loop() {
 
  
}

void IRAM_ATTR handleInterrupt1() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount = 0;

  pulseCount++;
  xQueueOverwriteFromISR(counter1Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void IRAM_ATTR handleInterrupt2() {
  BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
  static uint32_t pulseCount = 0;

  pulseCount++;
  xQueueOverwriteFromISR(counter2Queue, &pulseCount, &pxHigherPriorityTaskWoken);
  if (pxHigherPriorityTaskWoken) {
    portYIELD_FROM_ISR();
  }
}

void computeSpeed(void *pvParameters) {
  uint32_t lastCount1 = 0;
  uint32_t lastCount2 = 0;
  uint32_t incrementalCount1;
  uint32_t incrementalCount2;
  uint32_t lastMicros = 0;
  uint32_t currentCount1;
  uint32_t currentCount2;
  uint32_t currentMicros;
  uint32_t deltaT;
  uint32_t circles1 = 0;
  uint32_t circles2 = 0;
  
     
  const TickType_t xTimeInTicks = pdMS_TO_TICKS(500);

  for (;;) {
    vTaskDelay(xTimeInTicks);
    float angle_left1;
float angle_right1;
float horizonangle;
float Sdistance;
float rotation1;
float rotation2;
float speed1;
float speed2;
    currentMicros = micros();
    xQueuePeek(counter1Queue, &currentCount1, 0);
    xQueuePeek(counter2Queue, &currentCount2, 0);
    incrementalCount1 = currentCount1 - lastCount1;
    incrementalCount2 = currentCount2 - lastCount2;
   
    if ((incrementalCount1 > 0) && (incrementalCount2 > 0)) {
      lastCount1 = currentCount1;
      lastCount2 = currentCount2;
      circles1 += incrementalCount1;
      circles2 += incrementalCount2;
      deltaT = currentMicros - lastMicros;
      lastMicros = currentMicros;
      angle_left1 = (circles1 % 360) * (90/80) ;
angle_right1 = (circles2 % 360) * (90/80) ;
 horizonangle = (angle_right1 - angle_left1);
  Sdistance=(((circles1+circles2)/2) / diskslots)*0.208;   //0.208=π*δ , δ=diameter=2*radius
  
  rotation1 = (incrementalCount1 / diskslots) * 60.00;  // calculate RPM for Motor 1
  
  rotation2 = (incrementalCount2 / diskslots) * 60.00;  // calculate RPM for Motor 2
  
 
  speed1 = 2* rotation1 * 0.208; //*2 because of 0,5secinterrupt
  
 speed2 = 2* rotation2 * 0.208;  //radius=0.0333m  */
 

      // Compute Rotation speeds form "incrementalCount" values and and "deltaT"
      // Compute Distance from "circles" values
 Serial.print(speed1);  
 Serial.print(",");
 Serial.print(speed2);
 
 Serial.print(",");
  Serial.print(Sdistance);
   Serial.print(",");
  
  Serial.println(horizonangle);   
//delay(500);      

    }
   
  }
  vTaskDelete(nullptr);
}

i wish to have every 0.5 sec the speed of two wheels and distance and angle from serial.....