Need Help: Incorporating delay timing in a hardware interrupt

Hey everyone, I’m currently working on creating a display robot for the upcoming Burning Man festival. I got stumped on the power systems amp relays and using interrupts. I’m using an Arduino Mega ADK, along with an 8 relay board hooked up to the Arduino.

We’re using delays because the relays aren’t fast, and they have to be done in a certain order. In addition, we need to give the equipment that’s attached to the relays time to power down before going to the next step.

The problem is I really want to implement this via interrupts, primarily for our most important functions, like the activity monitor, the rain monitor, and the fan functions. Here’s the code.

#include <Wire.h>
#include <RTClib.h>

RTC_DS1307 rtc;  // To calculate the date and time functions for the main program

/************************************************************************
Setup the power system relays on the Arduino Mega
************************************************************************/

const int Relay01 = 39;  // Across Generator relay 1:2
const int Relay02 = 37;  // Across Generator relay 2:3
const int Relay03 = 35;  // Across Generator relay 5:6
const int Relay04 = 33;  // Amp 1
const int Relay05 = 31;  // Amp 2
const int Relay06 = 29;  // Amp 3
const int Relay07 = 27;  // Cooling Fan
const int Relay08 = 25;  // Night Lights

/*************************************************************************
Setup the Input Monitoring Pins: NOTE - for test purposes, we're using
rainPin 3 as the shutdown pin during simulation, cause we do want to shut
down the system when it's raining!
*************************************************************************/

const int activityPin = 2;  // Activity Monitor pin on interrupt 2 - returns HIGH or LOW
const int rainPin = 3;      // Rain Monitor pin on interrupt 3  - returns HIGH or LOW
const int temperaturePin = 21;  // Temperature Monitor on interrupt 21  - returns HIGH or LOW
const int batteryPin = 4;   // Battery Voltage check - on a scheduled time basis - returns HIGH or LOW
const int enginePin = 5;    // Engine on or off check - returns HIGH or LOW

/**************************************************************************
Setup our variables that are read in from the input pins
**************************************************************************/

int val_activity = 0;    // activity variable
int val_rain = 0;        // rain variable
int val_temp = 0;        // temperature variable
int val_battery = 0;      // battery variable
int val_engine = 0;      // engine variable

/***************************************************************************
Setup our integer variables for the engine logic to start or shut it off.
These correspond to the amp relays on the generator engine. NOT the relays
on the 8 relay board!
***************************************************************************/

int rel01;
int rel02;
int rel03;


void setup() {
  
  /***************************************************************
  Setup the Relay pins to output
  ***************************************************************/
  
  pinMode(Relay01, OUTPUT);
  pinMode(Relay02, OUTPUT);
  pinMode(Relay03, OUTPUT);
  pinMode(Relay04, OUTPUT);
  pinMode(Relay05, OUTPUT);
  pinMode(Relay06, OUTPUT);
  pinMode(Relay07, OUTPUT);
  pinMode(Relay08, OUTPUT);
  
  /***************************************************************
  Setup the 8 relay board all to HIGH. It's a negative logic board.
  HIGH means the relay is OPEN. LOW means the relay is closed
  ****************************************************************/
  digitalWrite(Relay01, HIGH);
  digitalWrite(Relay02, HIGH);
  digitalWrite(Relay03, HIGH);
  digitalWrite(Relay04, HIGH);
  digitalWrite(Relay05, HIGH);
  digitalWrite(Relay06, HIGH);
  digitalWrite(Relay07, HIGH);
  digitalWrite(Relay08, HIGH);
  
  /***************************************************************
  Setup the Monitoring Pins to Input
  ***************************************************************/
  pinMode(activityPin, INPUT);
  pinMode(rainPin, INPUT);
  pinMode(temperaturePin, INPUT);
  pinMode(batteryPin, INPUT);
  pinMode(enginePin, INPUT);
 
 /*****************************************************************
 Setup the RTC Clock Function
 *****************************************************************/
 
#ifdef AVR
  Wire.begin();
#else
  Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino Due
#endif
  rtc.begin();

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(__DATE__, __TIME__));
  }
 
 /*****************************************************************
 Setup the Serial Port
 *****************************************************************/
 
 Serial.begin(9600);
 
