Huge variations using two concurrent ISR in ESP32

I've tried to use a pair of proximity sensors for measuring two identical motor speed I used the esp32, with double ISR and counting the duration between puless however, the plot shows when motor speeds are low the calculation nearly each other and converge but when motor speed increases the calculation for both motors have huge variations and diverge since high speed means a high number of interrupt triggers and HW triggers can happen both without any priority

this is my code

#include "driver/gpio.h"
//PULSE INPUT 0
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int RPM_0_RESET = 0;
//portMUX_TYPE synch = portMUX_INITIALIZER_UNLOCKED;
#define PULSE_IN_0_PIN 15 //25-G1
volatile double pulseTime_0, lastTime_0;
volatile double pulse_freq_0;

void ICACHE_RAM_ATTR isr_pulse_0() {
  //used to measure time between pulses.
  //portENTER_CRITICAL(&synch);
  lastTime_0 = pulseTime_0;
  pulseTime_0 = micros();
  pulse_freq_0 = (pulseTime_0 - lastTime_0);
  
  //portEXIT_CRITICAL(&synch);
}

int RPM_1_RESET = 0;
//portMUX_TYPE synch = portMUX_INITIALIZER_UNLOCKED;
#define PULSE_IN_1_PIN 25 //25-G1
volatile double pulseTime_1, lastTime_1;
volatile double pulse_freq_1;

void ICACHE_RAM_ATTR isr_pulse_1() {
  //used to measure time between pulses.
  //portENTER_CRITICAL(&synch);
  lastTime_1 = pulseTime_1;
  pulseTime_1 = micros();
  pulse_freq_1 = (pulseTime_1 - lastTime_1);
  
  //portEXIT_CRITICAL(&synch);
}

//SEQUENCER
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#define telem_time_interval 200 //Time in ms
unsigned long telem_time = 0;

void setup() {
  delay(2000);
  //Serial
  //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  Serial.begin(115200);

  //PULSE INPUT 0 - RPM FEEDBACK
  //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  pinMode(PULSE_IN_0_PIN, INPUT);
  //attachInterrupt(PULSE_IN_0_PIN, isr_pulse_0, FALLING);
  //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  pinMode(PULSE_IN_1_PIN, INPUT);
}

void loop() {
  
  if (millis() > telem_time + telem_time_interval)
  {
    detachInterrupt(PULSE_IN_0_PIN);
    detachInterrupt(PULSE_IN_1_PIN);
   
    Serial.print(6000000/pulse_freq_0);
    Serial.print(",");
    Serial.println(6000000/pulse_freq_1);
    RPM_0_RESET++; //increment reset value
    RPM_1_RESET++;
    if (RPM_0_RESET > 5) {
      pulse_freq_0 = 0.0;
      RPM_0_RESET = 0;
    }
    if (RPM_1_RESET > 5) {
      pulse_freq_1 = 0.0;
      RPM_1_RESET = 0;
    }

    telem_time = millis();
    attachInterrupt(PULSE_IN_0_PIN, isr_pulse_0, RISING);
    attachInterrupt(PULSE_IN_1_PIN, isr_pulse_1, RISING);
  }
}

Why double? unsigned long is sufficient and faster.

You miss to reset the times before attachInterrupt().

That differs from the standard procedure (BlinkWithoutDelay).

Such a huge integer may cause chopped bits. Make it float.

Why two variables which are always reset and incremented together?

Better now? :wink:

@DrDiettrich, wrong hat :upside_down_face:

Jawohl :wink:

If one wants to count pulses with a ESP32 why not use the ESP32's built in Pule Counting Module? The ESP32's PCNT API works under the Arduino IDE. The PCNT modules count pulses without using the CPU.

Pulse Counter (PCNT) - ESP32 - — ESP-IDF Programming Guide latest documentation (espressif.com)

