ESP32 Pulse Counter ISR not working properly

Hi,

I have a project that uses two Pulse Counters from the ESP32 Espressif library: Pulse Counter (PCNT) - ESP32 - — ESP-IDF Programming Guide v5.0 documentation

Both counters should have a high limit of e.g. 125 counts/pulses which should trigger the corresponding interrupt, problem is that it does not trigger my events with an output and it overwrites the values with 0 instead of keeping it stored.

Below is an excerpt of my code and serial output.

Code:

#include <driver/adc.h>
#include <driver/pcnt.h>

// ESP32 Pulse Counter
#define PCNT_UNIT_01 PCNT_UNIT_0   // Select the Pulse Count 0
#define PCNT_UNIT_02 PCNT_UNIT_1   // Select the Pulse Count 1

#define PCNT_H_LIM_VAL 125          // Set the high limit count to trigger the interrupt
#define PCNT_L_LIM_VAL 0            // Set the low limit count to trigger the interrupt
#define PCNT_FILTER_VAL 256         // Set the filter value in nanoseconds, 1 second = 1 000 000 000 nanoseconds

#define PCNT_INPUT_SIG_IO_01 13     // Pulse Input selected as GPIO14
#define PCNT_INPUT_SIG_IO_02 14     // Pulse Input selected as GPIO12

pcnt_isr_handle_t user_isr_handle1 = NULL; // User ISR handler for Interrupt
pcnt_isr_handle_t user_isr_handle2 = NULL; // User ISR handler for Interrupt

...

void setup()
{
    pinMode(13, INPUT_PULLUP);  // Set pin13 (GPIO14) input for capturing GM Tube 01 events (pulses)
    pinMode(14, INPUT_PULLUP);  // Set pin14 (GPIO12) input for  capturing GM Tube 02 events (pulses)

    Init_PulseCounter_01();
    Init_PulseCounter_02();

    ...
}

void loop()
{
  if(event1)
  {
    Serial.println(F("--- ISR ---"));
    Serial.println("| Counter of tube 1 exceeded the value of " + String(PCNT_H_LIM_VAL));
    Serial.println(F("--- ISR ---"));

    event1 = false;
  }

  if(event2)
  {
    Serial.println(F("--- ISR ---"));
    Serial.println("| Counter of tube 2 exceeded the value of " + String(PCNT_H_LIM_VAL));
    Serial.println(F("--- ISR ---"));

    event2 = false;
  }

  // If reached 01 seconds with a total of 60 seconds
  if (((millis() - previousMillis_1) >= 1000UL) && (increaseSecCount <= 60))
  {
    //int16_t count_01 = 0;

    pcnt_get_counter_value(PCNT_UNIT_01, &cps_1);
    pcnt_get_counter_value(PCNT_UNIT_02, &cps_2);

    // Recalute with tube dead time accounted for actual CPS of both tubes
    actual_cps_1 = (cps_1 / (1 - (cps_1 * tubeDeadTime)));
    actual_cps_2 = (cps_2 / (1 - (cps_2 * tubeDeadTime)));

    // Add a new datapoint entry for both tubes to the moving average array
    sensorMovingAvg = cps_sensor.reading(actual_cps_1);
    sensorMovingAvg = cps_sensor.reading(actual_cps_2);
    
    ....

    // If 61 seconds have been reached do uploads and reset vars
  if (((millis() - previousMillis_3) >= 61000UL) && (increaseSecCount >= 61))
  {
       ...
       Clean_Counters();
  }
}

// ISR Function for counter 1
void IRAM_ATTR Counter1_ISR(void *arg)             
{
  cps_1 = pcnt_get_counter_value(PCNT_UNIT_01, &cps_1);
  event1 = true;

  Serial.println("Counter1_ISR triggered");

  if(user_isr_handle1)
  {
    //Free the ISR service handle.
    esp_intr_free(user_isr_handle1);
    user_isr_handle1 = NULL;
  }
}

// ISR Function for counter 2
void IRAM_ATTR Counter2_ISR(void *arg)             
{
  cps_2 = pcnt_get_counter_value(PCNT_UNIT_02, &cps_2);
  event2 = true;

  Serial.println("Counter2_ISR triggered");

  if(user_isr_handle2)
  {
    //Free the ISR service handle.
    esp_intr_free(user_isr_handle2);
    user_isr_handle2 = NULL;
  }
}

void Init_PulseCounter_01(void)
{
  pcnt_config_t pcnt_config_01 = { };
  pcnt_config_01.pulse_gpio_num = PCNT_INPUT_SIG_IO_01;         // Set pulse input GPIO member
  pcnt_config_01.pos_mode = PCNT_COUNT_INC;                     // Set Counter mode: pos_mode – Counter mode when detecting positive edge
  pcnt_config_01.counter_h_lim = PCNT_H_LIM_VAL;                // Set maximum counter value = triggers event
  pcnt_config_01.unit = PCNT_UNIT_01;                           // Select pulse unit
  pcnt_config_01.channel = PCNT_CHANNEL_0;                      // Select PCNT channel
  pcnt_unit_config(&pcnt_config_01);                            // Configure PCNT

  pcnt_set_filter_value(PCNT_UNIT_01, PCNT_FILTER_VAL);         // Set filter value
  pcnt_filter_enable(PCNT_UNIT_01);                             // Enable filter

  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_H_LIM);              // Enable event for when PCNT watch point event: Maximum counter value
  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_L_LIM);
  pcnt_isr_register(Counter1_ISR, NULL, 0, &user_isr_handle1);  // Set call back function for the Event
  pcnt_intr_enable(PCNT_UNIT_01);                               // Enable Pulse Counter (PCNT)

  pcnt_counter_pause(PCNT_UNIT_01);                             // Pause PCNT counter
  pcnt_counter_clear(PCNT_UNIT_01);                             // Clear PCNT counter

  pcnt_counter_resume(PCNT_UNIT_01);
  
  Serial.println(F("PCNT_01 Init Completed"));
}

