Using a SIM900a with Arduino uno to send GPS location to a server using GPRS

8k views Asked by At

I have been trying to do this for 2 days now. I am able to send the GPS lat,lang as a text message to a receiving number. However what i want now is to send this using GPRS. I am using a SIM900A GSM/GPRS module. I am a Software Engineering student and I'm quite new to Arduino. This is my code to use GSM.

#include <SoftwareSerial.h>
#include <TinyGPS.h>

TinyGPS gps;
SoftwareSerial ss(5, 6);

static void smartdelay(unsigned long ms);

void setup()
{
  Serial.begin(115200);
  ss.begin(9600);
}

void loop()
{
  float flat, flon;
  unsigned long age, date, time, chars = 0;
  unsigned short sentences = 0, failed = 0;

  gps.f_get_position(&flat, &flon, &age);
  sendLatLang(flat, flon);

  gps.stats(&chars, &sentences, &failed);
  Serial.println();

  smartdelay(1000);
}

static void smartdelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void sendLatLang(float lat, float lang)
{
  if (lat == TinyGPS::GPS_INVALID_F_ANGLE || lang == TinyGPS::GPS_INVALID_F_ANGLE) {
Serial.println("Searching for GPS fix...");
  } else {
    Serial.println("AT+CMGF=1");
    delay(1000);
    Serial.println("AT+CMGS=\"+94123445678\"");
    delay(1000);
    Serial.print("Latittude : ");
    Serial.println(lat);
    Serial.print("Longitude : ");
    Serial.println(lang);
    Serial.write(26);
    delay(1000);
  }
}
1

There are 1 answers

1
slash-dev On BEST ANSWER

First, SoftwareSerial is very, very inefficient. It disables interrupts for long periods of time, which interferes with other parts of your sketch.

To summarize this answer:

  • AltSoftSerial is best, but it only works on pins 8 & 9 (on an UNO).
  • NeoSWSerial is next best. It works on any two pins, but it only supports baud rates 9600, 19200 and 38400

Second, your because your program talks to two devices, you have to be careful about using delay or "blocking" at any part of your program. While the program is "stuck" at a delay, nothing else is being processed. GPS characters continue to come in, and they will eventually overflow the input buffer (64 char limit). The smartDelay function tries to work around that.

You should use a Finite-state Machine to handle the sending, without calling delay. Every time through loop, the FSM will check to see if it's time to do the next step. This is very easy to implement with a switch statement and a case for each "step" of the sending process (i.e., the current "state").

This allows loop to constantly handle all the GPS characters, even though the FSM is "waiting" for some time to elapse. The FSM isn't using delay, it is constantly checking millis() to see if the time has elapsed. It checks once per loop.

I would also recommend using my NeoGPS library. It is smaller, faster and more accurate than all other libraries, and it can be configured to parse only the fields and NMEA messages that you really use. If you'd like to try it, it is available from the Arduino IDE menu Sketch -> Include Library -> Manage Libraries.

Here is a NeoGPS version of your sketch:

#include <NeoSWSerial.h>
#include <NMEAGPS.h>

NMEAGPS gps;
NeoSWSerial gps_port(5, 6);

enum state_t { WAITING_TO_SEND, SENDING_CMD1, SENDING_CMD2 }; // valid FSM states
state_t  state;     // current FSM state
uint32_t stateTime; // FSM timer for delays between states

bool     newData = false; // true when a fix with a location is ready
gps_fix  fixToSend;
const uint32_t SEND_INTERVAL = 60 * 1000UL; // once a minute

void setup()
{
  Serial.begin(115200);
  gps_port.begin(9600);
}

void loop()
{
  // Always process GPS characters so the latest fix is ready to go
  while (gps.available( gps_port )) {
    gps_fix newFix = gps.read();

    if (newFix.valid.location) {
      newData   = true;
      fixToSend = newFix;
    }
  }

  // FSM for sending fixes
  switch (state) {

    case WAITING_TO_SEND:
      //  Don't send new data too often.
      if (newData && (millis() - stateTime >= SEND_INTERVAL)) {
        Serial.println( F("AT+CMGF=1") );
        stateTime = millis();
        state     = SENDING_CMD1;
      }
      break;

    case SENDING_CMD1:
      //  Delay 1 second after the CMGF command
      if (millis() - stateTime >= 1000) {
        Serial.println( F("AT+CMGS=\"+94123445678\"") );
        stateTime = millis();
        state     = SENDING_CMD2;
      }
      break;

    case SENDING_CMD2:
      //  Delay 1 second after the CMGS command...
      if (millis() - stateTime >= 1000) {
        // ... then send the message
        sendFix( fixToSend );
        Serial.write(26); // no more data to send

        newData   = false;
        stateTime = millis();
        state     = WAITING_TO_SEND;
      }
      break;
  }
}


static void sendFix( const gps_fix &fix )
{
  //  Send only the fix pieces of interest
  //    (other pieces described on Data Model page)

  Serial.print( F("Latitude : ") );
  Serial.println( fix.latitude(), 6 );
  Serial.print( F("Longitude : ") );
  Serial.println( fix.longitude(), 6 );
}

Your original program used 8896 bytes of program space and 564 bytes of RAM. The NeoGPS version uses 8562 bytes of program space and 364 bytes of RAM.

Even if you don't use NeoGPS, be sure to read the Troubleshooting page. It describes many common problems, which are usually related to program structure and timing.

P.S. Notice that the F macro is used on "double-quoted" string constants... this saves RAM by forcing them to be used from FLASH memory. The sketch also has a SENDING_INTERVAL to avoid sending lat/long messages every second. :P