How to Use Timer/Interrupts without disturbing main prg (LED Switching+GPS)

Hi All,

Sorry i am new in the forum, kindly help me to use timer or interrupt without disturbing my main program.

I have a main program of led switching light at the same time i needs to sends the information of the GSM/GPRS+GPS every 1hour to one url so we can monitor the location of the light. But the problem is delay where i can put the delay so led switching will not be effected.

Thanks

Forumtesting.ino (6.54 KB)

Just so we don’t see the dreaded code tag police…

Here’s his code in tags.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(4, 5); // RX, TX

#define SIMCOM(x)     mySerial.println(x)
#define DEBUG(x)      Serial.println(x)

#define ECHO          1
#define GPS_PWR_ON    2
#define GPS_PWR_OFF   3
#define GPS_PKT_ON    4
#define GPS_PKT_OFF   5
#define GET_GPS       6
#define TCP_START     7
#define TCP_SEND      8
#define TCP_STOP      9

/*
   NOTES: KEEP GPS OPEN DURING STATE MACHINE PROCSS.
   OTHERWISE THE GPS WILL BE RESETTED TO RAW DATA MODE.
   RAW DATA IS NON NMEA DATA THAT VARIES FROM DEVICE TO DEVICE.

   USE STATE MACHINE AS PER REQUIREMENT.
*/

int LEDB = 2;
//int ldr= 7;

typedef struct GPS_DATA {
  char gpsBuf[200];
  int index;
  bool isFound;
} GPSData;

typedef struct DATA_ADAPTER {
  char TIME[20];
  char PACKET[2];
  char LAT[15];
  char DIR_LAT[2];
  char LNG[15];
  char DIR_LNG[2];
  char SPEEDD[15];
  char ANGLE[15];
  char DATE[15];

  float lat;
  float lng;
} DATA;

bool stateMachine = false;
int state = 0;
GPSData gpsData;
DATA os;

String TCPIP = "\"TCP\",\"72.249.151.136\",\"80\"";
String data = "";

void setup() {
  //  Serial.begin(9600);
  //  while(!Serial);

  pinMode(LEDB, OUTPUT);
  //pinMode (ldr, INPUT);
  analogReference (DEFAULT);


  mySerial.begin(9600);
  while (!mySerial);
  delay(10000);
}