void Init_PulseCounter_02(void)
{
  pcnt_config_t pcnt_config_02 = { };
  pcnt_config_02.pulse_gpio_num = PCNT_INPUT_SIG_IO_02;         // Set pulse input GPIO member
  pcnt_config_02.pos_mode = PCNT_COUNT_INC;                     // Set Counter mode: pos_mode – Counter mode when detecting positive edge
  pcnt_config_02.counter_h_lim = PCNT_H_LIM_VAL;                // Set maximum counter value = triggers event
  pcnt_config_02.unit = PCNT_UNIT_02;                           // Select pulse unit
  pcnt_config_02.channel = PCNT_CHANNEL_0;                      // Select PCNT channel
  pcnt_unit_config(&pcnt_config_02);                            // Configure PCNT

  pcnt_set_filter_value(PCNT_UNIT_02, PCNT_FILTER_VAL);         // Set filter value
  pcnt_filter_enable(PCNT_UNIT_02);                             // Enable filter

  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_H_LIM);              // Enable event for when PCNT watch point event: Maximum counter value
  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_L_LIM);
  pcnt_isr_register(Counter2_ISR, NULL, 0, &user_isr_handle2);  // Set call back function for the Event
  pcnt_intr_enable(PCNT_UNIT_02);                               // Enable Pulse Counter (PCNT)

  pcnt_counter_pause(PCNT_UNIT_02);                             // Pause PCNT counter
  pcnt_counter_clear(PCNT_UNIT_02);                             // Clear PCNT counter

  pcnt_counter_resume(PCNT_UNIT_02);
  
  Serial.println(F("PCNT_02 Init Completed"));
}

// Function to clean the Counter and its variables
void Clean_Counters()                                       
{
  pcnt_counter_pause(PCNT_UNIT_01);    // Pause Pulse counters such that we can set event
  pcnt_counter_pause(PCNT_UNIT_02);

  pcnt_counter_clear(PCNT_UNIT_01);    // Clean Pulse Counters
  pcnt_counter_clear(PCNT_UNIT_02);

  pcnt_counter_resume(PCNT_UNIT_01);   // Resume Pulse Counters
  pcnt_counter_resume(PCNT_UNIT_02);
}

