From what I\'ve read, the solution to my problem is to use an interrupt, but if I understand them correctly, I can\'t use a delay in the routine that gets called by the inte
I'm a professional programmer, but I'm new to Arduino world. I noted that many Arduino owners don't know the fundamentals of programming, and cannot achieve good results in using this amazing piece of technology.
In particular, I want to suggest to not use Arduino interrupts if it's not necessary, because there are only two of them, and even if you can write a "beautiful" code for a single sensor, or actuator, when you have to implement a more complex project, you cannot use them. Surely you've been correct in using the interrupt to deal with a single button, but what if you have four buttons to manage?
For this reason I rewrote the "heartbeat" sketch using the technique of "time slice" or "single step". Using this technique, each time the loop function is executed, you run only a "single step" of your control functions, so you can insert as many as you need, and be equally quick and responsive to all the sensors you're using. To do so, I use global counters for each control function I have to implement, and each time the loop function is executed, I execute a single step of each function. This is the new sketch:
// Interrupt.ino - this sketch demonstrates how to implement a "virtual" interrupt using
// the technique of "single step" to avoid heavy duty cycles within the loop function.
int maxPwm = 128; // max pwm amount
int myPwm = 0; // current pwm value
int phase = 1; // current beat phase
int greenPin = 11; // output led pin
int buttonPin = 9; // input button pin
int buttonFlag = 1; // button flag for debounce
int myDir[] = {0,1,-1,1,-1}; // direction of heartbeat loop
int myDelay[] = {0,500,1000,500,3000}; // delay in microseconds of a single step
void setup()
{
pinMode(buttonPin, INPUT); // enable button pin for input
// it's not necessary to enable the analog output
}
void loop()
{
if(phase>0) heartbeat(); // if phase 1 to 4 beat, else steady
buttonRead(); // test if button has been pressed
}
// heartbeat function - each time is executed, it advances only one step
// phase 1: the led is given more and more voltage till myPwm equals to maxPwm
// phase 2: the led is given less and less voltage till myPwm equals to zero
// phase 3: the led is given more and more voltage till myPwm equals to maxPwm
// phase 4: the led is given less and less voltage till myPwm equals to zero
void heartbeat()
{
myPwm += myDir[phase];
analogWrite(greenPin, myPwm);
delayMicroseconds(myDelay[phase]);
if(myPwm==maxPwm||myPwm==0) phase = (phase%4)+1;
}
// buttonRead function - tests if the button is pressed;
// if so, forces phase 0 (no beat) and enlightens the led to the maximum pwm
// and remains in "inoperative" state till the button is released
void buttonRead()
{
if(digitalRead(buttonPin)!=buttonFlag) // if button status changes (pressed os released)
{
buttonFlag = 1 - buttonFlag; // toggle button flag value
if(buttonFlag) // if pressed, toggle between "beat" status and "steady" status
{
if(phase) myPwm = maxPwm; else myPwm = 0;
phase = phase==0;
analogWrite(greenPin, myPwm);
}
}
}
As you can see, the code is very compact and fast to execute. I divided the heartbeat loop into four "phases", regulated by the myDelay array, and whose direction of counting is regulated by the myDir array. If nothing happens, the voltage of the pwm led pin is incremented at each step until it reaches the maxPwm value, then the loop enters phase 2, in which the voltage is decremented until zero, and so on, implementing the original heartbeat.
If the button is pressed, the loop enters in phase zero (no heartbeat), and the led is alimented with maxPwm voltage. From now on, the loop maintains a steady led, until the button is released (this implements the "debounce" algorithm). If the button is pressed again, the buttonRead function restarts the heartbeat function, making it enter the phase 1 again, so the heartbeat is restored.
If you press the button again, the heartbeat stops, and so on. All without any exhitation or bouncing at all.
You are correct in most of your assumptions. The proper way to handle this is using an interrupt and it is not a good idea to have delays in your interrupt service routines (ISR). So what you want to do is set a flag in your ISR and check that flag in your main loop.
// Flag needs to be volatile if used in an ISR
volatile int buttonFlag = 0;
void loop()
{
if (buttonFlag == 0)
{
heartbeat(); //make led beat
}
else
{
analogWrite(greenPin, 255); //button stays green once pushed
functionA //has some delays in it
functionB //has some other delays
buttonFlag = 0; //clear flag after executing code
}
}
// Interrupt Service Routine attached to INT0 vector
ISR(EXT_INT0_vect)
{
buttonFlag = digitalRead(buttonPin); //set flag to value of button
}
Since the interrupt will only trigger on a change in the state of the button, you don't need to check for that.
Make sure that your flag variable is global and is declared volatile for use in the ISR. And make sure you are using the correct interrupt vector for use with the pin you are using.
Here is a good tutorial on Arduino interrupts. And here is another good example of what you're trying to do.
You may also want to look into debouncing your switch presses depending on what type of switch you're using. If instead of missing the first press, you're getting too many presses, you will need to implement some type of debouncing.