I'm trying to use the Timer1 library to make setting an interrupt after a set period (5 sec) but only after the LED has stopped fading up to the specified brightness. However, what I'm observing is that after fading up it immediately fades back down which would indicate it hits the ISR right away instead of after 5 secs. I've tried various Timer1 functions like stop() / start() to try and make this work, but no luck. Is the LEDFader library interfering with Timer1? Any help would be appreciated.
#include <LEDFader.h> // Don't forget to include the LEDFader libraries folder
#include <Curve.h>
#include <TimerOne.h>
#define RED_LED_PIN 6 // Main Red LED output pin
#define FADE_UP_TIME 1750 // Time to power up (power first turned on)
#define MED_BRIGHTNESS 100 // Med brightness of red LED (armed state)
#define COOL_DWN_DURATION 900 // Time to cool down torps after firing
unsigned long pwrDownTimeOut = 5000; // in milliseconds (i.e. 5 sec)
LEDFader red_led;
#define INT_BTN1_PIN 2 // external interrupt pin for button #1
// State variables
// ===============
volatile boolean readyPhotonTorps = false;
volatile boolean inWarmUp = false;
void setup() {
// Setup red LED; use exponential curve for the fading as it gives a better look
// when fading up to firing point (not so good at smaller duty cycle values,
// so try commenting out the red_led.set_curve(&Curve::exponential) line to see what you prefer.
// Alternatively, use a different gamma correction table as discussed above.
red_led = LEDFader(RED_LED_PIN);
red_led.set_curve(&Curve::exponential);
// Setup 1st button on external interrupt pin 2
pinMode(INT_BTN1_PIN, INPUT);
// Attach interrupt service routine (ISR) "btn1_pushed" to pin 2 using RISING (i.e. when button is released)
attachInterrupt(digitalPinToInterrupt(INT_BTN1_PIN), btn1_pushed, RISING);
//attachInterrupt(digitalPinToInterrupt(INT_BTN1_PIN), btn1_pushed, FALLING);
// Initialize Timer1
Timer1.initialize(5000000);
Timer1.attachInterrupt(firingSeqTimeout);
Timer1.stop();
}
// Interrupt Service Routines (ISR)
// ================================
void btn1_pushed() {
if (readyPhotonTorps == false) {
// Start fade up period to medium brightness
red_led.fade(MED_BRIGHTNESS, FADE_UP_TIME);
readyPhotonTorps = true; // Set flag to ignore this button until cooled down
inWarmUp = true; // Set flag indicating warm up period has begun
}
}
void firingSeqTimeout(void) {
// Fade down RED LED to off and reset all variables
red_led.fade(0, FADE_UP_TIME);
readyPhotonTorps = false;
inWarmUp = false;
Timer1.stop();
}
// Main routine
// ============
void loop() {
red_led.update();
unsigned long currentMillis = millis();
// (1) Photon torpedoes armed and ready to fire - hold this state of
// the RED LED till final firing sequence (or timeout/power down)
if (inWarmUp == true && !red_led.is_fading()) {
inWarmUp = false;
Timer1.restart();
}
}
Thanks cattledog. I made the changes to TimerOne.h:
//****************************
// Run Control
//****************************
void start() __attribute__((always_inline)) {
TCCR1B = 0;
//TCNT1 = 0; // TODO: does this cause an undesired interrupt?
TCNT1 = 1;
resume();
}
But I still get the same results. I thought maybe it wasn't doing a full compile after the header file change so I switched the board in the IDE to Mega, did a Verify, then switched back to Uno and uploaded to the board. No change.
Don't struggle with this. For a 5 second period for a delayed action, just use a millis() timer.
I actually think that neither the button interrupt nor the timer interrupt are needed. Calling the .fade() functions from within ISR's does not seem appropriate.
I'm doing a class at a model contest show later this month on Arduino programming but specifically on interrupts. I wanted to show how they could be used, knowing that there are other ways of doing this.
I moved all the .fade() fn calls to the main loop but still see the same issue:
#include <LEDFader.h> // Don't forget to include the LEDFader libraries folder
#include <Curve.h>
#include <TimerOne.h>
#define RED_LED_PIN 6 // Main Red LED output pin
#define FADE_UP_TIME 1750 // Time to power up (power first turned on)
#define MED_BRIGHTNESS 100 // Med brightness of red LED (armed state)
#define COOL_DWN_DURATION 900 // Time to cool down torps after firing
unsigned long pwrDownTimeOut = 5000; // in milliseconds (i.e. 5 sec)
LEDFader red_led;
#define INT_BTN1_PIN 2 // external interrupt pin for button #1
// State variables
// ===============
volatile boolean readyPhotonTorps = false;
volatile boolean inWarmUp = false;
volatile boolean startCoolDown = false;
void setup() {
red_led = LEDFader(RED_LED_PIN);
red_led.set_curve(&Curve::exponential);
// Setup 1st button on external interrupt pin 2
pinMode(INT_BTN1_PIN, INPUT);
// Attach interrupt service routine (ISR) "btn1_pushed" to pin 2 using RISING (i.e. when button is released)
attachInterrupt(digitalPinToInterrupt(INT_BTN1_PIN), btn1_pushed, RISING);
// Initialize Timer1
Timer1.initialize(5000000);
Timer1.attachInterrupt(firingSeqTimeout);
Timer1.stop();
}
// Interrupt Service Routines (ISR)
// ================================
void btn1_pushed() {
if (readyPhotonTorps == false) {
readyPhotonTorps = true; // Set flag to ignore this button until cooled down
inWarmUp = true; // Set flag indicating warm up period has begun
}
}
void firingSeqTimeout(void) {
startCoolDown = true;
}
// Main routine
// ============
void loop() {
red_led.update();
unsigned long currentMillis = millis();
if (readyPhotonTorps == true && inWarmUp == true) {
// Start fade up period to medium brightness
red_led.fade(MED_BRIGHTNESS, FADE_UP_TIME);
}
if (inWarmUp == true && !red_led.is_fading()) {
inWarmUp = false;
Timer1.restart();
}
if (startCoolDown == true) {
// Fade down RED LED to off and reset all variables
red_led.fade(0, FADE_UP_TIME);
startCoolDown = false;
readyPhotonTorps = false;
inWarmUp = false;
Timer1.stop();
}
}
I'm not sure what you did with the Timer1 library and implementing the change but I can see the effect of setting TCNT1 = 1 to achieve the 5 second timer period with the Timer1 library.
I do not understand the behaviour of LEDFader.h, and I don't see a fade down trying to use the led on pin 13, but I can see the timer period.
EDIT:
When I put the output on a PWM pin, I can see the led fade up and down with a 5 second hold at max brightness
See the values produced shown in these debug print outs with and without the library change.
if (startCoolDown == true) {
Serial.print("Timer1end ");
Serial.println(millis());
// Fade down RED LED to off and reset all variables
red_led.fade(0, FADE_UP_TIME);
startCoolDown = false;
readyPhotonTorps = false;
inWarmUp = false;
Timer1.stop();
}
Is it possible your source code for TimerOne is different than mine? I downloaded the latest from GitHub and the TimerOne.h file did not have the comment you see (I copied and pasted what you had so that's why it shows up in my post) [I'd attach the files to this post but I don't see that option].
I'll try your code tonight and see what I get. But I'm confused about your statement re: pin 13 - I don't use the built-in LED in my code so not sure why you mentioned it. And the LEDFader library allows for easier PWM control that is non-blocking. Works great, except when also using TimerOne.
Is it possible your source code for TimerOne is different than mine?
I am running the code from github. The added comment about TCNT1 = 1, may have been in my file from previous work or it may have been in the original Jesse Tane code.
void start() __attribute__((always_inline)) {
TCCR1B = 0;
TCNT1 = 0; // TODO: does this cause an undesired interrupt?
resume();
}
But I'm confused about your statement re: pin 13 - I don't use the built-in LED in my code so not sure why you mentioned it
Yeah, sorry for that confusion. I was trying to test your code and was too lazy to set up an led so I tried to use the built in one. When I set up an led and used your pins, I could see the fade.
In regards to the interrupt button to start the process I used INPUT_PULLUP for the pin, but otherwise I ran your code as posted with the library change to TCNT1 = 1 and I can see the hold at max brightness.
I'm still getting the same result - the LED fades up but immediately fades down. Here's my serial output:
Timer1start 6880
Timer1end 6880
And my full code:
#include <LEDFader.h> // Don't forget to include the LEDFader libraries folder
#include <Curve.h>
#include <TimerOne.h>
#define RED_LED_PIN 5 // Main Red LED output pin
#define FADE_UP_TIME 1750 // Time to power up (power first turned on)
#define MED_BRIGHTNESS 100 // Med brightness of red LED (armed state)
#define COOL_DWN_DURATION 900 // Time to cool down torps after firing
unsigned long pwrDownTimeOut = 5000; // in milliseconds (i.e. 5 sec)
LEDFader red_led;
#define INT_BTN1_PIN 2 // external interrupt pin for button #1
// State variables
// ===============
volatile boolean readyPhotonTorps = false;
volatile boolean inWarmUp = false;
volatile boolean startCoolDown = false;
void setup() {
red_led = LEDFader(RED_LED_PIN);
red_led.set_curve(&Curve::exponential);
// Setup 1st button on external interrupt pin 2
pinMode(INT_BTN1_PIN, INPUT_PULLUP);
// Attach interrupt service routine (ISR) "btn1_pushed" to pin 2 using RISING (i.e. when button is released)
attachInterrupt(digitalPinToInterrupt(INT_BTN1_PIN), btn1_pushed, RISING);
// Initialize Timer1
Timer1.initialize(5000000);
Timer1.attachInterrupt(firingSeqTimeout);
Timer1.stop();
Serial.begin(9600);
}
// Interrupt Service Routines (ISR)
// ================================
void btn1_pushed() {
if (readyPhotonTorps == false) {
readyPhotonTorps = true; // Set flag to ignore this button until cooled down
inWarmUp = true; // Set flag indicating warm up period has begun
}
}
void firingSeqTimeout(void) {
startCoolDown = true;
}
// Main routine
// ============
void loop() {
red_led.update();
unsigned long currentMillis = millis();
if (readyPhotonTorps == true && inWarmUp == true) {
// Start fade up period to medium brightness
red_led.fade(MED_BRIGHTNESS, FADE_UP_TIME);
}
if (inWarmUp == true && !red_led.is_fading()) {
inWarmUp = false;
Timer1.restart();
Serial.print("Timer1start ");
Serial.println(millis());
}
if (startCoolDown == true) {
Serial.print("Timer1end ");
Serial.println(millis());
// Fade down RED LED to off and reset all variables
red_led.fade(0, FADE_UP_TIME);
startCoolDown = false;
readyPhotonTorps = false;
inWarmUp = false;
Timer1.stop();
}
}
If I use pins 9 or 10 it doesn't fade/PWM - it just blinks. But all other PWM pins yield the same result.
I switched the momentary button to use INPUT_PULLUP and wired it appropriately. I don't think the button or it's interrupt on pin 2 makes a difference here, but I wanted it to be as close to what you had as I could.
I run your code as posted with no changes. I can see the immediate fade and no 5 second hold. When I change the library to use TCNT1=1 I can see the hold period before the fade.
I access the library in a text editor(Notepad++) and make the change. Then I save the changed file. Then I reload the code.
I believe the issue must be with your procedure for changing the library, saving the change and then reloading the code. You can not just press reset, it requires a new download.
Be sure you change the part of the library for the run control of the avr architecture.
Please explain exactly what you are doing.
This issue is also documented as a github issue for the library
I updated TimerOne.h at line 130 under "RUN CONTROL":
//****************************
// Run Control
//****************************
void start() __attribute__((always_inline)) {
TCCR1B = 0;
//TCNT1 = 0; // TODO: does this cause an undesired interrupt?
TCNT1 = 1;
resume();
}
I then switched the board to a Mega, ran Verify, then switched back to Uno and uploaded it.
But now I see a 2nd RUN CONTROL section around line 223, which does have the comment you had posted:
//****************************
// Run Control
//****************************
void start() __attribute__((always_inline)) {
TCCR1B = 0;
TCNT1 = 0; // TODO: does this cause an undesired interrupt?
resume();
}
Let me change that one and test.
EDIT:
Success! Turns out there are 4 RUN CONTROL sections and it was the 2nd one that needed updating. Now I get:
Fixing a library issue to use a timer interrupt in a demo of interrupt usage is not going to be useful for your audience. I would think that a demo of RPM measerement or velocity with encoder or beam break sensor is far better than using interrupts to react to a button press and replace a millis() timer.
This is for scale model builders, not coders. And the working example using a kit build-up is for firing photon torpedoes: the first button arms the torps (fades up LED to medium brightness), then the second button triggers the firing sequence (fade up to max brightness, then fade down to medium). If the LED sits at medium brightness for more than 5 secs, then fade down. I already have a working example just using millis() but want to show what you can do with interrupts.