Serial output except:
Event 1:

19:39:03.928 -> --- 01 sec ---
19:39:03.928 -> | Tube 1 33 sec current count (cps_1) = 123
19:39:03.928 -> | Tube 2 33 sec current count (cps_2) = 44
19:39:03.928 -> | Tubes current total mavg 33 sec current count = 60
19:39:03.968 -> | Voltage tubes = 436.745
19:39:03.968 -> --- 01 sec ---
19:39:03.968 -> 33 of 60

--- THERE SHOULD BE AN OUTPUT HERE FROM EVENT1 ---

19:39:04.934 -> --- 01 sec ---
19:39:04.934 -> | Tube 1 34 sec current count (cps_1) = 2
19:39:04.934 -> | Tube 2 34 sec current count (cps_2) = 50
19:39:04.934 -> | Tubes current total mavg 34 sec current count = 59
19:39:04.981 -> | Voltage tubes = 435.981
19:39:04.981 -> --- 01 sec ---

Event 2:

19:37:16.041 -> --- 01 sec ---
19:37:16.041 -> | Tube 1 54 sec current count (cps_1) = 76
19:37:16.041 -> | Tube 2 54 sec current count (cps_2) = 125
19:37:16.041 -> | Tubes current total mavg 54 sec current count = 58
19:37:16.041 -> | Voltage tubes = 435.675
19:37:16.041 -> --- 01 sec ---
19:37:16.041 -> 54 of 60

--- THERE SHOULD BE AN OUTPUT HERE FROM EVENT2 ---

19:37:17.054 -> --- 01 sec ---
19:37:17.054 -> | Tube 1 55 sec current count (cps_1) = 78
19:37:17.054 -> | Tube 2 55 sec current count (cps_2) = 3
19:37:17.054 -> | Tubes current total mavg 55 sec current count = 58
19:37:17.054 -> | Voltage tubes = 433.536
19:37:17.054 -> --- 01 sec ---

Probably its something small I'm overlooking.

Thanks!

Here is the configuration I use to get the PCNT to keep the count.

  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

This setting made the difference for me.

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

Still seems to not keep the value, to make it easy you can view my current code here on my Github page: GitHub - DonZalmrol/GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E: GM_SI-22G Stationary Logger with Espressif ESP32-WROVER-E

The events triggers, keeps resetting the the counter(s), but is not displaying the text it should display the following:

Serial.println(F("--- ISR ---"));
Serial.println("| Counter of tube exceeded the value of " + String(PCNT_H_LIM_VAL));
Serial.println(F("--- ISR ---"));

Where for example, is cps_1 defined ?

As a global variable.

You can view the full code on my GH: GitHub - DonZalmrol/GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E: GM_SI-22G Stationary Logger with Espressif ESP32-WROVER-E

This is set in an ISR/callback (asynchronous activity) and must be declared as volatile otherwise the compiler may generate code which uses a stale version of this variable.

boolean eventTriggerd = false;

ok, but how could I then use "eventTriggerd" in my loop if I can't declare it globally?

Just declare is globally as you have done but as:

volatile bool eventTriggerd = false; // volatile because it is set in an ISR

Thanks, tried it but its still the same. Also my two counter keep getting cleared/ reset to 0.

It seems odd that you are definining pcnt_config_02 local to a function. It will soon disappear when the function returns. Try making it global instead.

That function is only called by my setup() so the function only runs once :slight_smile:
Changed to a global one, but doesn't change anything.

When the count exceeds 150 its reset to 0, instead of continuing the counts and activating the bool eventTriggerd to display/ run something else.

But it should be kept as I set the:

pcnt_config_01.lctrl_mode = PCNT_MODE_KEEP;
pcnt_config_01.hctrl_mode = PCNT_MODE_KEEP;

And then only cleared when I call the Clean_Counters function to reset the values after 61 seconds.

PS: I have two counters in use for two tubes.

This from your Github repository GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E/Environmental_Stationary_Logger_V1.0.ino at main · DonZalmrol/GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E · GitHub :

