I am doing a project which needs to meet the following specifications.
On receiving an SMS arduino will start switching 1st relay on after some time duration(say 30 Seconds) relay 1 will be turned off and it will now turn on 2nd relay for (say 20seconds) so on 1>2>3>4.
The problem im facing is managing the time intervals between the switching process,i did think of using millis() i.e (currentMillis-previousMillisx>Intervalx) as in blinkWithoutDelay ,how ever this will work only once as i dont not know when will the next SMS come to the system ,if the user decides to send the message after 1hour currentMillis-previousMillisx will always return true so i cannot keep the motor on for (Say 30 seconds) and same will happen with all 4 cases.
If there is any alternative logic for this problem please suggest me or can i just add RTC for my project to manage time easily
Thank You
3 Answers 3
Well, you need a sort of tracking for your active relay, don't you? So you just have to reset that tracking variable and the previousMillis variables and you are done.
Some code (note: untested and moreover I don't know how you are receiving the SMS)
// The intervals for the different relays
const byte Intervals[] = {30, 20, 10, 5};
// The current state. 0 means relay 1 on,
// 1 means relay 2 on, 2->3 and 3->4, while
// 4 means no relay activated
byte currentState = 4;
unsigned long previousMillis;
void loop()
{
if (received_sms)
{
currentState = 0;
previousMillis = millis();
digitalWrite(RELAY1, HIGH);
// Maybe turn off all the other relays?
}
if (currentState < 4)
{ // Active state: check if we should advance
unsigned long currentInterval = ((unsigned long)Intervals[currentState])*1000;
while ((millis() - previousMillis) >= currentInterval)
{ // Advance to the next step
switch(currentState)
{
case 0: // Advance from state 0 to 1
digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, HIGH);
// Maybe turn off all the other relays?
previousMillis += currentInterval;
currentState = 1;
break;
...
// cases 1, 2, 3
...
default:
// It should never reach this
previousMillis = millis();
currentState = 4;
break;
}
}
}
}
-
(a) Using a
switch
/case
statement to select which relay to work on, with four blocks of nearly identical code (instead of using an array with pin numbers, and a single block of code) is odious. (b) Although thewhile
(in theif (currentState < 4)
block) works ok, it functions as anif
statement and IMO should be written as such.James Waldby - jwpat7– James Waldby - jwpat72016年01月18日 18:12:27 +00:00Commented Jan 18, 2016 at 18:12 -
1if(received_sms){} block made my day " previousMillis=millis()" inside that will now allow me to get the offset from the point i recieved the SMS ,,Thanks YouLokanath– Lokanath2016年01月19日 06:37:15 +00:00Commented Jan 19, 2016 at 6:37
-
@jwpat7 I agree that if the code is nearly identical the
switch/case
can be rewritten in a more concise form, but often (almost always) you have to do something else in the cases (and that's why when dealing with simple - and complex - state machines my mind always implements it asswitch/case
). In this case, however, this could be simplified. The while, however, is much more secure than the if. SincepreviousMillis
is not reset tomillis()
but just incremented (more precise), if some function blocks the thread for too much time you can lose one step; with the while you can recover itfrarugi87– frarugi872016年01月19日 08:51:47 +00:00Commented Jan 19, 2016 at 8:51
I do not really understand your problem. More specifically,
"currentMillis-previousMillisx will always return true" does not make
sense, as it is not a boolean expression (it's unsigned long
).
Anyway, the technique shown in blinkWithoutDelay is the way to go. Here is my (untested) version:
const int relay_count = 4; // we have 4 relays
const int relay_pins[relay_count] = {2, 3, 4, 5};
const unsigned long durations[relay_count] = {30000, 20000, 15000, 10000};
bool sms_received(); // you implement this one
void loop()
{
static int current_relay = -1; // meaning: no active relay
static unsigned long last_switch;
unsigned long now = millis();
// If no relay is active, wait for an SMS.
if (current_relay == -1) {
if (sms_received()) {
// Switch on first relay.
current_relay = 0;
digitalWrite(relay_pins[current_relay], HIGH);
last_switch = now;
}
}
// Otherwise, switch relays when it is time to do so.
else if (now - last_switch >= durations[current_relay]) {
digitalWrite(relay_pins[current_relay], LOW);
if (++current_relay == relay_count) {
current_relay = -1; // no active relay
} else {
digitalWrite(relay_pins[current_relay], HIGH);
last_switch = now;
}
}
}
BTW, you did not specify what should happen if you receive an SMS while one of the relays is active. I assume the SMS are only acknowledged when no relay is active. Otherwise you would have to change the code.
You could use timer interrupts. I've (successfully) used the TimerOne library. There are countless other libraries that allow you to use timer interrupts easily. "Start" the timer when you receive the SMS message. You could use a pair of boolean flags to keep track of the two relays with one timer.
-
i am now using TimerOne ,,,it however is not the best method and not very safeLokanath– Lokanath2016年01月19日 06:32:18 +00:00Commented Jan 19, 2016 at 6:32
-
@Lokanath I'm curious. Why do you say that?CamK– CamK2016年01月19日 13:05:14 +00:00Commented Jan 19, 2016 at 13:05
-
Interrupts are good but how ever you cannot pass values to the ISR,as far as i know ISR will execute when the timerExpires and uses the values of variables current values at the expiry point inside ISR which i totally cannot control which makes program unpredictableLokanath– Lokanath2016年01月20日 06:47:33 +00:00Commented Jan 20, 2016 at 6:47
-
@Lokanath You could use global variables and declare them as 'volatile'. This tells the compiler to reload the variable every time you reference it as opposed to using a copy from a register.CamK– CamK2016年01月20日 13:29:23 +00:00Commented Jan 20, 2016 at 13:29
-
ya you are right,,,usage of interrupts needs more than beginners knowledge,,,if you know how to use it you can manage themLokanath– Lokanath2016年01月21日 10:53:20 +00:00Commented Jan 21, 2016 at 10:53