1

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

2

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;
            }
        }

    }
}
3
  • (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 the while (in the if (currentState < 4) block) works ok, it functions as an if statement and IMO should be written as such. Commented Jan 18, 2016 at 18:12
  • 1
    if(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 You
    – Lokanath
    Commented 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 as switch/case). In this case, however, this could be simplified. The while, however, is much more secure than the if. Since previousMillis is not reset to millis() 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 it
    – frarugi87
    Commented Jan 19, 2016 at 8:51
1

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.

-1

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.

5
  • i am now using TimerOne ,,,it however is not the best method and not very safe
    – Lokanath
    Commented Jan 19, 2016 at 6:32
  • @Lokanath I'm curious. Why do you say that?
    – CamK
    Commented 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 unpredictable
    – Lokanath
    Commented 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
    Commented 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 them
    – Lokanath
    Commented Jan 21, 2016 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.