Hey everyone, I'm relatively new to the forums so go easy on my first post.
Project Background
I'm having a bit of unexpected behavior with a sketch I wrote to control two relays (one for a pump and one for two lights). I am using an Uno R3 and have it connected to an opto-isolated double relay board. (found here Opto-Isolated 2 Channel Relay Board)
The goal is every twelve hours the light relay will switch, and every hour (offset by 30 seconds so the two loops never simultaneously evaluate) the pump will turn on for 20 seconds.
**The problem [/u] The pump relay accurately turns on and off... the Lighting cycle does not. If I drop the cycle time for the lights down to 40 seconds (just an arbitrarily low number for testing) the lights will turn off and on every time... but when I bump the cycle up to 12 hours the lights do not flip.
[u]**My code [/u][/u] ```
[u][u]#include <time.h>
#define sbi(port,bit) (port)|=(1<<(bit))
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
Serial.begin(9600);
Serial.println("Start... Light ON and pump on");
delay(1000);
}
void loop()
{
if(millis()%43200000 == 0) //flip at 9:00AM and 9PM, 43200000= 12 hours
{
sbi(PINB,5); //flip pin 13
delay(100); //delay so that the flip only takes place once
Serial.println("flip");
}
if(millis()%3600000 == 30000)//turn on every hour, 3600000 = one hour
{
digitalWrite(12, HIGH);
Serial.println("PUMP ON");
delay(20000);
digitalWrite(12, LOW);
Serial.println("PUMP OFF");
}
}[/u][/u] ``` **My thoughts **[/u] I doubt rollover is an issue as millis() is supposed to go a full 50 days, and I think the delay in the first if statement should stop the flip from happening more than once. Maybe this has something to do with the loop not properly evaluating because the arithmetic takes to long and the if condition never evaluates.... or maybe it has something to do with how millis() only increments with the clock prescaler so somehow my time is being missed? Any help would be much appreciated... this seems like such a simple problem and it's got me stumped!
millis() can increment by 2, causing it to miss some specific values. Also, you have some longish calls to delay() that could cause those particular milliseconds to be missed, and any other code that exceeds a millisecond of runtime could mean that that specific values as passed when you're not looking.
So it's generally a bad idea to compare millis() to exact values; you want to check whether it's greater than your "action" time, or "near." You could do something like this:
if(millis()%43200000 <= 60000) { // within a minute of the 12 hour time?
if (halfdayActionNeeded) {
halfdayActionNeeded = false;
// flip lights.
}
} else {
// outside the 12hour window, so we'll need to do it again,
// next time it rolls around.
halfdayActionNeeded = true;
}
westfw:
millis() can increment by 2, causing it to miss some specific values. Also, you have some longish calls to delay() that could cause those particular milliseconds to be missed, and any other code that exceeds a millisecond of runtime could mean that that specific values as passed when you're not looking.
Awesome, I expected something like that but wasnt sure. The bool indicator is a good tip, i'll try it out and let you know if it works
CrossRoads:
Why not just count the hours, and at 9 and 12 or whatever make stuff switch?
I thought about this but wanted to try something with less variables. but now that i've added bool variables as well I'd imagine your loop requires the same amount of memory. I'll try that if the bool variable doesnt work
Delta_G:
Or you could stop trying to re-invent the wheel, take a look at the "Blink Without Delay" example code, and write some code that will be easy and work.
true, but sometimes it's useful to re-invent the wheel. Give a man a fish he eats for a day, teach a man to fish he eats for a lifetime. I like getting into the nitty gritty, finding out exactly why I was wrong and what to do to fix it (some say stubborn).
The modulo stuff is just going to be terrible to try to make work reliably. If you use the suggestion above then the thing will constantly be toggling the lights until you go outside that 1 minute window.
This doesnt seem correct. As soon as the millis() inequality is evaluated the boolean will be flipped and the loop will not execute again. I just tested it and I believe the loop to flip the pin only occurs once. Maybe my logic is faulty
Well sure enough the loop with the boolean value entirely skipped the flip, I tried it using the same method as blinkwithoutdelay.
my code for the revision is below (worked for 30 minute trial,testing one hour trial now), but first could you help me understand why the previous modulus division did not behave as we expected?
Thanks
#include <time.h>
long lastlight_time = 0;
long lastpump_time = -25000;
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
digitalWrite(13, LOW);
digitalWrite(12, LOW);
Serial.begin(9600);
Serial.println("Start... Light ON and pump OFF");
}
void loop()
{
if (millis()-lastlight_time >= 1800000)//43200000)
{
lastlight_time = millis();
digitalWrite(13, !digitalRead(13));
delay(100);
Serial.println("flip");
}
if (millis()-lastpump_time >= 1800000)
{
lastpump_time = millis();
digitalWrite(12, HIGH);
Serial.println("PUMP ON");
delay(10000); //CANOT BE LARGER THAN 25
digitalWrite(12, LOW);
Serial.println("PUMP OFF");
}
}
I have changed 30 s for 40s because the constants are simpler.
#include <SM.h>
SM light(nightTime);
const int lightPin = 12;
SM pump(wait40s);
const int pumpPin = 13;
//some time constants
const unsigned long H12 = 12*60*60*1000;
const unsigned long M59 = 59*60*1000;
const unsigned long S40 = 40*1000;
const unsigned long S20 = 20*1000;
void setup(){
Serial.begin(115200);
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
}//setup()
void loop(){
EXEC(light);
EXEC(pump);
}//()
State dayTime(){
digitalWrite(lightPin, 0);//light off during daytime
if(light.Timeout(H12)){
light.Set(nightTime);
pump.Set(wait40s);//resync pump
}
}//dayTime()
State nightTime(){
digitalWrite(lightPin, 1);//light on during night time
if(light.Timeout(H12)) light.Set(dayTime);
}//nightTime()
State wait40s(){
if(pump.Timeout(S40)) pump.Set(on20s);
}//wait40s()
State on20s(){
digitalWrite(pumpPin, 1);
if(pump.Timeout(S20)) pump.Set(wait59m);
}//on20s()
State wait59m(){
digitalWrite(pumpPin, 0);
if(pump.Timeout(M59)) pump.Set(wait40s);
}//wait59m()
Ahhh, found my problem, I left the computer plugged in to the serial monitor and it appears about one in every 10 times the pump turning on resets the arduino.
This is an opto-isolated relay but I guess that's not quite enough. I will get a capacitor and a resistor to bridge the relay leads with... and if that doesn't work ill try that in series with a flyback diode, and if that doesn't work I could try a Transient voltage suppressor... but I have zero experience with those
Does anyone with solid E&M understanding/ relay understanding know why the inrush current across the relay coil would reset the arduino? Is some sort of voltage transient spiking back into the micro?