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