void loop() {
  //Debugging...
  //while(Serial1.available()) Serial.write(Serial1.read());
  //while(Serial.available()) Serial1.write(Serial.read());


  float value = analogRead(A7);
  float TEMP = ((value * 5) / 1024);

  //  float value = analogRead(A0);
  //  float TEMP = ((value * 5) / 1024);
  //Serial.println(TEMP);

  gpsData.isFound = false;
  gpsData.index = 0;
  // stateMachine = true;
  state = ECHO;

  if (TEMP > 2.5) {
    //stateMachine = false;
    digitalWrite(LEDB, LOW);
    //state = default;
  }
  else if (TEMP < 2.5) {
    stateMachine = true;
    digitalWrite(LEDB, HIGH);
    delay(1000);
    digitalWrite(LEDB, LOW);
    //state = default;
  }

  //  gpsData.isFound = false;
  //  gpsData.index = 0;
  //  stateMachine = true;
  //  state = ECHO;


  while (stateMachine) {
    switch (state) {
      case ECHO:
        // DEBUG("ECHO OFF");
        SIMCOM("ATE0");
        delay(100);
        serialClear();
        state = GPS_PWR_ON;
        break;

      case GPS_PWR_ON:
        //DEBUG("GPS PWR ON");
        SIMCOM("AT+CGNSPWR=1");
        delay(200);
        serialClear();
        state = GPS_PKT_ON;
        break;

      case GPS_PWR_OFF:
        // DEBUG("GPS PWR OFF");
        SIMCOM("AT+CGNSPWR=0");
        delay(200);
        serialClear();
        state = 0;
        break;

      case GPS_PKT_ON:
        //DEBUG("GPS PKT ON");
        SIMCOM("AT+CGNSTST=1");
        delay(200);
        serialClear();
        state = GET_GPS;
        break;

      case GPS_PKT_OFF:
        // DEBUG("GPS PKT OFF");
        SIMCOM("AT+CGNSTST=0");
        delay(200);
        serialClear();
        state = TCP_START;                  //USE STATE HERE TO GO EITHER TO TCP COMMUNICATION OR LEAVE THE STATE MACHINE. CURRENTLY ON DEFAULT STATE.
        break;

      case GET_GPS:
        while (!gpsData.isFound) {
          if (mySerial.available() > 0) {
            char c = mySerial.read();
            //Serial.write(c);
            if (!gpsData.isFound) {
              gpsData.gpsBuf[gpsData.index++] = c;
              if (gpsData.index >= 6) {
                if (gpsData.gpsBuf[0] == '

-jim lee && gpsData.gpsBuf[1] == ‘G’ && gpsData.gpsBuf[2] == ‘P’ && gpsData.gpsBuf[3] == ‘R’ && gpsData.gpsBuf[4] == ‘M’ && gpsData.gpsBuf[5] == ‘C’) {
                  if (gpsData.gpsBuf[gpsData.index] == 0x0A) {
                    gpsData.isFound = true;
                    gpsData.gpsBuf[gpsData.index++] = 0x00;
                    gpsData.index = 0;
                  }
                } else {
                  gpsData.index = 0;
                }
              }
            }
          }
        }
        //DEBUG(“OUT OF BOX”);
        getGPS();
        // convertLatLng();
        delay(200);
        serialClear();
        gpsData.isFound = false;
        state = GPS_PKT_OFF;
        break;

case TCP_START:
        // DEBUG(“STARTING TCP”);
        SIMCOM(“AT+CIPSTART=” + TCPIP);
        delay(1500);
        serialClear();
        state = TCP_SEND;
        break;

case TCP_SEND:
        //DEBUG(“SENDING TO TCP”);
        SIMCOM(“AT+CIPSEND”);
        delay(500);
        serialClear();
        data = “”;
        data += “GET /pages/path/get_data_cars.aspx?id=2&LT=”;
        //data += os.lat;    //FOR FLOAT VARIABLE.
        data += os.LAT;  //FOR STRING.
        data += “&LO=”;
        //data += os.lng;    //FOR FLOAT VARIABLE.
        data += os.LNG;  //FOR STRING.
        data += " HTTP/1.1\r\nHOST:comdelta-gpstracking.com\r\n\r\n";
        // DEBUG(data);
        SIMCOM(data);
        mySerial.write(0x1A);
        delay(200);
        serialClear();
        state = TCP_STOP;
        break;

case TCP_STOP:
        // DEBUG(“STOPPING TCP”);
        SIMCOM(“AT+CIPCLOSE”);
        delay(200);
        serialClear();
        state = 0;
        break;

default:
        //DEBUG(“SYSTEM OFF”);
        serialClear();
        stateMachine = false;
        delayMicroseconds(3600000000);
        break;
    }
  }
}

short getGPS() {
  //    EXAMPLE PACKET…
  //    $GPRMC,065134.000,A,2450.1904,N,06707.5436,E,0.0,154.5,020115,A*64
  unsigned int index = 7;
  short result = 0;

//    char val = “$GPRMC,051814.000,A,2479.4586,N,06713.4666,E,0.0,262.31,241115,A*6D\r\n”;
  if (gpsData.gpsBuf[0] == ’


-jim lee && gpsData.gpsBuf[1] == 'G' && gpsData.gpsBuf[2] == 'P' && gpsData.gpsBuf[3] == 'R' && gpsData.gpsBuf[4] == 'M' && gpsData.gpsBuf[5] == 'C') {
    index = getData(os.TIME, gpsData.gpsBuf, index);
    index = getData(os.PACKET, gpsData.gpsBuf, index);
    index = getData(os.LAT, gpsData.gpsBuf, index);
    index = getData(os.DIR_LAT, gpsData.gpsBuf, index);
    index = getData(os.LNG, gpsData.gpsBuf, index);
    index = getData(os.DIR_LNG, gpsData.gpsBuf, index);
    index = getData(os.SPEEDD, gpsData.gpsBuf, index);
    index = getData(os.ANGLE, gpsData.gpsBuf, index);
    index = getData(os.DATE, gpsData.gpsBuf, index);
    //        os.lat = str2float(os.LAT);
    //        os.lng = str2float(os.LNG);
    if (os.PACKET[0] == 'A') result = 1;
    else result = 1;
  } else {
    result = 0;
  }
  return result;
}

unsigned int getData(char *data, char *packet, int index) {
  unsigned int i = 0;
  do {
    data[i++] = packet[index++];
  } while (packet[index] != ',');
  data[i] = 0;
  return index + 1;
}