static void Init_PulseCounter_01(void)
{
  pcnt_config_t pcnt_config_01 = { };
  pcnt_config_01.pulse_gpio_num = PCNT_INPUT_SIG_IO_01;         // Set pulse input GPIO member
  //pcnt_config_01.ctrl_gpio_num = PCNT_PIN_NOT_USED;
  // What to do on the positive / negative edge of pulse input?
  pcnt_config_01.pos_mode = PCNT_COUNT_INC;   // Count up on the positive edge
  //pcnt_config_01.neg_mode = PCNT_COUNT_DIS;   // Count down disable

  // What to do when control input is low or high?
  pcnt_config_01.lctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if low
  pcnt_config_01.hctrl_mode = PCNT_MODE_KEEP;    // Keep the primary counter mode 

  // Set the maximum and minimum limit values to watch
  pcnt_config_01.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config_01.counter_l_lim  = PCNT_L_LIM_VAL;

  pcnt_config_01.unit = PCNT_UNIT_01;                           // Select pulse unit
  pcnt_config_01.channel = PCNT_CHANNEL_0;                      // Select PCNT channel 0
  pcnt_unit_config(&pcnt_config_01);                            // Configure PCNT

  pcnt_set_filter_value(PCNT_UNIT_01, PCNT_FILTER_VAL);         // Maximum filter_val should be limited to 1023.
  pcnt_filter_enable(PCNT_UNIT_01);                             // Enable filter

  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_H_LIM);              // Enable event for when PCNT watch point event: Maximum counter value
  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_L_LIM);

  pcnt_isr_register(Counter_ISR, NULL, 0, &user_isr_handle);    // Set call back function for the Event
  pcnt_intr_enable(PCNT_UNIT_01);                               // Enable Pulse Counter (PCNT)

  pcnt_counter_pause(PCNT_UNIT_01);                             // Pause PCNT counter
  pcnt_counter_clear(PCNT_UNIT_01);                             // Clear PCNT counter

  pcnt_counter_resume(PCNT_UNIT_01);
  
  Serial.println(F("PCNT_01 Init Completed"));
}

Yes, I know it is only called once and that you have two more or less identical setup routines.

You are creating a configuration in a struct called pcnt_config_01 which is local to the function Init_PulseCounter_01(), then handing the address of that struct over to something external here: pcnt_unit_config(&pcnt_config_01).
When the function Init_PulseCounter_01() returns, that configuration is lost. What I can't see is if pcnt_unit_config() makes its own copy of that configuration information, however that would be quite unusual.

One other thing you can try is adding the static keyword here:

static pcnt_config_t pcnt_config_01 = { }; // static to retain the storage space after the enclosing function returns

and, of course, here too:

static pcnt_config_t pcnt_config_02 = { };

Did you base this set up routine on an example you found ? If so, provide a link to it.

If that does not work, then I suggest that you set up a very basic but compilable sketch with just the minimum code required to demonstrate your problem with the pulse counter and post that so maybe someone else can try it.

Thanks for your help so far! I've created a new small project that I needed to do anyways, it includes the PCNTs, the same thing happens.

It looks like the "Counter_ISR" is called once but never recalled when it should get triggered.

The values of both counters don't seem to keep the values when the "PCNT_H_LIM_VAL" is reached and keeps resetting them to 0, even while "hctrl_mode" has been set to "PCNT_MODE_KEEP".

New (simplified) code:

I've just glanced over it:
This should probably also be volatile:
pcnt_isr_handle_t user_isr_handle = NULL; // User ISR handler for Interrupt

EDIT

OK. You've said the ISR is called only once.
Can you explain what this code is doing in the ISR?

// ISR Function for counter 1
static void IRAM_ATTR Counter_ISR(void *arg)
{ 
   eventTriggerd = true; 
   esp_intr_disable(user_isr_handle);
   user_isr_handle = NULL;
}

Is the ISR disabling itself? If so, where are you re-enabling it?
Since the ISR appears to be shared between two instances of the counter this would appear to affect the other counter as well.