/*****************************************************************
Attach the interrupts to test the system start and
system stop functions.
*******************************************************************/
attachInterrupt(2, system_start, RISING);
attachInterrupt(3, system_stop, RISING);
  
  
}

void loop() {
  
   //val_activity = digitalRead(activityPin);  // read in the activity pin
   // val_rain = digitalRead(rainPin);  // read in the shutdown pin
   
   //Serial.println(val_activity);
   //Serial.println(val_rain);
   // buttontest(); // The button test simply emulates our inputs into the robot.
}

void buttontest() {
  
   if (val_activity == 1) {
    system_start();
  } else {
  }
  
    if (val_rain == 1) {
      system_stop();
    } else {
    }
  
  
}

void system_start() {
  
  delay(500);
  digitalWrite(Relay01, LOW);  // attached to generator relay 1 and 2 CLOSED
  rel01 = 0;                   // set rel01 value to 0 to indicate state for fan function
  delay(500);
  
  digitalWrite(Relay02, HIGH); // attached to generator relay 2 and 3 OPEN
  rel02 = 1;                   // set rel02 value to 1
  delay(500);
 
  digitalWrite(Relay03, HIGH);  // attached to generator relay 5 and 6 OPEN
  delay(500);
  rel03 = 1;
  
  digitalWrite(Relay01, HIGH);
  delay(500);
  
  digitalWrite(Relay04, LOW);  // turn on Amp 1 
  delay(500);

  digitalWrite(Relay05, LOW);  // turn on Amp 2
  delay(500);

  digitalWrite(Relay06, LOW);  // turn on Amp 3
  delay(500);
  
    
  digitalWrite(Relay08, LOW);  // turn on lights for the night
  delay(500);
  
}

void system_stop() {
  digitalWrite(Relay04, HIGH);  // shutdown Amp 1
  delay(500);
  
  digitalWrite(Relay05, HIGH); // shutdown Amp 2
  delay(500);
  
  digitalWrite(Relay06, HIGH);  // shutdown Amp 3
  delay(500);
 
  digitalWrite(Relay01, HIGH);  // attached to generator relay 1 and 2
  delay(500);
  rel01 = 1;
  
  digitalWrite(Relay02, LOW); // attached to generator relay 2 and 3 CLOSED for shutdown
  rel02 = 0;
  delay(500);
  
  digitalWrite(Relay03, LOW);  // attached to generator relay 5 and 6 CLOSED for shutdown
  rel03 = 0;
    
  digitalWrite(Relay08, LOW);  // leave the lights alone
  delay(500);
}

The problem with this second version is, obviously, the relays blaze with complete disregard to the delays. I understand that the hardware interrupts disable the delay() function, and the Millis() function in order for it to function AS an interrupt. No problems understanding that. My problem is this:

Is there another way to implement a time delay when using a hardware interrupt? So far, all the examples I see using Millis involve blinking an LED. I’ve gone way beyond that.

Any advice is appreciated.

Don't process code in your ISR.
In your ISR set a flag then leave.
In loop() if that flag is set switch to a function and do you stuff.

Edit:
See:

Are you sensing two buttons with interrupts? Very bad idea if you use mechanical buttons because they bounce around, triggering start/stop multiple times with one push. If you want to sense two buttons, why not sense them in a loop? What other "critical" system functions are you running?

Doing things like operating relays, and sensing if it raining, is hardly something that needs to be done within a microsecond. Rework so you can do the major work outside the interrupt.