void serialClear() {
  mySerial.flush();
  // while(Serial1.available()) Serial.write(Serial1.read());
}

-jim lee

SoftwareSerial mySerial(4, 5); // RX, TX

Can you post a picture of the mySerial that is connected to those pins?

  while (!mySerial);

Have you ever seen another piece of code that does that?

Why do you do that AFTER dereferencing the (stupidly named) mySerial instance?

  delay(10000);

After every is ready to go, stuff your head in the sand for 10 seconds. Why?

  float value = analogRead(A7);

The analogRead() function does not return a float.

  if (TEMP > 2.5) {
  }
  else if (TEMP < 2.5) {

And, if TEMP is exactly 2.5? Why is THAT a special case?

        delayMicroseconds(3600000000);

RTFM and pay attention to the range of values that delayMicroseconds() accepts as VALID input.

What IS the problem that you are trying to solve?

First, let me say that it’s nice to see that you’re using an FSM, and that your code is very readable! The problem is that you are using delay, and some parts of the sketch block (waiting for GPRMC). The LDR/LED won’t be able to run while your sketch is delaying/blocking. You’ll need use FSMs for everything: turning the LED on for a certain amount of time, watching TEMP and sending a message, etc.

It looks like the FSM only runs when TEMP gets below 2.5, and then it runs until it gets to state 0. I would suggest adding 0 as a state at the top:

    #define CHECKING_TEMP 0

This makes it an obvious transition, and you can have a case CHECKING_TEMP instead of default. Very readable!

Next, I would like to offer some suggestions:

1) Use something besides SoftwareSerial for the GPS. SoftwareSerial is very inefficient, because it disables interrupts for long periods of time. This can interfere with other parts of your sketch or with other libraries. Also, SoftwareSerial cannot transmit (a command) and receive (GPS data) at the same time. If you send a command, not GPS data can be received. But it also means that if a GPS character starts coming in, your sketch has to wait to do anything else. It won’t be able to send anything until the GPS character is completely received, 1ms later.

Instead, use AltSoftSerial on pins 8 & 9. It is much more efficient, but it can only be used on those pins. AltSoftSerial can transmit and receive at the same time.

If you can’t switch pins, I would suggest using my NeoSWSerial library. It is almost as efficient as AltSoftSerial, and it can work on any two pins. It can transmit and receive simultaneously, but it only works at baud rates 9600, 19200 and 38400. It would work for your GPS port. And please name the variable something meaningful:

#include <NeoSWSerial.h>

NeoSWSerial SIMCOM( 4, 5 );  //  now you don't need a define!

2) Don’t use String™. You are just using them to build SIM command strings. It’s very easy to write out the individual pieces. There’s no reason to build one big command and then write it with one print statement. For example, instead of this:

         data = "";
        data += "GET /pages/path/get_data_cars.aspx?id=2&LT=";
        //data += os.lat;     //FOR FLOAT VARIABLE.
        data += os.LAT;   //FOR STRING.
        data += "&LO=";
        //data += os.lng;     //FOR FLOAT VARIABLE.
        data += os.LNG;   //FOR STRING.
        data += " HTTP/1.1\r\nHOST:comdelta-gpstracking.com\r\n\r\n";
        // DEBUG(data);
        SIMCOM(data);

… just do this:

         SIMCOM.print( F("GET /pages/path/get_data_cars.aspx?id=2&LT=") );
        //SIMCOM.print( os.lat, 6 );     //FOR FLOAT VARIABLE.
        SIMCOM.print( os.LAT );   //FOR CHAR ARRAY
        SIMCOM.print( F("&LO=") );
        //SIMCOM.print( os.lng, 6 );     //FOR FLOAT VARIABLE.
        SIMCOM.print( os.LNG );   //FOR CHAR ARRAY.
        SIMCOM.println( F(" HTTP/1.1\r\nHOST:comdelta-gpstracking.com\r\n\r\n") );

That does the exact same thing.

Then, to get rid of the String TCPIP, just use a define at the top:

    #define TCPIP "\"TCP\",\"72.249.151.136\",\"80\""

… and change the usage below to this:

    SIMCOM.println( F("AT+CIPSTART=" TCPIP) );

It looks a little weird, but the compiler will concatenate double-quoted strings that are next to each other. When compiling, TCPIP gets substituted first to this:

    SIMCOM.println( F("AT+CIPSTART=" "\"TCP\",\"72.249.151.136\",\"80\"") );