Is this the example you followed? : esp-idf/pcnt_event_example_main.c at v4.4.2 · espressif/esp-idf · GitHub

If not, post the example that you found.

Well I got esp_intr_disable(user_isr_handle); from this forum post How to use "Pulse Counter" module usage under Arduino framework - ESP32 Forum

If I remove the esp_intr_disable(user_isr_handle); then my ESP32 Wrover-E simply crashes. I was also starting to look into the esp_intr options.

And you are right, it seems to be disabling itself and not re-enabling because that's not anywhere in my current code.

Updated my code: GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E/Environmental_Stationary_Logger_V1.0.ino at main · DonZalmrol/GM_SI-22G-Stationary-Logger-with-Espressif-ESP32-WROVER-E · GitHub

But it keeps crashing when PCNT_UNIT_02 gets triggered.

My used examples:

maybe this gets you further. I've changed the way the ISR is called, based on this: esp-idf/pcnt_event_example_main.c at v4.4 · espressif/esp-idf · GitHub , and I've marked changes with "//!!"

// https://github.com/DonZalmrol/ESP32-Wrover-E-Dual-GM-tube-deadtime-calculator/blob/main/GM%20Tube%20Dead%20Time%20Calculator/GM%20Tube%20Dead%20Time%20Calculator.ino 

// Include libraries
#include <driver/pcnt.h>
#include <esp_adc_cal.h>

// Helper functions declarations
float displayTubeVoltage(float);
static void Init_PulseCounter_01(void);
static void Init_PulseCounter_02(void);

// Variables
int countingTimeTotal = 60;
int increaseSecCount = 1, increaseSixtySecCount = 0, countingTime = 60;
float tubeVoltage = 0.0, deadtime = 0.0;

int16_t cps_1 = 0, cps_2 = 0, cpm_temp = 0;
unsigned long currentMillis = 0, previousMillis_1 = 0, previousMillis_2 = 0;

// ESP32 Pulse Counter
static pcnt_config_t pcnt_config_01 = {};
static pcnt_config_t pcnt_config_02 = {};

#define PCNT_UNIT_01 PCNT_UNIT_0   // Select the Pulse Counter Unit 0
#define PCNT_UNIT_02 PCNT_UNIT_1   // Select the Pulse Counter Unit 1

#define PCNT_H_LIM_VAL 2          // Set the high limit count to trigger the interrupt  //!! was 100
#define PCNT_L_LIM_VAL 0            // Set the low limit count to trigger the interrupt
#define PCNT_FILTER_VAL 0         // Set the filter value from 0-1023

#define PCNT_INPUT_SIG_IO_01 13     // Pulse Input selected as GPIO14
#define PCNT_INPUT_SIG_IO_02 14     // Pulse Input selected as GPIO12

volatile bool eventTriggerd = false;
pcnt_isr_handle_t user_isr_handle = NULL; // User ISR handler for Interrupt


//!! moved
// ISR Function for counter 1
void IRAM_ATTR Counter_ISR(void *arg)  //!! was static
{
  eventTriggerd = true;

  // esp_intr_disable(user_isr_handle);
  // user_isr_handle = NULL;
}




//--------------------//
//--- START SETUP ---//
//------------------//
void setup()
{
  // Initialize UART on 115200 baud rate for ethernet shield
  Serial.begin(115200);

  Serial.println(F("Initializing Serial."));
  while (!Serial)
  {
    Serial.print(".");
    delay(100);
  }
  Serial.println(F("Serial initialized."));

  // Initialize Arduino pins
  pinMode(13, INPUT_PULLUP);  // Set pin13 (GPIO14) input for capturing GM Tube 01 events (pulses)
  pinMode(14, INPUT_PULLUP);  // Set pin14 (GPIO12) input for  capturing GM Tube 02 events (pulses)

  // Initialize Pulse Counter (PCNT)
  Init_PulseCounter_01();
  Init_PulseCounter_02();

  Serial.println(F("--- Start program ---"));
  Serial.println(F("\n"));
}
//------------------//
//--- END SETUP ---//
//----------------//