liudr:
Are you sensing two buttons with interrupts? Very bad idea if you use mechanical buttons because they bounce around, triggering start/stop multiple times with one push. If you want to sense two buttons, why not sense them in a loop? What other "critical" system functions are you running?

Hey Liudr, the critical systems functions are to detect activity when someone interacts with the robot, which will activate it, shut it down at night, detect rain, shut the cooling fan off AFTER about a minute has elapsed of the amp shut down, and to check the batteries and activate the generator in case the batteries are low.

My main problem is figuring out how to get rid of the delay() functions, as I'm not a fan of them. Millis() is something I'm struggling to implement, since most examples show Millis() for repeating things like, LED lights. That's not what I'm trying to do, since I want to run things on their own mini schedule.

Thanks Nick, that's what I'm testing out right now. I'm using the interrupts only to take values and then to pass them on to global variables.

I'll put up the revised code for review once I'm done hacking it...

I want to run things on their own mini schedule

When millis + a timing variable > a threshold you call a routine, then start it all over again.

I would have a loop and include all the checking (buttons, sensors, etc.) in the loop. To have timed loop, like check something every 10 minutes, you could do a simple timer like LarryD described. I wrote a timer class to do such simple thing as reset timer and check if timer expires but it boils down to what LarryD described.

LarryD:
When millis + a timing variable > a threshold you call a routine, then start it all over again.

Ugh.

I want to run things on their own mini schedule

Ugh.

Too much New Zealand Stoneleigh :astonished:
millis + a timing variable > a threshold >>>>>should be >>>> millis - a timing variable > a threshold
Edit
millis + a timing variable > a threshold >>>>>should be >>>> millis - a timing variable >= a threshold

//Mini Schedule
const unsigned long TaskAtime  = 500;  //Run TaskA every 1/2 second
const unsigned long TaskBtime  = 1000; //Run TaskB every 1 second
const unsigned long TaskCtime  = 2000; //Run TaskC every 2 second

unsigned long TimerA;                   //Times up Task A timer
unsigned long TimerB;                   //Times up Task B timer
unsigned long TimerC;                   //Times up Task C timer
//etc.

void setup()
{
  TimerA = millis();                      //Initailize  
  TimerB = TimerA;                        //Initialize
  TimerC = TimerA;                        //Initialize

  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  pinMode(11,OUTPUT);
}

void loop()
{
  unsigned long millisNow = millis();

  if (millisNow - TimerA >= TaskAtime)               //Edit: added =
  {
    TimerA = millis();      //Re-initialize
    TaskA();
  } 
  if (millisNow - TimerB >= TaskBtime)               //Edit: added =
  {
    TimerB = millis();      //Re-initialize
    TaskB();
  }  
  if (millisNow - TimerC >= TaskCtime)               //Edit: added =
  {
    TimerC = millis();      //Re-initialize
    TaskC();
  }  

}                   // END of loop()

//************************************************

void TaskA()
{
  digitalWrite(13,!digitalRead(13));   //Toggle pin 13
}
//***************
void TaskB()
{
  digitalWrite(12,!digitalRead(12));   //Toggle pin 12
}
//***************
void TaskC()
{
  digitalWrite(11,!digitalRead(11));   //Toggle pin 11
}

I want to run things on their own mini schedule

You really should investigate an RTOS

LarryD:
Too much New Zealand Stoneleigh :astonished:

Apparently, way too much :wink:

millis - a timing variable >= a threshold

(Other than that, it’s a good example. Thanks for posting it.)

mrburnette:

I want to run things on their own mini schedule

You really should investigate an RTOS

Got any suggestions for one that's pretty easy to understand? Something that's not ridiculously complicated, low foot print, straightforward, well documented with lots of code examples to study from.

http://forum.arduino.cc/index.php?PHPSESSID=tv2orbjfvfmhvmh2al0gtqrbn4&topic=178532.0

user fat16lib has been very active in this area
Added: FreeRTOS version 7.4.2 - Libraries - Arduino Forum

Ray