Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

write timer without delay()

I wrote this code to send an SMS if an input is HIGH as you can see in it,but the problem is I have 4 inputs and delay()s are fatal and very wrong if I need to do more than one single thing at a time (I use 4 inputs).

So I need to change delay() with millis() or something else in void loop() ,Send_SMS() and initia().

can someone help me,and thank you in advance.

const int DI = 2;
const int DT = 3;
const int DGP1 = 4;
const int DGP2 = 5;

int value1_old = 0;
int value2_old = 0;
int value3_old = 0;
int value4_old = 0;

unsigned long previousMillis = 0;
unsigned long interval=100;

#include<SoftwareSerial.h>
SoftwareSerial SIM900 (7, 8);

void setup() {
    pinMode(DI, INPUT);
    pinMode(DT, INPUT);
    pinMode(DGP1, INPUT);
    pinMode(DGP2, INPUT);

    SIM900.begin(19200);
    SIM900power();
    delay(20000);

}

void SIM900power(){
    digitalWrite(9 ,HIGH);
    delay(1000);
    digitalWrite(9 ,LOW);
    delay(5000);
}

void initia(){
    SIM900.print("AT+CMGF=1\r");
    delay(100);
    SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
    delay(100);
}

void Send_SMS(){
    SIM900.println((char)26);
    delay(100);
    SIM900.println();
    delay(100);
    SIM900power();
}

void loop() {
    int value1 = digitalRead (DI);
    int value2 = digitalRead (DT);
    int value3 = digitalRead (DGP1);
    int value4 = digitalRead (DGP2);

    if (value2 != value2_old && value2 == HIGH) {
        initia();
        SIM900.println("Station 85: Defaut electrique");
        delay(100);
        Send_SMS();
        value2_old = value2;
    }

    if (value3 != value3_old && value3 == HIGH)
    {
        initia();
        SIM900.println("Station 85: DefautGP1");
        delay(100);
        Send_SMS();
        value3_old = value3;
    }

    if (value4 != value4_old && value4 == HIGH)
    {
        initia();
        SIM900.println("Station 85:DD>1000");
        delay(100);
        Send_SMS();
        value4_old = value4;
    }
    value2_old = value2;
    value3_old = value3;
    value4_old = value4;
}
like image 387
Tariq Talbi Avatar asked Apr 18 '15 11:04

Tariq Talbi


People also ask

What can I use instead of a delay in Arduino?

Instead of using delay(), you can employ millis() in your sketch and check how much time has elapsed since the Arduino last executed some code.

Why function delay () is not recommended to be used in the sketch?

More knowledgeable programmers usually avoid the use of delay() for timing of events longer than 10's of milliseconds unless the Arduino sketch is very simple. Certain things do go on while the delay() function is controlling the Atmega chip, however, because the delay function does not disable interrupts.

What is delay () in Arduino?

Arduino - delay () function This number represents the time (measured in milliseconds). The program should wait until moving on to the next line of code when it encounters this function.

What is blink without delay in Arduino?

It turns the LED on and then makes note of the time. Then, each time through loop() , it checks to see if the desired blink time has passed. If it has, it toggles the LED on or off and makes note of the new time. In this way the LED blinks continuously while the sketch execution never lags on a single instruction.


2 Answers

As mclopez pointed out, is better to provide a good answer rather than pointing out only what are the flaws of the other ones, so... Here we go.

In my opinion the ISR solution is not a good choice, because ISRs block the normal execution. The only feasible implementation using interrupts is to record the pin change and then send the SMSs.

This, however, is not a good solution in my opinion. For this problem, I'd go with a state machine to send the SMSs; since you have states, you can wait the transition while doing other things, like checking the buttons.

I do not know how SIM900 sends the SMS, so I'm taking your workflow and implementing it in a state machine. I'm not sure this is the optimal solution, particularly because I don't think you actually need to reboot the module each time, but you can trim it later.

Now, the workflow has seven actions you have to perform, each followed by a wait. I write it here so it's simpler to see every action:

SIM900.print("AT+CMGF=1\r");
delay(100);
SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
delay(100);  
SIM900.println("Message you want");
delay(100);  
SIM900.println((char)26);
delay(100);
SIM900.println();
delay(100);
digitalWrite(9 ,HIGH);
delay(1000);
digitalWrite(9 ,LOW);
delay(5000);

So we have eight states: an idle one (when you are waiting for the state machine to start, named SIM_IDLE), followed by five "SEND" states (I name them SIM_SEND1..5) and two states to reset the power (named SIM_POW1 and SIM_POW2). You are normally in idle state; when one or more buttons are pressed, you switch to the first send, cycle through them and then reset the power and go back to idle.

The pressed button changes only state SIM_SEND3 (when you actually send the message), so whenever a button press is detected a boolean variable is set (a button press is detected also during the state machine execution) and is reset only in that state, after the correct message is sent.

Now, here is the code which implements this:

const uint8_t DI = 2;
const uint8_t DT = 3;
const uint8_t DGP1 = 4;
const uint8_t DGP2 = 5;

const uint8_t SIMPOW = 9;

uint8_t value1_old = 0;
uint8_t value2_old = 0;
uint8_t value3_old = 0;
uint8_t value4_old = 0;

boolean value1_changed = false;
boolean value2_changed = false;
boolean value3_changed = false;
boolean value4_changed = false;

