6

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;
}
Butiri Dan
  • 1,759
  • 5
  • 12
  • 18
  • Do you mean to say that by using delays you are wasting cycles waiting on things rather than doing more other potential (time critical) tasks? If so, I think you need to look towards using interrupts (to perform time-critical task in the ISR) or using the hardware timer present on the AVR itself. – initramfs Apr 18 '15 at 12:33
  • 1
    Use the millis() function instead. Tutorial example [is here](http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay). – Hans Passant Apr 18 '15 at 13:16
  • I think that I need to use millis() in the place of delay,but I don't know how,because I'm a beginner :/ – Tariq Talbi Apr 20 '15 at 15:23

5 Answers5

1

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

frarugi87
  • 2,826
  • 1
  • 20
  • 41
1

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.

Eduardo Baitello
  • 10,469
  • 7
  • 46
  • 74
madaerodog
  • 11
  • 1
0

Not exactly the answer to the original question... But for those looking for a simple solution to print seconds without using the delay():

if(millis() % 1000 == 0) {
   Serial.println(millis());
}

Simple, isn't it?

Fellipe Sanches
  • 7,395
  • 4
  • 32
  • 33
-1

The function millis() will fail when the value overflows. As @CPU_Terminator said, use interrupts. There are useful Arduino libraries for that, for example Timer1.

EDIT. Assuming that what you want to do is send an SMS each 100ms if some of your inputs changed, you can use a code like this (I removed some delays that doesn't seem necesary to me, add them again if I'm wrong):

#include <SoftwareSerial.h>
#include "TimerOne.h"

const int DI = 2;
const int DT = 3;
const int DGP1 = 4;
const int DGP2 = 5;
const long interval = 100000; // in microseconds

int value1 = 0;
int value2 = 0;
int value3 = 0;
int value4 = 0;
int value1_old = 0;
int value2_old = 0;
int value3_old = 0;
int value4_old = 0;
boolean changed1 = false;
boolean changed2 = false;
boolean changed3 = false;
boolean changed4 = false;

SoftwareSerial SIM900 (7, 8);

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

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

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

void isr_timer(){
  if (changed2) {
    initia();
    SIM900.println("Station 85: Defaut electrique");
    Send_SMS();
    changed2 = false;
  }

  if (changed3) {
    initia();
    SIM900.println("Station 85: DefautGP1");
    Send_SMS();
    changed3 = false;
  }

  if (changed4) {
    initia();
    SIM900.println("Station 85:DD>1000");
    Send_SMS();
    changed4 = false;
  }
}

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

  SIM900.begin(19200);
  SIM900power();
  delay(20000);
  Timer1.initialize(interval);
  Timer1.attachInterrupt(isr_timer);
}

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

  if (value1 != value1_old && value1 == HIGH) changed1 = true;
  if (value2 != value2_old && value2 == HIGH) changed2 = true;
  if (value3 != value3_old && value3 == HIGH) changed3 = true;
  if (value4 != value4_old && value4 == HIGH) changed4 = true;

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

  // Here the rest of your code
}

This way the function isr_timer() will be executed each 0.1 seconds.

mclopez
  • 151
  • 1
  • 11
  • Great! I got -1 and nobody tells why. – mclopez Apr 19 '15 at 11:39
  • There you have it, hope it's useful. If it's the answer you expected, accept it and voteup, please :) – mclopez Apr 20 '15 at 18:50
  • Ok,I'll check if it's ok,thak you very much ^^ – Tariq Talbi Apr 20 '15 at 22:39
  • Sorry,but not working,it can't send SMS :/ , I think that the millis() is the solution,but I don't know how to use it because I'm a beginner – Tariq Talbi Apr 21 '15 at 16:42
  • For the `millis()`, @Hans_Passant gave you a link in another answer. Read it. But if this isn't working may be because I removed some delay that was needed (I really don't know how to send SMS). Add all the delays I removed, and change `interval` to something that makes sense (a value a bit bigger that the overall time needed to SMS). – mclopez Apr 21 '15 at 18:01
  • #mclopez please check this link: http://stackoverflow.com/questions/29818894/send-sms-for-1-time-with-arduino-gprs-sim900-if-an-iput-is-high – Tariq Talbi Apr 23 '15 at 09:16
  • `millis()` will not reach its maximum value for 50 days and then when it does overflow it only starts again at 0. All you have to do is handle it correctly in a long running application... – RussDunn Apr 23 '15 at 14:13
  • I think you made two mistakes in your answer. The first is that saying that millis overflows is misleading. It does, but this is not a problem if you code properly (i.e. save the value of the last time in a unsigned long variable, then compare it to the millis output by subtraction: `if ((millis() - lastVal) > 100) { lastVal += 100; do_something(); }` This template never has problems, even at millis overflows. The second BIG error is that you are using an ISR to do heavy work. Never do this. ISRs should be kept the shortest possible, and never use peripherals (no, the serial can't be used) ... – frarugi87 Jan 03 '17 at 10:32
  • ... in ISRs, otherwise you can block the entire execution of the code or make something not work. In this context, an ISR could be used (but I think it is overkill) to detect input changes, then gather them and finally at the next loop send the appropriate SMS. But using an ISR for 100ms and then stopping the execution is not correct. – frarugi87 Jan 03 '17 at 10:40
  • @frarugi87 Yes, you are right about the use of ISRs. But this is a Q&A site, so instead of just pointing that my answer is wrong, please provide a better one :) – mclopez Jan 04 '17 at 21:09
  • @mclopez you are totally right, but I didn't have the time to write a correct program. Now I added what I think is a correct answer (or at least it should have a better behavior because it doesn't use interrupts that, well, interrupt the normal workflow) – frarugi87 Jan 05 '17 at 14:03
-1

You may try using the code in isr_timer() function:

if (changed2) {
    initia();
    SIM900.println("Station 85: Defaut electrique");
    delay(100);
    Send_SMS();
    changed2 = false;
}

Best regards

Ercan Ersoy
  • 27
  • 2
  • 10