//-------------------//
//--- START LOOP ---//
//-----------------//
void loop()
{
  // If reached 01 seconds with a total of countingTime seconds
  if (((millis() - previousMillis_1) >= 1000UL) && (increaseSecCount <= countingTimeTotal))
  {
    Serial.println("| eventTriggerd value = " + String(eventTriggerd));
    if(eventTriggerd)
    {
      Serial.println(F("\n--- ISR ---"));  //!!
      Serial.println("| Counter of tube exceeded the value of " + String(PCNT_H_LIM_VAL));
      Serial.println(F("--- ISR ---\n"));  //!!

      eventTriggerd = false;
    }

    pcnt_get_counter_value(PCNT_UNIT_01, &cps_1);
    pcnt_get_counter_value(PCNT_UNIT_02, &cps_2);

    Serial.println(F("--- 01 sec ---"));
    Serial.println("| Tube 1 " + String(increaseSecCount) + " sec current count (cps_1) = " + String(cps_1));
    Serial.println("| Tube 2 " + String(increaseSecCount) + " sec current count (cps_2) = " + String(cps_2));
    Serial.println("| Tube voltage = " + String(displayTubeVoltage(), 3) + " V");
    Serial.println(F("--- 01 sec ---"));

    // Display 1 second tick for a total of 60
    Serial.println(String(increaseSecCount) + " of " + String(countingTimeTotal));

    increaseSecCount++;

    previousMillis_1 = millis();
    previousMillis_2 = millis();
  }
  // If countingTime seconds have been reached do simple check of values
  if ( ( (millis() - previousMillis_2) >= (countingTimeTotal * 1000UL) + 1000UL ) && (increaseSecCount >= countingTimeTotal))
  {
    // Raw CPM
    //cpm_temp = cps_1 + cps_2;

    // Print new line
    Serial.println(F("\n"));

    /*
      // Uncomment below for calculating the tube dead time, only use when you have a high enough test source!
      // Otherwise your ESP32 will
      // Values from Excel
      float c3 = countingTimeTotal;
      float c5 = cps_1;
      float c6 = cps_2;
      floatc 7 = 1; //cpm_temp OR 1
      float rc = 1.0;
      float deadtime = ( (C5/C3) + (C6/C3) - (C7/C3) ) / ( 2 * (C5/C3) * (C6/C3) );
    */

    Serial.println("--- " + String(increaseSecCount) + " sec ---");
    Serial.println("| Tube 1 C.P.M = " + String(cps_1));
    Serial.println("| Tube 2 C.P.M = " + String(cps_2));
    Serial.println("| Tube voltage = " + String(displayTubeVoltage(), 3) + " V");
    Serial.println("--- " + String(increaseSecCount) + " sec ---");

    Serial.println(String(increaseSecCount) + " of " + String(countingTimeTotal));

    // Print new line
    Serial.println(F("\n"));

    // Reset variables
    cps_1 = 0, cps_2 = 0, cpm_temp = 0, deadtime = 0.0;
    increaseSecCount = 1;
    
    Clean_Counters();

    previousMillis_1 = millis();
    previousMillis_2 = millis();
  }
}
//-----------------//
//--- END LOOP ---//
//---------------//

//------------------------//
//--- START functions ---//
//----------------------//

