Hello all, I have a slight issue. The project I'm working on uses an Arduino Uno to interface a data logging and processing program in Python with two sensors and a valve for an experiment I'm working on.
The problem I'm having is figuring out how to turn on the valve for a certain amount of time. Ideally, the python code sends a message over serial with the duration for the valve to be opened in milliseconds. Here is the program flow that I am trying to accomplish:
-
timer1 is initialized in setup(), although it is not turned on and the number of clock cycles is not set.
-
processPiData() is called from loop() when there is new serial data available
-
processPiData() pulls out the number of ms sent from python and calls valveOpenTest(openTime) with the int value of milliseconds as the argument
-
valveOpenTest() opens the valve, does the necessary math to set OCR1A with the proper number of clock cycles, and turns on the CTC interrupt (now set to the time specified by the python code)
-
ISR(TIMER1_COMPA_vect) is called when the CTC returns a match. It closes the valve, and turns off the CTC interrupt (otherwise the valve would keep opening and closing every openTime seconds until valveOpenTest is called again).
The next time python wants to turn on the valve, it sends the duration again and the process repeats.
So, to the problem. To test, I'm just flashing the builtin led in the same place where I would be actuating the valve. As it stands, I can accomplish all of the above except turn off the interrupt at the end of the ISR. If I don't turn off the timer interrupt, the led keeps flashing with the period specified by openTime, but as soon as I add the line to disable further interrupts the led doesn't even flash anymore.
I'll stop talking and show the relevant code:
void setup() {
pinMode(LED_PIN, OUTPUT); // open LED for visual feedback
Serial.begin(115200); // initialize serial data stream
// Configure interrupts
cli();
//... other interrupts here...
// Setup Timer 1
TCCR1A = 0; // set settings register A to 0
TCCR1B = 0; // set settings register B to 0
TCCR1B |= (1 << WGM12); // turn on CTC mode (allows for variable overflow time)
// Set 1024 prescaler
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// TIMSK1 |= (1 << OCIE1A); // would turn on timer interrupt, but leaving that disabled
sei(); // enable global interrupts
}
void loop() {
// ... irrelevant stuff...
// Check if there is an incoming stream and if so, process it
if (Serial.available()) {
// Serial.print("data available\n");
processPiData();
}
}
boolean processPiData( void ) {
\\... heavily excerpted because there is much confusing stuff going on
\\ checks if string in array matches keyword. if so, the next value in the char array is openTime
\\ and thus we call the valve function with that argumentassigns it as an int
if ((strcmp(strptr[numWords], "VAL") == 0)) {
long openTime = strtol(token, &ptr, 10);
if (openTime) {
valveOpenTest(openTime);
\\Serial.print("got ms\n");
}
}
void valveOpenTest(int ms) {
Serial.print("valveOpen\n");
digitalWriteFast(LED_PIN, !digitalReadFast(LED_PIN)); // cycle LED
cli();
ms = ms / 1000; // convert ms to s
OCR1A = ms * 15625 - 1; // set the number of clock cycles
TIMSK1 |= (1 << OCIE1A); // turn on CTC interrupts
sei();
}
And finally,
ISR(TIMER1_COMPA_vect) {
digitalWriteFast(LED_PIN, !digitalReadFast(LED_PIN));
TIMSK1 &= ~(1 << OCIE1A); // turn off CTC interrupts
}
So, it almost works perfectly. If I comment out that last line, the led starts to flash after the initial message from python, and continues to flash at intervals of 2000ms (that's what I sent from Python) for the rest of the time.
Of course, I want it to flash just once (after the message was sent from python). Ideally, that last line would accomplish this, as it would turn off the timer until I called it again, but once I add that line the led doesn't flash at all.
It's weird, because due to strategically placed print statements, I can see that the program hits valveOpenTest() every time it's called from processPiData(), and when the last line is commented I can see that the ISR gets called every 2000 ms after each timer overflow. However, when I add that line, valveOpenTest() and the ISR only get called once for each new message python sends, which is exactly what should happen. Yet, the led doesn't flash.
Also interesting is the order in which things are called. Without the interrupt turned off (last line is commented), valveOpen() gets called, flashes the led, finishes, and goes back to processPiData(). Then 2000ms later, I see that the ISR was called. This is how it should be; of course the ISR is still called every 2000ms and that is undesirable. However, with the last line included, valveOpen() finishes, the ISR gets called, and then the program returns back to processPiData(). This happens every time a new message is sent from python, and that is of course not the order in which things are supposed to flow (the ISR should always get called 2000ms after valveOpen()).
I've tried this with digitalWrite() as well, but that doesn't work either.
So, does anyone have any helpful hints/suggestions/solutions for this issue? And yes, I'm fully aware that I could do some millis() interval checking, but if I wanted to do that I would have saved the time typing this post and finished my project by now. I'll use that method if there is no other fix.
Thanks in advance.