ESP WROOM 32 - digitalWrite does not work when executed in interrupt

Hello
I'm trying to use two timers to get behaviour:
Turn on pump -> Wait 5 seconds -> Turn off pump -> wait 15 seconds -> Turn on pump -> ...

#include "Pump.h"
#include "globals.h"
#include <BluetoothSerial.h>
hw_timer_t *pumpTimer = NULL; // Timer dla pompy
hw_timer_t *waitTimer = NULL; // Timer oczekiwania

const unsigned long PUMP_DURATION = 5000000;
const unsigned long WAIT_DURATION = 15000000;
bool pumpActive = false;
void IRAM_ATTR onPumpTimer()
{
  digitalWrite(5, HIGH);
  Serial.println("Pompa OFF");
  pumpActive = false;
  timerAlarmEnable(waitTimer);
}

void IRAM_ATTR onWaitTimer()
{
  if (!pumpActive)
  {
    digitalWrite(5, LOW);
    Serial.println("Pompa ON");
    pumpActive = true;
    timerAlarmWrite(pumpTimer, PUMP_DURATION, false); 
    timerAlarmEnable(pumpTimer);
    timerAlarmDisable(waitTimer);
  }
}

void setup()
{
   InitializeSensors();
  Pump::Initialize();
  Serial.begin(115200);
  pinMode(5, OUTPUT);
  pumpTimer = timerBegin(0, 80, true);
  waitTimer = timerBegin(1, 80, true);
  timerAttachInterrupt(pumpTimer, &onPumpTimer, true);
  timerAttachInterrupt(waitTimer, &onWaitTimer, true);
  timerAlarmWrite(waitTimer, WAIT_DURATION, true); 
  timerAlarmEnable(waitTimer);
}

// int ReadMoisture()
// {
//   int dry = 2695, wet = 1100;
//   int data = analogRead(AOUT);
//   int moistValue = map(data, dry, wet, 0, 100);
//   return moistValue;
// }

void loop()
{
  
}

It goes to both interrupts setting high state on pin, but it's always high, event though there is digitalWrite(5,LOW) in second interrupt. I've checked this and it works:

void loop()
{
  digitalWrite(5,HIGH);
  delay(1000); //works fine
  digitalWrite(5,LOW);
  delay(1000)
}

What am I missing?

It’s not the digitalWrite() that doesn’t work in interrupt, it’s the Serial.print()

And the main problem is probably that you did not defile the variables changed in the interrupt as volatile

1 Like

Changed to this but still the same.

#include "Pump.h"
#include "globals.h"
#include <BluetoothSerial.h>
hw_timer_t *pumpTimer = NULL; // Timer dla pompy
hw_timer_t *waitTimer = NULL; // Timer oczekiwania

volatile const unsigned long PUMP_DURATION = 5000000;
volatile const unsigned long WAIT_DURATION = 15000000;
volatile bool pumpActive = false;
void IRAM_ATTR onPumpTimer()
{
  digitalWrite(5, HIGH);
  pumpActive = false;
  timerAlarmEnable(waitTimer);
}

void IRAM_ATTR onWaitTimer()
{
  if (!pumpActive)
  {
    digitalWrite(5, LOW);
    pumpActive = true;
    timerAlarmWrite(pumpTimer, PUMP_DURATION, false); 
    timerAlarmEnable(pumpTimer);
    timerAlarmDisable(waitTimer);
  }
}

void setup()
{
   //InitializeSensors();
  //Pump::Initialize();
  //Serial.begin(115200);
  pinMode(5, OUTPUT);
  pumpTimer = timerBegin(0, 80, true);
  waitTimer = timerBegin(1, 80, true);
  timerAttachInterrupt(pumpTimer, &onPumpTimer, true);
  timerAttachInterrupt(waitTimer, &onWaitTimer, true);
  timerAlarmWrite(waitTimer, WAIT_DURATION, true); 
  timerAlarmEnable(waitTimer);
}

// int ReadMoisture()
// {
//   int dry = 2695, wet = 1100;
//   int data = analogRead(AOUT);
//   int moistValue = map(data, dry, wet, 0, 100);
//   return moistValue;
// }

void loop()
{
  
}

Hi @viperoo1500100900 ,

Welcome to the forum..
You know this might be a good chance to play with the ESP Timer..
High Resolution Timer ESP32

did a test sketch, using the timer to blink onboard led..
ESP32 Timer

might be worth a peek..

good luck.. ~q

So, do you need to enable the pumpTimer as well, would you say?

You know you could do this in the loop without any timers..
Maybe there's another reason for the complexity??

~q

Well I also would like to add sending data via Bluetooth so I guess I can't use delay() to not block ESP. I would like to just have cyclic Turn on-> wait some time-> turn off etc. Don't know if my solution is right.

It is going to be a better option?

I wouldn't use delay either..
you're duration is quite large, can easily track this using a millis timer and state machine ..
won't slow the loop down at all and no complexity..

would you like an example??

~q

Yes, please if it's not the problem. The timers I used was something I first saw and tried to use them. And I tought interrupts will be good when also bluetooth transmission is present.

ok, give me a few..
on the phone..

~q

check this..

#define PUMP_PIN 5

#define MINS  1000 * 60

unsigned long lastPump;
unsigned long intervalPump = 5 * MINS;
bool pumpOn = false;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  pinMode(PUMP_PIN, OUTPUT);

}

void loop() {

  CheckPump();

}

void CheckPump() {
  unsigned long now = millis();

  if (now - lastPump >= intervalPump) {
    lastPump = now;
    if (!pumpOn) {
      //turning pump on
      Serial.println("Pump On");
      digitalWrite(PUMP_PIN, HIGH);
      //set new interval 15 minutes..
      intervalPump = 15 * MINS;
      pumpOn = true;
    } else {
      //turning pump off
      Serial.println("Pump Off");
      digitalWrite(PUMP_PIN, LOW);
      //set new interval 5 mins..
      intervalPump = 5 * MINS;
     pumpOn = false;
    }
  }

}


no timers needed..
takes 5 minutes before pump starts..
could initially set the intervalPump to 0 and it would turn on at boot..
keep your loop fast, avoid delay..

good luck.. ~q

could move it into it's own thread..


#define PUMP_PIN 5 * 1000

#define MINS  1000




void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  pinMode(PUMP_PIN, OUTPUT);

  xTaskCreate( CheckPump, "Pump Task", 1000, NULL, 1, NULL);

}

void loop() {

  delay(10);


}

void CheckPump(void *pvParameters) {

  unsigned long lastPump;
  unsigned long intervalPump = 5 * MINS;
  bool pumpOn = false;

  while (1) {
    unsigned long now = millis();

    if (now - lastPump >= intervalPump) {
      lastPump = now;
      if (!pumpOn) {
        //turning pump on
        Serial.println("Pump On");
        digitalWrite(PUMP_PIN, HIGH);
        //set new interval 15 minutes..
        intervalPump = 15 * MINS;
      } else {
        //turning pump off
        Serial.println("Pump Off");
        digitalWrite(PUMP_PIN, LOW);
        //set new interval 5 mins..
        intervalPump = 5 * MINS;
      }
    }
  delay(1000);// breath some..
  }

}

~q

And when I want to add sending data via Bluetooth to phone I can just put code to do that in loop()?
Or also use xTaskCreate()?

Have you ever coded in a multi-tasking environment? There's something of a learning curve.

Yes..
The first example uses the loop..
The second example is just putting the routine into its own thread..
So far as everything sits now, I would use the loop, less complex..

good luck.. ~q

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