/*
   Chappie Weather upgrade/addition
   process wind speed direction and rain fall.
*/
#include "esp32/ulp.h"
//#include "ulptool.h"
#include "driver/rtc_io.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "driver/pcnt.h"
#include <driver/adc.h>
#include <SimpleKalmanFilter.h>
#include <ESP32Time.h>
////
ESP32Time rtc;
WiFiClient wifiClient;
PubSubClient MQTTclient(mqtt_server, mqtt_port, wifiClient);
////
float CalculatedVoltage = 0.0f;
float kph = 0.0f;
float rain  = 0.0f;
/*
   PCNT PCNT_UNIT_0, PCNT_CHANNEL_0 GPIO_NUM_15 = pulse input pin
   PCNT PCNT_UNIT_1, PCNT_CHANNEL_0 GPIO_NUM_4 = pulse input pin
*/
pcnt_unit_t pcnt_unit00 = PCNT_UNIT_0; //pcnt unit 0 channel 0
pcnt_unit_t pcnt_unit10 = PCNT_UNIT_1; //pcnt unit 1 channel 0
//
//
hw_timer_t * timer = NULL;
//
#define evtAnemometer  ( 1 << 0 )
#define evtRainFall    ( 1 << 1 )
#define evtParseMQTT   ( 1 << 2 )
EventGroupHandle_t eg;
#define OneMinuteGroup ( evtAnemometer | evtRainFall )
////
QueueHandle_t xQ_Message; // payload and topic queue of MQTT payload and topic
const int payloadSize = 100;
struct stu_message
{
  char payload [payloadSize] = {'\0'};
  String topic ;
} x_message;
////
SemaphoreHandle_t sema_MQTT_KeepAlive; // used to stop all other MQTT thing do's
SemaphoreHandle_t sema_mqttOK; // protect the mqttOK variable.
SemaphoreHandle_t sema_CalculatedVoltage; // protects the CalculatedVoltage variable.
////
int mqttOK = 0; // stores a count value that is used to cause an esp reset
volatile bool TimeSet = false;
////
/*
   Topic topicOK has been subscribed to, the mqtt broker sends out "OK" messages if the client receives an OK message the mqttOK value is set back to zero.
   If the mqttOK count reaches a set point the ESP32 will reset.
*/
////
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  memset( x_message.payload, '\0', payloadSize ); // clear payload char buffer
  x_message.topic = ""; //clear topic string buffer
  x_message.topic = topic; //store new topic
  int i = 0; // extract payload
  for ( i; i < length; i++)
  {
    x_message.payload[i] = (char)payload[i];
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data to queue
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
// interrupt service routine for WiFi events put into IRAM
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, OneMinuteGroup, &xHigherPriorityTaskWoken);
} // void IRAM_ATTR onTimer()
////
void setup()
{
  eg = xEventGroupCreate(); // get an event group handle
  x_message.topic.reserve(100);
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);// using GPIO 34 wind direction
  adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);// using GPIO 39 current
  adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);// using GPIO 36 battery volts

  // hardware timer 4 set for one minute alarm
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 60000000, true);
  timerAlarmEnable(timer);
  /* Initialize PCNT's counter */
  int PCNT_H_LIM_VAL         = 3000;
  int PCNT_L_LIM_VAL         = -10;
  // 1st PCNT counter
  pcnt_config_t pcnt_config  = {};
  pcnt_config.pulse_gpio_num = GPIO_NUM_15;// Set PCNT input signal and control GPIOs
  pcnt_config.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_config.channel        = PCNT_CHANNEL_0;
  pcnt_config.unit           = PCNT_UNIT_0;
  // What to do on the positive / negative edge of pulse input?
  pcnt_config.pos_mode       = PCNT_COUNT_INC;   // Count up on the positive edge
  pcnt_config.neg_mode       = PCNT_COUNT_DIS;   // Count down disable
  // What to do when control input is low or high?
  pcnt_config.lctrl_mode     = PCNT_MODE_KEEP; // Keep the primary counter mode if low
  pcnt_config.hctrl_mode     = PCNT_MODE_KEEP;    // Keep the primary counter mode if high
  // Set the maximum and minimum limit values to watch
  pcnt_config.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config.counter_l_lim  = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config); // Initialize PCNT unit
  pcnt_set_filter_value( PCNT_UNIT_0, 1); //Configure and enable the input filter
  pcnt_filter_enable( PCNT_UNIT_0 );
  pcnt_counter_pause( PCNT_UNIT_0 );
  pcnt_counter_clear( PCNT_UNIT_0 );
  pcnt_counter_resume( PCNT_UNIT_0); // start the show
  // setup 2nd PCNT
  pcnt_config = {};
  pcnt_config.pulse_gpio_num = GPIO_NUM_4;
  pcnt_config.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_config.channel        = PCNT_CHANNEL_0;
  pcnt_config.unit           = PCNT_UNIT_1;
  pcnt_config.pos_mode       = PCNT_COUNT_INC;
  pcnt_config.neg_mode       = PCNT_COUNT_DIS;
  pcnt_config.lctrl_mode     = PCNT_MODE_KEEP;
  pcnt_config.hctrl_mode     = PCNT_MODE_KEEP;
  pcnt_config.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config.counter_l_lim  = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config);
  //pcnt_set_filter_value( PCNT_UNIT_1, 1 );
  //pcnt_filter_enable  ( PCNT_UNIT_1 );
  pcnt_counter_pause  ( PCNT_UNIT_1 );
  pcnt_counter_clear  ( PCNT_UNIT_1 );
  pcnt_counter_resume ( PCNT_UNIT_1 );
  //
  xQ_Message = xQueueCreate( 1, sizeof(stu_message) );
  //
  sema_CalculatedVoltage = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_CalculatedVoltage );
  sema_mqttOK = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  sema_MQTT_KeepAlive = xSemaphoreCreateBinary();
  ///
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 15000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 10000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fReadBattery, "fReadBattery", 4000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fReadCurrent, "fReadCurrent", 4000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fWindDirection, "fWindDirection", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fAnemometer, "fAnemometer", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fRainFall, "fRainFall", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 3000, NULL, 3, NULL, 1 ); // assign all to core 1
} //void setup()
static void init_ulp_program()
{

}
////
void fWindDirection( void *pvParameters )
// read the wind direction sensor, return heading in degrees
{
  float adcValue = 0.0f;
  uint64_t TimePastKalman  = esp_timer_get_time();
  SimpleKalmanFilter KF_ADC( 1.0f, 1.0f, .01f );
  float high = 0.0f;
  float low = 2000.0f;
  float ADscale = 3.3f / 4096.0f;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 100; //delay for mS
  int count = 0;
  String windDirection;
  windDirection.reserve(20);
  String MQTTinfo = "";
  MQTTinfo.reserve( 150 );
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  for (;;)
  {
    windDirection = "";
    adcValue = float( adc1_get_raw(ADC1_CHANNEL_6) ); //take a raw ADC reading
    KF_ADC.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue = KF_ADC.updateEstimate( adcValue ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    adcValue = adcValue * ADscale;
    if ( (adcValue >= 0.0f) & (adcValue <= .25f )  )
    {
      // log_i( " n" );
      windDirection.concat( "N" );
    }
    if ( (adcValue > .25f) & (adcValue <= .6f ) )
    {
      //  log_i( " e" );
      windDirection.concat( "E" );
    }
    if ( (adcValue > 2.0f) & ( adcValue < 3.3f) )
    {
      //   log_i( " s" );
      windDirection.concat( "S");
    }
    if ( (adcValue >= 1.7f) & (adcValue < 2.0f ) )
    {
      // log_i( " w" );
      windDirection.concat( "W" );
    }
    if ( count >= 30 )
    {
      MQTTinfo.concat( String(kph, 2) );
      MQTTinfo.concat( ",");
      MQTTinfo.concat( windDirection );
      MQTTinfo.concat( ",");
      MQTTinfo.concat( String(rain, 2) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicWSWDRF, MQTTinfo.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      count = 0;
    }
    count++;
    MQTTinfo = "";
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
// read rainfall
void fRainFall( void *pvParemeters )
{
  int16_t click = 0; //count tipping bucket clicks
  pcnt_counter_pause( PCNT_UNIT_1 );
  pcnt_counter_clear( PCNT_UNIT_1 );
  pcnt_counter_resume( PCNT_UNIT_1 );
  for  (;;)
  {
    xEventGroupWaitBits (eg, evtRainFall, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( (rtc.getHour(true) == 23) && (rtc.getMinute() == 59) )
    {
      pcnt_counter_pause( PCNT_UNIT_1 );
      rain = 0.0f;
      pcnt_counter_clear( PCNT_UNIT_1 );
      pcnt_counter_resume( PCNT_UNIT_1 );
    } else {
      pcnt_counter_pause( PCNT_UNIT_1 );
      pcnt_get_counter_value( PCNT_UNIT_1, &click );
      if ( click != 0 )
      {
        rain = rain + (0.2794f * (float)click);// 0.2794mm of rain per click
        pcnt_counter_clear( PCNT_UNIT_1 );
        //log_i( "count %d, rain rain = %f mm", click, rain );
      }
      pcnt_counter_resume( PCNT_UNIT_1 );
      click = 0;
    }
  }
  vTaskDelete ( NULL );
}
////
void fAnemometer( void *pvParameters )
{
  int16_t count = 0;
  pcnt_counter_clear(PCNT_UNIT_0);
  pcnt_counter_resume(PCNT_UNIT_0);
  for (;;)
  {
    xEventGroupWaitBits (eg, evtAnemometer, pdTRUE, pdTRUE, portMAX_DELAY);
    pcnt_counter_pause( PCNT_UNIT_0 );
    pcnt_get_counter_value( PCNT_UNIT_0, &count);
    kph = 2.4f * ((float)count / 60.0f);// A wind speed of 2.4km/h causes the switch to close once per second
    pcnt_counter_clear( PCNT_UNIT_0 );
    pcnt_counter_resume( PCNT_UNIT_0 );
  }
  vTaskDelete ( NULL );
}
//////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 86300; // 1st time update in 100 counts
  int maxNonMQTTresponse = 60;
  for (;;)
  {
    vTaskDelay( 1000 );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
  }
  vTaskDelete( NULL );
}
//////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      // parse the time from the OK message and update MCU time
      if ( String(px_message.topic) == topicOK )
      {
        if ( !TimeSet)
        {
          String temp = "";
          temp =  px_message.payload[0];
          temp += px_message.payload[1];
          temp += px_message.payload[2];
          temp += px_message.payload[3];
          int year =  temp.toInt();
          temp = "";
          temp =  px_message.payload[5];
          temp += px_message.payload[6];
          int month =  temp.toInt();
          temp =  "";
          temp =  px_message.payload[8];
          temp += px_message.payload[9];
          int day =  temp.toInt();
          temp = "";
          temp = px_message.payload[11];
          temp += px_message.payload[12];
          int hour =  temp.toInt();
          temp = "";
          temp = px_message.payload[14];
          temp += px_message.payload[15];
          int min =  temp.toInt();
          rtc.setTime( 0, min, hour, day, month, year );
          log_i( "rtc  %s ", rtc.getTime() );
          TimeSet = true;
        }
      }
      //
    } //if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK = 0;
    xSemaphoreGive( sema_mqttOK );
  }
} // void fparseMQTT( void *pvParameters )#include <ESP32Time.h>
//////
void fReadCurrent( void * parameter )
{
  const TickType_t xFrequency = 1000; //delay for mS
  const float mVperAmp        = 185.0f;
  float    ADbits             = 4096.0f;
  float    ref_voltage        = 3.3f;
  float    mA                 = 0.0f;
  float    adcValue           = 0.0f;
  float    Voltage            = 0.0f;
  float    Power              = 0.0f;
  float    offSET             = 0.0f;
  int      printCount         = 0;
  uint64_t TimePastKalman     = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  SimpleKalmanFilter KF_I( 1.0f, 1.0f, .01f );
  /*
     185mv/A = 5 AMP MODULE
     100mv/A = 20 amp module
     66mv/A = 30 amp module
  */
  String powerInfo = "";
  powerInfo.reserve( 150 );
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  TickType_t xLastWakeTime = xTaskGetTickCount();
  for (;;)
  {
    adc1_get_raw(ADC1_CHANNEL_3); // read once discard reading
    adcValue = ( (float)adc1_get_raw(ADC1_CHANNEL_3) );
    //log_i( "adcValue I = %f", adcValue );
    Voltage = ( (adcValue * ref_voltage) / ADbits ) + offSET; // Gets you mV
    mA = Voltage / mVperAmp; // get amps
    KF_I.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    mA = KF_I.updateEstimate( mA ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    printCount++;
    if ( printCount == 60 )
    {
      xSemaphoreTake( sema_CalculatedVoltage, portMAX_DELAY);
      Power = CalculatedVoltage * mA;
      //log_i( "Voltage=%f mA=%f Power=%f", CalculatedVoltage, mA, Power );
      printCount = 0;
      powerInfo.concat( String(CalculatedVoltage, 2) );
      xSemaphoreGive( sema_CalculatedVoltage );
      powerInfo.concat( ",");
      powerInfo.concat( String(mA, 4) );
      powerInfo.concat( ",");
      powerInfo.concat( String(Power, 4) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicPower, powerInfo.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      powerInfo = "";
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete( NULL );
} //void fReadCurrent( void * parameter )
////
void fReadBattery( void * parameter )
{
  float adcValue = 0.0f;
  const float r1 = 50500.0f; // R1 in ohm, 50K
  const float r2 = 10000.0f; // R2 in ohm, 10k potentiometer
  float Vbatt = 0.0f;
  int printCount = 0;
  float vRefScale = (3.3f / 4096.0f) * ((r1 + r2) / r2);
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  for (;;)
  {
    adc1_get_raw(ADC1_CHANNEL_0); //read and discard
    adcValue = float( adc1_get_raw(ADC1_CHANNEL_0) ); //take a raw ADC reading
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue = KF_ADC_b.updateEstimate( adcValue ); // apply simple Kalman filter
    Vbatt = adcValue * vRefScale;
    xSemaphoreTake( sema_CalculatedVoltage, portMAX_DELAY );
    CalculatedVoltage = Vbatt;
    xSemaphoreGive( sema_CalculatedVoltage );
    
      printCount++;
      if ( printCount == 3 )
      {
      log_i( "Vbatt %f", Vbatt );
      printCount = 0;
      }
    
    TimePastKalman = esp_timer_get_time(); // time of update complete
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    //log_i( "fReadBattery %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}
////
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  // setting must be set before a mqtt connection is made
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    vTaskDelay( 250 ); //task runs approx every 250 mS
  }
  vTaskDelete ( NULL );
}
////
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 4000 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
////
void connectToMQTT()
{
  MQTTclient.setKeepAlive( 90 ); // needs be made before connecting
  byte mac[5];
  WiFi.macAddress(mac);
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  while ( !MQTTclient.connected() )
  {
    // boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password, NULL , 1, true, NULL );
    vTaskDelay( 250 );
  }
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( topicOK );
} // void connectToMQTT()
////
void loop() {}

PCNT used to count pulses.

thanks for your comments , I did some change according to your tips , but it seems if have more than one interrupt, some time somewhere gonna lost

int RPM_1_RESET = 0;
#define PULSE_IN_1_PIN 25 //25-G1
volatile unsigned long pulseTime_1, lastTime_1;
volatile unsigned long pulse_wide_1;
static volatile uint8_t counnterL=0,L_rpm=0;

void ICACHE_RAM_ATTR isr_pulse_1() {
  L_rpm++;
  lastTime_1 = pulseTime_1;
  pulseTime_1 = micros();
  pulse_wide_1 = (pulseTime_1 - lastTime_1);

}
//////////////////////////////////////////////////////
void RPM_measure(){
  
   if (last_measurment_interval <  millis()-last_measurment) 
          {
            //Serial.println(last_measurment + last_measurment_interval);
            detachInterrupt(PULSE_IN_0_PIN);
            detachInterrupt(PULSE_IN_1_PIN);

            blinkLED();
            esp_task_wdt_reset();
             
            double sumR=0,sumL=0;
            double RPM_0=0,RPM_1=0;
            if(R_rpm)
            {

              //for(int i=0;i<NoS;i++) sumR+=listR[i]; RPM_0=60000000*NoS/sumR;  
              RPM_0=60000000/pulse_wide_0;
            }
            Serial.print(RPM_0);
            //Serial.print(6000000/pulse_wide_0);
            Serial.print(",");
            if(L_rpm) 
            {
              //for(int i=0;i<NoS;i++) sumL+=listL[i]; RPM_1=60000000*NoS/sumL; 
              RPM_1= 60000000/pulse_wide_1;
             }
            //Serial.print(6000000/pulse_wide_1);
            Serial.print(RPM_1);
            Serial.println();
//            RPM_0_RESET++; //increment reset value
//            RPM_1_RESET++;
//            if (RPM_0_RESET > 5) {
//                                    pulse_wide_0 = 0.0;
//                                    RPM_0_RESET = 0;
//                                  }
//            if (RPM_1_RESET > 5) {
//                                  pulse_wide_1 = 0.0;
//                                  RPM_1_RESET = 0;
//                                }
        
            
             if (flipper)    sendRPM(flipper,RPM_1);
             else            sendRPM(flipper,RPM_0);   
             flipper = !flipper;
            
            if(((esp_reset_reason() == 6) & !SW_RESET_BOOL) || TWAI_send_failed >= 10){ resetTWAI(); }
            last_measurment = millis();
            L_rpm=R_rpm=0;
            
            attachInterrupt(PULSE_IN_0_PIN, isr_pulse_0, RISING);
            attachInterrupt(PULSE_IN_1_PIN, isr_pulse_1, RISING); 
          
          }

}
//////////////////////////////////////////////

@Idahowalker @DrDiettrich thanks for the tips, actually someone else suggest to use the RMT lib for counting the pulse as well
can you please strip down your code so I can test it with 2 counters

@Idahowalker I wire the following code to count pulses for 2 pins however it's only returns 0, can you advise what did I wrong?

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "driver/pcnt.h"
#include "driver/periph_ctrl.h"
#include <esp_task_wdt.h> //for watchdog
#include "driver/gpio.h"


#define SPEED_TIMER     200 
const int16_t rpmPulseMax = 300;

hw_timer_t *timer = NULL;
double rpm_trg_L = 0;
double rpm_trg_R = 0;
double rpm_L = 0;
double rpm_R = 0;
TaskHandle_t th;

void loop_slow(void *pvParameters)
{
  TickType_t xLastWakeTime;
  const TickType_t xFrequency = 100 / portTICK_PERIOD_MS;
  // Initialise the xLastWakeTime variable with the current time.
  xLastWakeTime = xTaskGetTickCount();

  while (true)
  {

    //RPM
    uint64_t time_now;
    int16_t count_L;
    int16_t count_R;

    time_now = timerRead(timer);
    pcnt_get_counter_value(PCNT_UNIT_0, &count_L);
    pcnt_get_counter_value(PCNT_UNIT_1, &count_R);

    rpm_L = (double)(count_L) / ((double)time_now / 10000.0);
    rpm_R = (double)(count_R) / ((double)time_now / 10000.0);

    
    timer = timerBegin(0, 80, true); //80分周で1usec
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_clear(PCNT_UNIT_1);

   Serial.print("TrgRPM L:");
    Serial.print(rpm_L);
    Serial.print(" R:");
    Serial.println(rpm_R);

    // Wait for the next cycle.
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
  }
}

static unsigned long stimer=0;

void Calculate_Speed()
{
  int16_t count_L;
  int16_t count_R;
  
  if (millis() - stimer > SPEED_TIMER)
  {
    pcnt_get_counter_value(PCNT_UNIT_0, &count_L);
    pcnt_get_counter_value(PCNT_UNIT_1, &count_R);

    rpm_L = (double)(count_L) *30;
    rpm_R = (double)(count_R) *30;
    
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_clear(PCNT_UNIT_1);
    Serial.print("TrgRPM L:");
    Serial.print(rpm_L);
    Serial.print(" R:");
    Serial.println(rpm_R);
    stimer = millis();
  }
}

void pcnt_setup( int gpioA, int gpioB)
{
  
  pinMode(gpioA, INPUT);
  pinMode(gpioB, INPUT);
  
  pcnt_config_t pcnt_confA;
  pcnt_config_t pcnt_confB;

  pcnt_confA.unit       = PCNT_UNIT_0;
  pcnt_confB.unit       = PCNT_UNIT_1;
  
  pcnt_confA.channel    = PCNT_CHANNEL_0;
  pcnt_confB.channel    = PCNT_CHANNEL_1;
  
  pcnt_confA.pulse_gpio_num = gpioA;
  pcnt_confB.pulse_gpio_num = gpioB;
  
  pcnt_confA.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_confB.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  
  pcnt_confA.pos_mode     = PCNT_COUNT_INC;
  pcnt_confB.pos_mode     = PCNT_COUNT_INC;
  
  pcnt_confA.neg_mode     = PCNT_COUNT_DEC;
  pcnt_confB.neg_mode     = PCNT_COUNT_DEC;
  
  
   pcnt_confA.lctrl_mode   = PCNT_MODE_KEEP;
   pcnt_confB.lctrl_mode   = PCNT_MODE_KEEP;
  
  pcnt_confA.hctrl_mode   = PCNT_MODE_KEEP;
  pcnt_confB.hctrl_mode   = PCNT_MODE_REVERSE;
  
  pcnt_confA.counter_h_lim  = rpmPulseMax;
  pcnt_confB.counter_h_lim  = rpmPulseMax;
  
  pcnt_confA.counter_l_lim  = 0;
  pcnt_confB.counter_l_lim  = 0;

  
  

  /* Initialize PCNT unit */
  pcnt_unit_config(&pcnt_confA);
  pcnt_unit_config(&pcnt_confB);

  pcnt_counter_pause(PCNT_UNIT_0);
  pcnt_counter_clear(PCNT_UNIT_0);
  pcnt_counter_resume(PCNT_UNIT_0);
  pcnt_counter_pause(PCNT_UNIT_1);
  pcnt_counter_clear(PCNT_UNIT_1);
  pcnt_counter_resume(PCNT_UNIT_1);
}

void setup() {
  delay(1000);
  Serial.begin(115200);
  Serial.print("Running on core ");  Serial.println(xPortGetCoreID());
  pcnt_setup( GPIO_NUM_15, GPIO_NUM_25);

 //  xTaskCreatePinnedToCore(loop_slow, "loop_slow", 2048, NULL, 3, &th, tskNO_AFFINITY);

}

void loop() {
  Calculate_Speed();

}

Starting with the PCNT configuration function I declare the configuration structure like so: pcnt_config_t pcnt_config = {};. This way being a signal to the complier to make sure all the structure values are set to their default values.

Otherwise, the code looks good to me.

@Idahowalker I did , but result still ambiguous, not reliable
regards to this picture , both counter must show constant speed

TrgRPM L:0.00 R:-30.00
TrgRPM L:0.00 R:30.00
TrgRPM L:0.00 R:-30.00
TrgRPM L:0.00 R:0.00
TrgRPM L:0.00 R:0.00
TrgRPM L:0.00 R:0.00
TrgRPM L:30.00 R:0.00
TrgRPM L:-30.00 R:0.00
TrgRPM L:30.00 R:0.00
TrgRPM L:-30.00 R:0.00
TrgRPM L:0.00 R:0.00
TrgRPM L:0.00 R:0.00

Like I wrote above the initialization code of the PCNT looks good to me.