// Measure Tube voltage through A0
float displayTubeVoltage(void)
{
  float adcInput = 0.0, lowVoltage = 0.0, voltage_offset = 0.0, highVoltage = 0.0;

  adc1_config_width(ADC_WIDTH_12Bit);
  //adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_11db);
  adcInput = adc1_get_raw(ADC1_CHANNEL_5);

  // ESP32 pin 33 measures from 0-3.3V and has a width of 0-4096
  // Use 3.4 for as correcting value
  lowVoltage = ((adcInput * 3.4 ) / 4096.0);
  
  /*
    If you want to monitor high voltage on tubes with microcontroller ADC you can use A0 module output.
    ~190-195 conversion factor according to Alex - RH Electronics 2021-03-15
    ~184.097 calcultated on 2022-09-20 for ESP32 Wrover-E after research, see below
    Calculate your value with the "ADC Linearity calculator", yellow is your measured ADC value.
    Measure the voltage between your tubes and multiple x2, measure voltage of pin A0
    
    Measured:
     - ADC = 2863
     - Voltage of tube = 2x 218.8V = 437.6V
     - Voltage of A0 = 2.37V
    Calculated:
     - Voltage should be 2377mV
     - Conversion factor calculation = 437.6V / 2.377 = 184.097
     - Confirm with calculated high voltage table     
  */
  voltage_offset = 184.097;
  highVoltage = (lowVoltage * voltage_offset);

  // Uncomment for testing
  //Serial.println("| adcInput = " + String(adcInput, 3));
  //Serial.println("| A0 LV = " + String(lowVoltage, 3));
  //Serial.println("| Tubes HV = " + String(highVoltage, 3));

  return highVoltage;
}


static void Init_PulseCounter_01(void)
{
  pcnt_config_01.pulse_gpio_num = PCNT_INPUT_SIG_IO_01;   // Set pulse input GPIO member
  pcnt_config_01.ctrl_gpio_num = PCNT_PIN_NOT_USED;       // No GPIO for control

  // What to do on the positive / negative edge of pulse input?
  pcnt_config_01.pos_mode = PCNT_COUNT_INC;   // Count up on the positive edge
  pcnt_config_01.neg_mode = PCNT_COUNT_DIS;   // Count down disable

  // What to do when control input is low or high?
  pcnt_config_01.lctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if low
  pcnt_config_01.hctrl_mode = PCNT_MODE_KEEP;    // Keep the primary counter mode 

  // Set the maximum and minimum limit values to watch
  pcnt_config_01.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config_01.counter_l_lim  = PCNT_L_LIM_VAL;

  pcnt_config_01.unit = PCNT_UNIT_01;                           // Select pulse unit
  pcnt_config_01.channel = PCNT_CHANNEL_0;                      // Select PCNT channel 0
  pcnt_unit_config(&pcnt_config_01);                            // Configure PCNT

  pcnt_counter_pause(PCNT_UNIT_01);                             // Pause PCNT counter
  pcnt_counter_clear(PCNT_UNIT_01);                             // Clear PCNT counter

  pcnt_set_filter_value(PCNT_UNIT_01, PCNT_FILTER_VAL);         // Maximum filter_val should be limited to 1023.
  pcnt_filter_enable(PCNT_UNIT_01);                             // Enable filter

  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_H_LIM);              // Enable event for when PCNT watch point event: Maximum counter value
  pcnt_event_enable(PCNT_UNIT_01, PCNT_EVT_L_LIM);

  
  // pcnt_isr_register(Counter_ISR, NULL, 0, &user_isr_handle);    // Set call back function for the Event  //!! 
  pcnt_isr_service_install(0);
  pcnt_isr_handler_add(PCNT_UNIT_01, Counter_ISR, NULL);

   
  pcnt_intr_enable(PCNT_UNIT_01);                               // Enable Pulse Counter (PCNT)

  pcnt_counter_resume(PCNT_UNIT_01);
  
  Serial.println(F("PCNT_01 Init Completed"));
}