Now you can see the two double-quoted strings, next to each other. Then the F macro uses those two literals like they were one literal:

    SIMCOM.println( F("AT+CIPSTART=\"TCP\",\"72.249.151.136\",\"80\"") );

Doing these two things will save 1600 bytes in your program size. It will also avoid a common memory problem caused by the String class.

3) Use the F macro around “double-quoted” string constants. Without the F macro, those literals also use RAM. The F macro forces the print to get the characters from FLASH memory. This will save >100 bytes of RAM.

4) Eliminate delay by adding extra “waiting” states. Like SoftwareSerial, a delay will keep your sketch from doing anything else. I would suggest looking at the “Blink without delay” and “How to do several things at a time” examples on the Useful Links page at the top of this subforum.

After reading those topics, I hope you can see that your state machine is very close to being able to avoid those delays. Let’s look at the first state, ECHO. If you added an ECHO_DELAY state, you could eliminate the delay. Instead of this:

      case ECHO:
        // DEBUG("ECHO OFF");
        SIMCOM("ATE0");
        delay(100);
        serialClear();
        state = GPS_PWR_ON;
        break;

… just do this:

       case ECHO:
        // DEBUG("ECHO OFF");
        SIMCOM( F("ATE0") );
        state     = ECHO_DELAY;
        stateTime = millis();
        break;

      case ECHO_DELAY:
        serialClear();
        if (millis() - stateTime >= 100)
          state = GPS_PWR_ON;
        break;

That’s it! Now your program will keep running, perhaps doing other tasks while it’s waiting for those 100ms to go by. You will need to declare stateTime with your other FSM variables:

bool     stateMachine = false;
int      state = 0;
uint32_t stateTime;

Apply this same technique to all the other states and delays.

5) The serial flush() method empties the transmit buffer by waiting. It does not empty the receive buffer. serialClear should probably do this:

void serialClear() {
  while (SIMCOM.available()) {
    char c = SIMCOM.read();
    //Serial.write( c );
  }
}

6) Use the C/C++ enum type to declare your states. An enumerated type has a discrete set of values, while an int can have any numeric value within its range. Your enum states would be declared as:

enum state_t
  {
    CHECKING_TEMP,
    ECHO, ECHO_DELAY, 
    GPS_PWR_ON, GPS_PWR_OFF, GPS_PKT_ON, GPS_PKT_OFF, GET_GPS, 
    TCP_START, TCP_SEND, TCP_STOP,
    SYSTEM_OFF
  };

Then you would declare the state variable:

    state_t  state = CHECKING_TEMP;

7) Use a real GPS library to parse the GPS characters. Your current implementation does not verify the checksum, it “blocks” in the GET_GPS state (like a delay), it doesn’t check for valid values in the fields, and it hasn’t converted the funky NMEA lat/lon format to simething you can use.

I would like to suggest my NeoGPS library. It is smaller, faster and more accurate than other libraries. It can be configured to parse only the fields you actually use. Here is how you would use NeoGPS to get the parsed GPS data:

     case GET_GPS:
      if (gps.available( gps_port )) {
        fix = gps.read(); // declared above, this structure now contains the latest GPS values

        //DEBUG("OUT OF BOX");

        state     = GET_GPS_DELAY;
        stateTime = millis();
      }
      break;

    case GET_GPS_DELAY:
      serialClear();
      if (millis() - stateTime >= 200)
        state = GPS_PKT_OFF;
      break;

The variable fix will contain all the parsed pieces of a GPRMC sentence. The lat/lon will be “converted” and ready to use for distance and bearing calculations.

I have attached a version of your program incorporating all of those suggestions. 8-O If you’d like to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch → Include Library → Manage Libraries.

Your original program used 6652 bytes of program space and 693 bytes of RAM (no GPS parser).
The NeoGPS/NeoSWSerial uses 9110 bytes of program space and only 218 bytes of RAM.

The difference in program size is caused by +1400 bytes for NeoSWSerial, -1600 for String, +2600 for GPS parser.

While most changes are optional/recommended, changes 4 & 5 are required for you to implement your extra LDR/LED state machine. No part of the sketch blocks, because it is always running: no delays and no wait for GPS. Just add another state variable and time value in loop.

Cheers,
/dev

SalmanAhmed.ino (6.95 KB)