/********************************/
//         SIM STATES
#define SIM_IDLE   0
//SEND1: SIM900.print("AT+CMGF=1\r");delay(100);
#define SIM_SEND1  1
//SEND2: SIM900.println("AT + CMGS = \"+212xxxxxxx\"");delay(100);
#define SIM_SEND2  2
//SEND3: SIM900.println("Message you want");delay(100);
#define SIM_SEND3  3
//SEND4: SIM900.println((char)26);delay(100);
#define SIM_SEND4  4
//SEND5: SIM900.println();delay(100);
#define SIM_SEND5  5
//POW1: digitalWrite(SIMPOW,HIGH);delay(1000);
#define SIM_POW1   6
//POW2: digitalWrite(SIMPOW,LOW);delay(5000);
#define SIM_POW2   7
/********************************/

unsigned long previousMillis;
uint8_t currentSimState;

#include<SoftwareSerial.h>
SoftwareSerial SIM900 (7, 8);

void setup()
{
    pinMode(DI, INPUT);
    pinMode(DT, INPUT);
    pinMode(DGP1, INPUT);
    pinMode(DGP2, INPUT);
    pinMode(SIMPOW, OUTPUT);

    SIM900.begin(19200);

    digitalWrite(SIMPOW,HIGH);
    delay(1000);
    digitalWrite(SIMPOW,LOW);
    delay(25000);
    currentSimState = -1; // Force a state transition
}

void loop()
{
    uint8_t value1 = digitalRead (DI);
    uint8_t value2 = digitalRead (DT);
    uint8_t value3 = digitalRead (DGP1);
    uint8_t value4 = digitalRead (DGP2);

    unsigned long currentMillis = millis();

    if (value2 != value2_old && value2 == HIGH)
        value2_changed = true;
    if (value3 != value3_old && value3 == HIGH)
        value3_changed = true;
    if (value4 != value4_old && value4 == HIGH)
        value4_changed = true;

    value1_old = value1;
    value2_old = value2;
    value3_old = value3;
    value4_old = value4;

    // Check if a state transition is needed
    uint8_t newSimState = currentSimState;
    switch (currentSimState)
    {
    case SIM_IDLE: // Start sending if a value changed
        if ((value2_changed) || (value3_changed) || (value4_changed))
            newSimState = SIM_SEND1;
        break;
    case SIM_SEND1: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND2;
        break;
    case SIM_SEND2: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND3;
        break;
    case SIM_SEND3: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND4;
        break;
    case SIM_SEND4: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_SEND5;
        break;
    case SIM_SEND5: // Wait 100 ms
        if ((currentMillis - previousMillis) >= 100)
            newSimState = SIM_POW1;
        break;
    case SIM_POW1: // Wait 1000 ms
        if ((currentMillis - previousMillis) >= 1000)
            newSimState = SIM_POW2;
        break;
    case SIM_POW2: // Wait 1000 ms
        if ((currentMillis - previousMillis) >= 1000)
            newSimState = SIM_IDLE;
        break;
    default:
        newSimState = SIM_IDLE;
        break;
    }

    // If there was a transition, do the appropriate action
    if (newSimState != currentSimState)
    {
        case SIM_IDLE:
            // Do nothing
            break;
        case SIM_SEND1:
            SIM900.print("AT+CMGF=1\r");
            previousMillis = millis();
            break;
        case SIM_SEND2:
            SIM900.println("AT + CMGS = \"+212xxxxxxx\"");
            previousMillis = millis();
            break;
        case SIM_SEND3:
            if (value2_changed)
            {
                SIM900.println("Station 85: Defaut electrique");
                value2_changed = false;
            }
            else if (value3_changed)
            {
                SIM900.println("Station 85: DefautGP1");
                value2_changed = false;
            }
            else if (value4_changed)
            {
                SIM900.println("Station 85:DD>1000");
                value2_changed = false;
            }
            else
            {
                // Should never arrive here. Just in case, you
                // can either abort the SMS sending if you can
                // or send another message
            }
            previousMillis = millis();
            break;
        case SIM_SEND4:
            SIM900.println((char)26);
            previousMillis = millis();
            break;
        case SIM_SEND5:
            SIM900.println();
            previousMillis = millis();
            break;
        case SIM_POW1:
            digitalWrite(SIMPOW,HIGH);
            previousMillis = millis();
            break;
        case SIM_POW2:
            digitalWrite(SIMPOW,LOW);
            previousMillis = millis();
            break;
        }
    }

    // Advance state
    currentSimState = newSimState;
}

It may look complicated, but it is indeed very simple. The loop is made by three "blocks".

The first one is the button check. If any button is pressed, the corresponding valueX_changed flag is set. This is a very trivial part, just check if any button has a different state and then set the flag.

The second part is the check for a state transition. In this switch statement the program determines if the state of the state machine needs to be changed. This happens when a button was pressed if the state was idle, or if a specified amount of time has passed if in the process of sending an SMS.

The third part is the action to be performed when a state changes. So if the state changed, do the state action, which means nothing for the idle state, send something for the SIM_SENDx states and change the pin for the SIM_POWx states.

Just a note: in the setup you added a 20 seconds delay not presend in the normal workflow. If you want to remove this, you can just remove the four lines from the setup performing the reset and modify the default case in the first switch to set newSimState = SIM_POW1; instead of SIM_IDLE.

There can be small bugs in this code, since I haven't tested it, but it should do what you wanted

like image 190
frarugi87 Avatar answered Oct 06 '22 01:10

frarugi87


Use the Timer library https://playground.arduino.cc/Code/Timer/. As stated by them:

The disadvantage of the delay approach is that nothing else can go on while the delay is happening. You cannot update a display, or check for key presses for example.

So instead of delay, you can use:

 t.every(1000, doStuff);

To trigger a function while leaving the loop to do its business meanwhile.

Hope it helps.

like image 42
madaerodog Avatar answered Oct 05 '22 23:10

madaerodog