static void Init_PulseCounter_02(void)
{
  pcnt_config_02.pulse_gpio_num = PCNT_INPUT_SIG_IO_02;   // Set pulse input GPIO member
  pcnt_config_02.ctrl_gpio_num = PCNT_PIN_NOT_USED;       // No GPIO for control 

  // What to do on the positive / negative edge of pulse input?
  pcnt_config_02.pos_mode = PCNT_COUNT_INC;   // Count up on the positive edge
  pcnt_config_02.neg_mode = PCNT_COUNT_DIS;   // Count down disable

  // What to do when control input is low or high?
  pcnt_config_02.lctrl_mode = PCNT_MODE_KEEP;     // Keep the primary counter mode if low
  pcnt_config_02.hctrl_mode = PCNT_MODE_KEEP;    // Keep the primary counter mode

  // Set the maximum and minimum limit values to watch
  pcnt_config_02.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config_02.counter_l_lim  = PCNT_L_LIM_VAL;

  pcnt_config_02.unit = PCNT_UNIT_02;                           // Select pulse unit
  pcnt_config_02.channel = PCNT_CHANNEL_1;                      // Select PCNT channel 1
  pcnt_unit_config(&pcnt_config_02);                            // Configure PCNT

  pcnt_counter_pause(PCNT_UNIT_02);                             // Pause PCNT counter
  pcnt_counter_clear(PCNT_UNIT_02);                             // Clear PCNT counter

  pcnt_set_filter_value(PCNT_UNIT_02, PCNT_FILTER_VAL);         // Set filter value
  pcnt_filter_enable(PCNT_UNIT_02);                             // Enable filter

  pcnt_event_enable(PCNT_UNIT_02, PCNT_EVT_H_LIM);              // Enable event for when PCNT watch point event: Maximum counter value
  pcnt_event_enable(PCNT_UNIT_02, PCNT_EVT_L_LIM);

  // pcnt_isr_register(Counter_ISR, NULL, 0, &user_isr_handle);    // Set call back function for the Event  //!! 
  pcnt_isr_service_install(0);  //!!
  pcnt_isr_handler_add(PCNT_UNIT_02, Counter_ISR, NULL);  //!!
  
  pcnt_intr_enable(PCNT_UNIT_02);                               // Enable Pulse Counter (PCNT)

  pcnt_counter_resume(PCNT_UNIT_02);
  
  Serial.println(F("PCNT_02 Init Completed"));
}

// Function to clean the Counter and its variables
static void Clean_Counters()                                       
{
  pcnt_counter_pause(PCNT_UNIT_01);    // Pause Pulse counters such that we can set event
  pcnt_counter_pause(PCNT_UNIT_02);

  pcnt_counter_clear(PCNT_UNIT_01);    // Clean Pulse Counters
  pcnt_counter_clear(PCNT_UNIT_02);

  pcnt_counter_resume(PCNT_UNIT_01);   // Resume Pulse Counters
  pcnt_counter_resume(PCNT_UNIT_02);
}

Sample output demonstrating multiple calls of the ISR

11:33:52.068 -> --- ISR ---
11:33:52.068 -> | Counter of tube exceeded the value of 2
11:33:52.068 -> --- ISR ---
11:33:52.068 -> 
11:33:52.068 -> --- 01 sec ---
11:33:52.068 -> | Tube 1 38 sec current count (cps_1) = 1
11:33:52.068 -> | Tube 2 38 sec current count (cps_2) = 1
11:33:52.108 -> | Tube voltage = 613.552 V
11:33:52.108 -> --- 01 sec ---
11:33:52.108 -> 38 of 60
11:33:53.113 -> | eventTriggerd value = 1
11:33:53.113 -> 
11:33:53.113 -> --- ISR ---
11:33:53.113 -> | Counter of tube exceeded the value of 2
11:33:53.113 -> --- ISR ---
11:33:53.113 -> 
11:33:53.113 -> --- 01 sec ---
11:33:53.113 -> | Tube 1 39 sec current count (cps_1) = 1
11:33:53.113 -> | Tube 2 39 sec current count (cps_2) = 1
11:33:53.113 -> | Tube voltage = 597.965 V
11:33:53.113 -> --- 01 sec ---
11:33:53.113 -> 39 of 60

1 Like

Well I'll be damned, that seems to have fixed it!
Greatly appreciate the help!

Only thing that still doesn't work is that it keeps clearing the counter values after the high limit has been reached. Might be something in the IRAM_ATTR that reset it?

You appear to be calling this each loop() iteration: Clean_Counters() . What happens if you remove it?

Doesn't change anything, I'm starting to believe that the "PCNT_MODE_KEEP" is not for keeping the value stored but to "keep" the current (increase) counting mode that was set by "PCNT_COUNT_INC".

So that would mean that the value should be fetched and stored separately before its cleared back to 0.

enum pcnt_channel_level_action_t: Pulse Counter (PCNT) - ESP32 - — ESP-IDF Programming Guide latest documentation

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