Crispin Mukalay / Adafruit_FONA_SIMCOM_Library

Dependents:   GPS_SIMCOM_SIM808

Fork of Adafruit_FONA_Library by Marc Plouhinec

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Adafruit_FONA.cpp Source File

Adafruit_FONA.cpp

00001 /***************************************************
00002   This is a library for our Adafruit FONA Cellular Module
00003 
00004   Designed specifically to work with the Adafruit FONA
00005   ----> http://www.adafruit.com/products/1946
00006   ----> http://www.adafruit.com/products/1963
00007 
00008   These displays use TTL Serial to communicate, 2 pins are required to
00009   interface
00010   Adafruit invests time and resources providing this open source code,
00011   please support Adafruit and open-source hardware by purchasing
00012   products from Adafruit!
00013 
00014   Written by Limor Fried/Ladyada for Adafruit Industries.
00015   BSD license, all text above must be included in any redistribution
00016  ****************************************************/
00017  
00018  /*
00019   *  Modified by Marc PLOUHINEC 27/06/2015 for use in mbed
00020   */
00021   
00022  /*
00023   *  Modified by Crispin MUKALAY 31/08/2017 to read GPS coordinates using SIMCOM's GSM/GPS/BT SIM808 module
00024   */
00025 
00026 #include <algorithm>
00027 #include "Adafruit_FONA.h"
00028 
00029 #define HIGH 1
00030 #define LOW 0
00031 
00032 bool Adafruit_FONA::begin(int baudrate) {
00033     mySerial.baud(baudrate);
00034     mySerial.attach(this, &Adafruit_FONA::onSerialDataReceived, Serial::RxIrq);
00035     
00036     _rstpin = HIGH;
00037     wait_ms(10);
00038     _rstpin = LOW;
00039     wait_ms(100);
00040     
00041     _rstpin = HIGH;
00042     
00043     // give 3 seconds to reboot
00044     wait_ms(3000);
00045     
00046     while (readable()) getc();
00047     
00048     sendCheckReply("AT", "OK");
00049     wait_ms(100);
00050     sendCheckReply("AT", "OK");
00051     wait_ms(100);
00052     sendCheckReply("AT", "OK");
00053     wait_ms(100);
00054     
00055     // turn off Echo!
00056     sendCheckReply("ATE0", "OK");
00057     wait_ms(100);
00058     
00059     if (! sendCheckReply("ATE0", "OK")) {
00060         return false;
00061     }
00062     
00063     return true;
00064 }
00065 
00066 void Adafruit_FONA::setEventListener(EventListener *eventListener) {
00067     this->eventListener = eventListener;
00068 }
00069 
00070 /********* Stream ********************************************/
00071 
00072 int Adafruit_FONA::_putc(int value) {
00073     return mySerial.putc(value);
00074 }
00075 
00076 int Adafruit_FONA::_getc() {
00077     __disable_irq(); // Start Critical Section - don't interrupt while changing global buffer variables
00078     
00079     // Wait for data if the buffer is empty
00080     if (isRxBufferEmpty()) {
00081         __enable_irq(); // End Critical Section - need to allow rx interrupt to get new characters for buffer
00082         
00083         while(isRxBufferEmpty());
00084         
00085         __disable_irq(); // Start Critical Section - don't interrupt while changing global buffer variables
00086     }
00087     
00088     int data = rxBuffer[rxBufferOutIndex];
00089     incrementRxBufferOutIndex();
00090     
00091     __enable_irq(); // End Critical Section
00092     
00093     return data;
00094 }
00095 
00096 int Adafruit_FONA::readable() {
00097     return !isRxBufferEmpty();
00098 }
00099 
00100 void Adafruit_FONA::onSerialDataReceived() {
00101     while (mySerial.readable() && !isRxBufferFull()) {
00102         int data = mySerial.getc();
00103         rxBuffer[rxBufferInIndex] = data;
00104         
00105         //
00106         // Analyze the received data in order to detect events like RING or NO CARRIER
00107         //
00108         
00109         // Copy the data in the current line
00110         if (currentReceivedLineSize < RX_BUFFER_SIZE && data != '\r' && data != '\n') {
00111             currentReceivedLine[currentReceivedLineSize] = (char) data;
00112             currentReceivedLineSize++;
00113         }
00114         
00115         // Check if the line is complete
00116         if (data == '\n') {
00117             currentReceivedLine[currentReceivedLineSize] = 0;
00118             
00119             if (eventListener != NULL) {
00120                 // Check if we have a special event
00121                 if (strcmp(currentReceivedLine, "RING") == 0) {
00122                     eventListener->onRing();
00123                 } else if (strcmp(currentReceivedLine, "NO CARRIER") == 0) {
00124                     eventListener->onNoCarrier();
00125                 }
00126             }
00127             
00128             currentReceivedLineSize = 0;
00129         }
00130         
00131         incrementRxBufferInIndex();
00132     }
00133 }
00134 
00135 /********* Real Time Clock ********************************************/
00136 
00137 bool Adafruit_FONA::enableRTC(uint8_t i) {
00138     if (! sendCheckReply("AT+CLTS=", i, "OK")) 
00139         return false;
00140     return sendCheckReply("AT&W", "OK");
00141 }
00142 
00143 /********* BATTERY & ADC ********************************************/
00144 
00145 /* returns value in mV (uint16_t) */
00146 bool Adafruit_FONA::getBattVoltage(uint16_t *v) {
00147     return sendParseReply("AT+CBC", "+CBC: ", v, ',', 2);
00148 }
00149 
00150 /* returns the percentage charge of battery as reported by sim800 */
00151 bool Adafruit_FONA::getBattPercent(uint16_t *p) {
00152     return sendParseReply("AT+CBC", "+CBC: ", p, ',', 1);
00153 }
00154 
00155 bool Adafruit_FONA::getADCVoltage(uint16_t *v) {
00156     return sendParseReply("AT+CADC?", "+CADC: 1,", v);
00157 }
00158 
00159 /********* SIM ***********************************************************/
00160 
00161 bool Adafruit_FONA::unlockSIM(char *pin)
00162 {
00163     char sendbuff[14] = "AT+CPIN=";
00164     sendbuff[8] = pin[0];
00165     sendbuff[9] = pin[1];
00166     sendbuff[10] = pin[2];
00167     sendbuff[11] = pin[3];
00168     sendbuff[12] = NULL;
00169     
00170     return sendCheckReply(sendbuff, "OK");
00171 }
00172 
00173 uint8_t Adafruit_FONA::getSIMCCID(char *ccid) {
00174     getReply("AT+CCID");
00175     // up to 20 chars
00176     strncpy(ccid, replybuffer, 20);
00177     ccid[20] = 0;
00178     
00179     readline(); // eat 'OK'
00180     
00181     return strlen(ccid);
00182 }
00183 
00184 /********* IMEI **********************************************************/
00185 
00186 uint8_t Adafruit_FONA::getIMEI(char *imei) {
00187     getReply("AT+GSN");
00188     
00189     // up to 15 chars
00190     strncpy(imei, replybuffer, 15);
00191     imei[15] = 0;
00192     
00193     readline(); // eat 'OK'
00194     
00195     return strlen(imei);
00196 }
00197 
00198 /********* NETWORK *******************************************************/
00199 
00200 uint8_t Adafruit_FONA::getNetworkStatus(void) {
00201     uint16_t status;
00202     
00203     if (! sendParseReply("AT+CREG?", "+CREG: ", &status, ',', 1)) return 0;
00204     
00205     return status;
00206 }
00207 
00208 
00209 uint8_t Adafruit_FONA::getRSSI(void) {
00210     uint16_t reply;
00211     
00212     if (! sendParseReply("AT+CSQ", "+CSQ: ", &reply) ) return 0;
00213     
00214     return reply;
00215 }
00216 
00217 /********* AUDIO *******************************************************/
00218 
00219 bool Adafruit_FONA::setAudio(uint8_t a) {
00220     // 0 is headset, 1 is external audio
00221     if (a > 1) return false;
00222     
00223     return sendCheckReply("AT+CHFA=", a, "OK");
00224 }
00225 
00226 uint8_t Adafruit_FONA::getVolume(void) {
00227     uint16_t reply;
00228     
00229     if (! sendParseReply("AT+CLVL?", "+CLVL: ", &reply) ) return 0;
00230     
00231     return reply;
00232 }
00233 
00234 bool Adafruit_FONA::setVolume(uint8_t i) {
00235     return sendCheckReply("AT+CLVL=", i, "OK");
00236 }
00237 
00238 
00239 bool Adafruit_FONA::playDTMF(char dtmf) {
00240     char str[4];
00241     str[0] = '\"';
00242     str[1] = dtmf;
00243     str[2] = '\"';
00244     str[3] = 0;
00245     return sendCheckReply("AT+CLDTMF=3,", str, "OK");
00246 }
00247 
00248 bool Adafruit_FONA::playToolkitTone(uint8_t t, uint16_t len) {
00249     return sendCheckReply("AT+STTONE=1,", t, len, "OK");
00250 }
00251 
00252 bool Adafruit_FONA::setMicVolume(uint8_t a, uint8_t level) {
00253     // 0 is headset, 1 is external audio
00254     if (a > 1) return false;
00255     
00256     return sendCheckReply("AT+CMIC=", a, level, "OK");
00257 }
00258 
00259 /********* FM RADIO *******************************************************/
00260 
00261 
00262 bool Adafruit_FONA::FMradio(bool onoff, uint8_t a) {
00263     if (! onoff) {
00264         return sendCheckReply("AT+FMCLOSE", "OK");
00265     }
00266     
00267     // 0 is headset, 1 is external audio
00268     if (a > 1) return false;
00269     
00270     return sendCheckReply("AT+FMOPEN=", a, "OK");
00271 }
00272 
00273 bool Adafruit_FONA::tuneFMradio(uint16_t station) {
00274     // Fail if FM station is outside allowed range.
00275     if ((station < 870) || (station > 1090))
00276         return false;
00277     
00278     return sendCheckReply("AT+FMFREQ=", station, "OK");
00279 }
00280 
00281 bool Adafruit_FONA::setFMVolume(uint8_t i) {
00282     // Fail if volume is outside allowed range (0-6).
00283     if (i > 6) {
00284     return false;
00285     }
00286     // Send FM volume command and verify response.
00287     return sendCheckReply("AT+FMVOLUME=", i, "OK");
00288 }
00289 
00290 int8_t Adafruit_FONA::getFMVolume() {
00291     uint16_t level;
00292     
00293     if (! sendParseReply("AT+FMVOLUME?", "+FMVOLUME: ", &level) ) return 0;
00294     
00295     return level;
00296 }
00297 
00298 int8_t Adafruit_FONA::getFMSignalLevel(uint16_t station) {
00299     // Fail if FM station is outside allowed range.
00300     if ((station < 875) || (station > 1080)) {
00301         return -1;
00302     }   
00303     
00304     // Send FM signal level query command.
00305     // Note, need to explicitly send timeout so right overload is chosen.
00306     getReply("AT+FMSIGNAL=", station, FONA_DEFAULT_TIMEOUT_MS);
00307     // Check response starts with expected value.
00308     char *p = strstr(replybuffer, "+FMSIGNAL: ");
00309     if (p == 0) return -1;
00310     p+=11;
00311     // Find second colon to get start of signal quality.
00312     p = strchr(p, ':');
00313     if (p == 0) return -1;
00314     p+=1;
00315     // Parse signal quality.
00316     int8_t level = atoi(p);
00317     readline();  // eat the "OK"
00318     return level;
00319 }
00320 
00321 /********* PWM/BUZZER **************************************************/
00322 
00323 bool Adafruit_FONA::setPWM(uint16_t period, uint8_t duty) {
00324     if (period > 2000) return false;
00325     if (duty > 100) return false;
00326     
00327     return sendCheckReply("AT+SPWM=0,", period, duty, "OK");
00328 }
00329 
00330 /********* CALL PHONES **************************************************/
00331 bool Adafruit_FONA::callPhone(char *number) {
00332     char sendbuff[35] = "ATD";
00333     strncpy(sendbuff+3, number, min((int)30, (int)strlen(number)));
00334     uint8_t x = strlen(sendbuff);
00335     sendbuff[x] = ';';
00336     sendbuff[x+1] = 0;
00337     
00338     return sendCheckReply(sendbuff, "OK");
00339 }
00340 
00341 bool Adafruit_FONA::hangUp(void) {
00342     return sendCheckReply("ATH0", "OK");
00343 }
00344 
00345 bool Adafruit_FONA::pickUp(void) {
00346     return sendCheckReply("ATA", "OK");
00347 }
00348 
00349 void Adafruit_FONA::onIncomingCall() {
00350 #ifdef ADAFRUIT_FONA_DEBUG
00351     printf("> Incoming call...\r\n");
00352 #endif
00353     _incomingCall = true;
00354 }
00355 
00356 bool Adafruit_FONA::callerIdNotification(bool enable) {
00357     if(enable){
00358         _ringIndicatorInterruptIn.fall(this, &Adafruit_FONA::onIncomingCall);
00359         return sendCheckReply("AT+CLIP=1", "OK");
00360     }
00361     
00362     _ringIndicatorInterruptIn.fall(NULL);
00363     return sendCheckReply("AT+CLIP=0", "OK");
00364 }
00365 
00366 bool Adafruit_FONA::incomingCallNumber(char* phonenum) {
00367     //+CLIP: "<incoming phone number>",145,"",0,"",0
00368     if(!_incomingCall)
00369         return false;
00370     
00371     readline();
00372     while(!strcmp(replybuffer, "RING") == 0) {
00373         flushInput();
00374         readline();
00375     }
00376     
00377     readline(); //reads incoming phone number line
00378     
00379     parseReply("+CLIP: \"", phonenum, '"');
00380     
00381 #ifdef ADAFRUIT_FONA_DEBUG
00382     printf("Phone Number: %s\r\n", replybuffer);
00383 #endif
00384     
00385     _incomingCall = false;
00386     return true;
00387 }
00388 
00389 /********* SMS **********************************************************/
00390 
00391 uint8_t Adafruit_FONA::getSMSInterrupt(void) {
00392     uint16_t reply;
00393     
00394     if (! sendParseReply("AT+CFGRI?", "+CFGRI: ", &reply) ) return 0;
00395     
00396     return reply;
00397 }
00398 
00399 bool Adafruit_FONA::setSMSInterrupt(uint8_t i) {
00400     return sendCheckReply("AT+CFGRI=", i, "OK");
00401 }
00402 
00403 int8_t Adafruit_FONA::getNumSMS(void) {
00404     uint16_t numsms;
00405     
00406     if (! sendCheckReply("AT+CMGF=1", "OK")) return -1;
00407     // ask how many sms are stored
00408     
00409     if (! sendParseReply("AT+CPMS?", "+CPMS: \"SM_P\",", &numsms) ) return -1;
00410     
00411     return numsms;
00412 }
00413 
00414 // Reading SMS's is a bit involved so we don't use helpers that may cause delays or debug
00415 // printouts!
00416 bool Adafruit_FONA::readSMS(uint8_t i, char *smsbuff, uint16_t maxlen, uint16_t *readlen) {
00417     // text mode
00418     if (! sendCheckReply("AT+CMGF=1", "OK")) return false;
00419     
00420     // show all text mode parameters
00421     if (! sendCheckReply("AT+CSDH=1", "OK")) return false;
00422     
00423     // parse out the SMS len
00424     uint16_t thesmslen = 0;
00425     
00426     //getReply(F("AT+CMGR="), i, 1000);  //  do not print debug!
00427     mySerial.printf("AT+CMGR=%d\r\n", i);
00428     readline(1000); // timeout
00429     
00430     // parse it out...
00431     if (! parseReply("+CMGR:", &thesmslen, ',', 11)) {
00432         *readlen = 0;
00433         return false;
00434     }
00435     
00436     readRaw(thesmslen);
00437     
00438     flushInput();
00439     
00440     uint16_t thelen = min(maxlen, (uint16_t)strlen(replybuffer));
00441     strncpy(smsbuff, replybuffer, thelen);
00442     smsbuff[thelen] = 0; // end the string
00443     
00444 #ifdef ADAFRUIT_FONA_DEBUG
00445     printf("%s\r\n", replybuffer);
00446 #endif
00447     *readlen = thelen;
00448     return true;
00449 }
00450 
00451 // Retrieve the sender of the specified SMS message and copy it as a string to
00452 // the sender buffer.  Up to senderlen characters of the sender will be copied
00453 // and a null terminator will be added if less than senderlen charactesr are
00454 // copied to the result.  Returns true if a result was successfully retrieved,
00455 // otherwise false.
00456 bool Adafruit_FONA::getSMSSender(uint8_t i, char *sender, int senderlen) {
00457     // Ensure text mode and all text mode parameters are sent.
00458     if (! sendCheckReply("AT+CMGF=1", "OK")) return false;
00459     if (! sendCheckReply("AT+CSDH=1", "OK")) return false;
00460     // Send command to retrieve SMS message and parse a line of response.
00461     mySerial.printf("AT+CMGR=%d\r\n", i);
00462     readline(1000);
00463     // Parse the second field in the response.
00464     bool result = parseReplyQuoted("+CMGR:", sender, senderlen, ',', 1);
00465     // Drop any remaining data from the response.
00466     flushInput();
00467     return result;
00468 }
00469 
00470 bool Adafruit_FONA::sendSMS(char *smsaddr, char *smsmsg) {
00471     if (! sendCheckReply("AT+CMGF=1", "OK")) return -1;
00472     
00473     char sendcmd[30] = "AT+CMGS=\"";
00474     strncpy(sendcmd+9, smsaddr, 30-9-2);  // 9 bytes beginning, 2 bytes for close quote + null
00475     sendcmd[strlen(sendcmd)] = '\"';
00476     
00477     if (! sendCheckReply(sendcmd, "> ")) return false;
00478 #ifdef ADAFRUIT_FONA_DEBUG
00479     printf("> %s\r\n", smsmsg);
00480 #endif
00481     mySerial.printf("%s\r\n\r\n", smsmsg);
00482     mySerial.putc(0x1A);
00483 #ifdef ADAFRUIT_FONA_DEBUG
00484     printf("^Z\r\n");
00485 #endif
00486     readline(10000); // read the +CMGS reply, wait up to 10 seconds!!!
00487     //Serial.print("* "); Serial.println(replybuffer);
00488     if (strstr(replybuffer, "+CMGS") == 0) {
00489         return false;
00490     }
00491     readline(1000); // read OK
00492     //Serial.print("* "); Serial.println(replybuffer);
00493     
00494     if (strcmp(replybuffer, "OK") != 0) {
00495         return false;
00496     }
00497     
00498     return true;
00499 }
00500 
00501 
00502 bool Adafruit_FONA::deleteSMS(uint8_t i) {
00503     if (! sendCheckReply("AT+CMGF=1", "OK")) return -1;
00504     // read an sms
00505     char sendbuff[12] = "AT+CMGD=000";
00506     sendbuff[8] = (i / 100) + '0';
00507     i %= 100;
00508     sendbuff[9] = (i / 10) + '0';
00509     i %= 10;
00510     sendbuff[10] = i + '0';
00511     
00512     return sendCheckReply(sendbuff, "OK", 2000);
00513 }
00514 
00515 /********* TIME **********************************************************/
00516 
00517 bool Adafruit_FONA::enableNetworkTimeSync(bool onoff) {
00518     if (onoff) {
00519         if (! sendCheckReply("AT+CLTS=1", "OK"))
00520             return false;
00521     } else {
00522         if (! sendCheckReply("AT+CLTS=0", "OK"))
00523             return false;
00524     }
00525     
00526     flushInput(); // eat any 'Unsolicted Result Code'
00527     
00528     return true;
00529 }
00530 
00531 bool Adafruit_FONA::enableNTPTimeSync(bool onoff, const char* ntpserver) {
00532     if (onoff) {
00533         if (! sendCheckReply("AT+CNTPCID=1", "OK"))
00534             return false;
00535         
00536         mySerial.printf("AT+CNTP=\"");
00537         if (ntpserver != 0) {
00538             mySerial.printf(ntpserver);
00539         } else {
00540             mySerial.printf("pool.ntp.org");
00541         }
00542         mySerial.printf("\",0\r\n");
00543         readline(FONA_DEFAULT_TIMEOUT_MS);
00544         if (strcmp(replybuffer, "OK") != 0)
00545             return false;
00546         
00547         if (! sendCheckReply("AT+CNTP", "OK", 10000))
00548             return false;
00549         
00550         uint16_t status;
00551         readline(10000);
00552         if (! parseReply("+CNTP:", &status))
00553             return false;
00554     } else {
00555         if (! sendCheckReply("AT+CNTPCID=0", "OK"))
00556             return false;
00557     }
00558     
00559     return true;
00560 }
00561 
00562 bool Adafruit_FONA::getTime(char* buff, uint16_t maxlen) {
00563     getReply("AT+CCLK?", (uint16_t) 10000);
00564     if (strncmp(replybuffer, "+CCLK: ", 7) != 0)
00565         return false;
00566     
00567     char *p = replybuffer+7;
00568     uint16_t lentocopy = min((uint16_t)(maxlen-1), (uint16_t)strlen(p));
00569     strncpy(buff, p, lentocopy+1);
00570     buff[lentocopy] = 0;
00571     
00572     readline(); // eat OK
00573     
00574     return true;
00575 }
00576 
00577 /********* GPS **********************************************************/
00578 
00579 
00580 bool Adafruit_FONA::enableGPS(bool onoff) {
00581     uint16_t state;
00582     
00583     // first check if its already on or off
00584     if (! sendParseReply("AT+CGNSPWR?", "+CGNSPWR: ", &state) )
00585         return false;
00586     
00587     if (onoff && !state) {
00588         if (! sendCheckReply("AT+CGNSPWR=1", "OK"))
00589             return false;
00590     } else if (!onoff && state) {
00591         if (! sendCheckReply("AT+CGNSPWR=0", "OK"))
00592             return false;
00593     }
00594     return true;
00595 }
00596 
00597 int8_t Adafruit_FONA::GPSstatus(void) {
00598     getReply("AT+CGPSSTATUS?");
00599     
00600     char *p = strstr(replybuffer, "+CGPSSTATUS: Location ");
00601     if (p == 0) return -1;
00602     
00603     p+=22;
00604     
00605     readline(); // eat 'OK'
00606     
00607     
00608     if (p[0] == 'U') return 0;
00609     if (p[0] == 'N') return 1;
00610     if (p[0] == '2') return 2;
00611     if (p[0] == '3') return 3;
00612     
00613     // else
00614     return 0;
00615 }
00616 
00617 uint8_t Adafruit_FONA::getGPSInfo(uint8_t arg, char *buffer, uint8_t maxbuff) {
00618     int32_t x = arg;
00619     
00620     getReply("AT+CGNSINF");
00621 
00622     char *p = strstr(replybuffer, "+CGNSINF: ");
00623     if (p == 0){
00624         buffer[0] = 0;
00625         return 0;
00626     }
00627     p+=9;
00628     uint8_t len = max((uint8_t)(maxbuff-1), (uint8_t)strlen(p));
00629     strncpy(buffer, p, len);
00630     buffer[len] = 0;
00631     
00632     readline(); // eat 'OK'
00633     return len;
00634 }
00635 
00636 bool Adafruit_FONA::getGPS(float *fix, float *lat, float *lon,float *altitude, float *speed_kph, float *heading) {
00637     char gpsbuffer[120];
00638 
00639     // grab the mode 2^5 gps csv from the sim808
00640     uint8_t res_len = getGPSInfo(32, gpsbuffer, 120);
00641 
00642     // make sure we have a response
00643     if (res_len == 0)
00644         return false;
00645     
00646     // skip mode
00647     char *tok = strtok(gpsbuffer, ",");
00648     if (! tok) return false;
00649     
00650     // grab fix
00651     char *fixp = strtok(NULL, ",");
00652     if (! fixp) return false;
00653     
00654     *fix = atof(fixp);
00655     
00656     // skip date
00657     tok = strtok(NULL, ",");
00658     if (! tok) return false;
00659     
00660     // grab the latitude
00661     char *latp = strtok(NULL, ",");
00662     if (! latp) return false;
00663     
00664     // grab longitude
00665     char *longp = strtok(NULL, ",");
00666     if (! longp) return false;
00667 
00668     double latitude = atof(latp);
00669     double longitude = atof(longp);
00670 
00671     *lat = latitude;  
00672     *lon = longitude;
00673     
00674     // grab altitude
00675     char *altp = strtok(NULL, ",");
00676     if (! altp) return false;
00677     
00678     *altitude = atof(altp);
00679     
00680     // only grab speed if needed
00681     if (speed_kph != NULL) {
00682         
00683         // grab the speed in knots
00684         char *speedp = strtok(NULL, ",");
00685         if (! speedp) return false;
00686         
00687         // convert to kph
00688         //*speed_kph = atof(speedp) * 1.852;
00689         *speed_kph = atof(speedp);
00690         
00691     // grab heading
00692     char *headingp = strtok(NULL, ",");
00693     if (! headingp) return false;
00694     
00695     *heading = atof(headingp);
00696         
00697         
00698     }
00699       
00700     return true;
00701 }
00702 
00703 bool Adafruit_FONA::enableGPSNMEA(uint8_t i) {
00704     char sendbuff[15] = "AT+CGPSOUT=000";
00705     sendbuff[11] = (i / 100) + '0';
00706     i %= 100;
00707     sendbuff[12] = (i / 10) + '0';
00708     i %= 10;
00709     sendbuff[13] = i + '0';
00710     
00711     return sendCheckReply(sendbuff, "OK", 2000);
00712 }
00713 
00714 
00715 /********* GPRS **********************************************************/
00716 
00717 
00718 bool Adafruit_FONA::enableGPRS(bool onoff) {
00719     if (onoff) {
00720         // disconnect all sockets
00721         sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000);
00722         
00723         if (! sendCheckReply("AT+CGATT=1", "OK", 10000))
00724             return false;
00725         
00726         // set bearer profile! connection type GPRS
00727         if (! sendCheckReply("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", "OK", 10000))
00728             return false;
00729         
00730         // set bearer profile access point name
00731         if (apn) {
00732             // Send command AT+SAPBR=3,1,"APN","<apn value>" where <apn value> is the configured APN value.
00733             if (! sendCheckReplyQuoted("AT+SAPBR=3,1,\"APN\",", apn, "OK", 10000))
00734                 return false;
00735             
00736             // set username/password
00737             if (apnusername) {
00738                 // Send command AT+SAPBR=3,1,"USER","<user>" where <user> is the configured APN username.
00739                 if (! sendCheckReplyQuoted("AT+SAPBR=3,1,\"USER\",", apnusername, "OK", 10000))
00740                     return false;
00741             }
00742             if (apnpassword) {
00743                 // Send command AT+SAPBR=3,1,"PWD","<password>" where <password> is the configured APN password.
00744                 if (! sendCheckReplyQuoted("AT+SAPBR=3,1,\"PWD\",", apnpassword, "OK", 10000))
00745                     return false;
00746             }
00747         }
00748         
00749         // open GPRS context
00750         if (! sendCheckReply("AT+SAPBR=1,1", "OK", 10000))
00751             return false;
00752     } else {
00753         // disconnect all sockets
00754         if (! sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000))
00755             return false;
00756         
00757         // close GPRS context
00758         if (! sendCheckReply("AT+SAPBR=0,1", "OK", 10000))
00759             return false;
00760         
00761         if (! sendCheckReply("AT+CGATT=0", "OK", 10000))
00762             return false;
00763     }
00764     return true;
00765 }
00766 
00767 uint8_t Adafruit_FONA::GPRSstate(void) {
00768     uint16_t state;
00769     
00770     if (! sendParseReply("AT+CGATT?", "+CGATT: ", &state) )
00771         return -1;
00772     
00773     return state;
00774 }
00775 
00776 void Adafruit_FONA::setGPRSNetworkSettings(const char* apn, const char* ausername, const char* apassword) {
00777     this->apn = (char*) apn;
00778     this->apnusername = (char*) ausername;
00779     this->apnpassword = (char*) apassword;
00780 }
00781 
00782 bool Adafruit_FONA::getGSMLoc(uint16_t *errorcode, char *buff, uint16_t maxlen) {
00783     getReply("AT+CIPGSMLOC=1,1", (uint16_t)10000);
00784     
00785     if (! parseReply("+CIPGSMLOC: ", errorcode))
00786         return false;
00787     
00788     char *p = replybuffer+14;
00789     uint16_t lentocopy = min((uint16_t)(maxlen-1), (uint16_t)strlen(p));
00790     strncpy(buff, p, lentocopy+1);
00791     
00792     readline(); // eat OK
00793     
00794     return true;
00795 }
00796 
00797 bool Adafruit_FONA::getGSMLoc(float *lat, float *lon) {
00798     uint16_t returncode;
00799     char gpsbuffer[120];
00800     
00801     // make sure we could get a response
00802     if (! getGSMLoc(&returncode, gpsbuffer, 120))
00803         return false;
00804     
00805     // make sure we have a valid return code
00806     if (returncode != 0)
00807         return false;
00808     
00809     // tokenize the gps buffer to locate the lat & long
00810     char *latp = strtok(gpsbuffer, ",");
00811     if (! latp) return false;
00812     
00813     char *longp = strtok(NULL, ",");
00814     if (! longp) return false;
00815     
00816     *lat = atof(latp);
00817     *lon = atof(longp);
00818     
00819     return true;
00820 }
00821 
00822 /********* TCP FUNCTIONS  ************************************/
00823 
00824 
00825 bool Adafruit_FONA::TCPconnect(char *server, uint16_t port) {
00826     flushInput();
00827     
00828     // close all old connections
00829     if (! sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000) ) return false;
00830     
00831     // single connection at a time
00832     if (! sendCheckReply("AT+CIPMUX=0", "OK") ) return false;
00833     
00834     // manually read data
00835     if (! sendCheckReply("AT+CIPRXGET=1", "OK") ) return false;
00836     
00837 #ifdef ADAFRUIT_FONA_DEBUG
00838     printf("AT+CIPSTART=\"TCP\",\"%s\",\"%d\"\r\n", server, port);
00839 #endif
00840     
00841     mySerial.printf("AT+CIPSTART=\"TCP\",\"%s\",\"%d\"\r\n", server, port);
00842     
00843     if (! expectReply("OK")) return false;
00844     if (! expectReply("CONNECT OK")) return false;
00845     return true;
00846 }
00847 
00848 bool Adafruit_FONA::TCPclose(void) {
00849     return sendCheckReply("AT+CIPCLOSE", "OK");
00850 }
00851 
00852 bool Adafruit_FONA::TCPconnected(void) {
00853     if (! sendCheckReply("AT+CIPSTATUS", "OK", 100) ) return false;
00854     readline(100);
00855 #ifdef ADAFRUIT_FONA_DEBUG
00856     printf("\t<--- %s\r\n", replybuffer);
00857 #endif
00858     return (strcmp(replybuffer, "STATE: CONNECT OK") == 0);
00859 }
00860 
00861 bool Adafruit_FONA::TCPsend(char *packet, uint8_t len) {
00862 #ifdef ADAFRUIT_FONA_DEBUG
00863     printf("AT+CIPSEND=%d\r\n", len);
00864     
00865     for (uint16_t i=0; i<len; i++) {
00866         printf(" 0x%#02x", packet[i]);
00867     }
00868     printf("\r\n");
00869 #endif
00870     
00871     
00872     mySerial.printf("AT+CIPSEND=%d\r\n", len);
00873     readline();
00874 #ifdef ADAFRUIT_FONA_DEBUG
00875     printf("\t<--- %s\r\n", replybuffer);
00876 #endif
00877     if (replybuffer[0] != '>') return false;
00878     
00879     for (uint16_t i=0; i<len; i++) {
00880         mySerial.putc(packet[i]);
00881     }
00882     readline(3000); // wait up to 3 seconds to send the data
00883 #ifdef ADAFRUIT_FONA_DEBUG
00884     printf("\t<--- %s\r\n", replybuffer);
00885 #endif
00886     
00887     return (strcmp(replybuffer, "SEND OK") == 0);
00888 }
00889 
00890 uint16_t Adafruit_FONA::TCPavailable(void) {
00891     uint16_t avail;
00892     
00893     if (! sendParseReply("AT+CIPRXGET=4", "+CIPRXGET: 4,", &avail, ',', 0) ) return false;
00894     
00895 #ifdef ADAFRUIT_FONA_DEBUG
00896     printf("%d bytes available\r\n", avail);
00897 #endif
00898     
00899     return avail;
00900 }
00901 
00902 
00903 uint16_t Adafruit_FONA::TCPread(uint8_t *buff, uint8_t len) {
00904     uint16_t avail;
00905     
00906     mySerial.printf("AT+CIPRXGET=2,%d\r\n", len);
00907     readline();
00908     if (! parseReply("+CIPRXGET: 2,", &avail, ',', 0)) return false;
00909     
00910     readRaw(avail);
00911     
00912 #ifdef ADAFRUIT_FONA_DEBUG
00913     printf("%d bytes read\r\n", avail);
00914     for (uint8_t i=0;i<avail;i++) {
00915         printf(" 0x%#02x", replybuffer[i]);
00916     }
00917     printf("\r\n");
00918 #endif
00919     
00920     memcpy(buff, replybuffer, avail);
00921     
00922     return avail;
00923 }
00924 
00925 /********* HTTP LOW LEVEL FUNCTIONS  ************************************/
00926 
00927 bool Adafruit_FONA::HTTP_init() {
00928     return sendCheckReply("AT+HTTPINIT", "OK");
00929 }
00930 
00931 bool Adafruit_FONA::HTTP_term() {
00932     return sendCheckReply("AT+HTTPTERM", "OK");
00933 }
00934 
00935 void Adafruit_FONA::HTTP_para_start(const char* parameter, bool quoted) {
00936     flushInput();
00937     
00938 #ifdef ADAFRUIT_FONA_DEBUG
00939     printf("\t---> AT+HTTPPARA=\"%s\"\r\n", parameter);
00940 #endif
00941     
00942     mySerial.printf("AT+HTTPPARA=\"%s", parameter);
00943     if (quoted)
00944         mySerial.printf("\",\"");
00945     else
00946         mySerial.printf("\",");
00947 }
00948 
00949 bool Adafruit_FONA::HTTP_para_end(bool quoted) {
00950     if (quoted)
00951         mySerial.printf("\"\r\n");
00952     else
00953         mySerial.printf("\r\n");
00954     
00955     return expectReply("OK");
00956 }
00957 
00958 bool Adafruit_FONA::HTTP_para(const char* parameter, const char* value) {
00959     HTTP_para_start(parameter, true);
00960     mySerial.printf(value);
00961     return HTTP_para_end(true);
00962 }
00963 
00964 bool Adafruit_FONA::HTTP_para(const char* parameter, int32_t value) {
00965     HTTP_para_start(parameter, false);
00966     mySerial.printf("%d", value);
00967     return HTTP_para_end(false);
00968 }
00969 
00970 bool Adafruit_FONA::HTTP_data(uint32_t size, uint32_t maxTime) {
00971     flushInput();
00972     
00973 #ifdef ADAFRUIT_FONA_DEBUG
00974     printf("\t---> AT+HTTPDATA=%d,%d\r\n", size, maxTime);
00975 #endif
00976     
00977     mySerial.printf("AT+HTTPDATA=%d,%d\r\n", size, maxTime);
00978     
00979     return expectReply("DOWNLOAD");
00980 }
00981 
00982 bool Adafruit_FONA::HTTP_action(uint8_t method, uint16_t *status, uint16_t *datalen, int32_t timeout) {
00983     // Send request.
00984     if (! sendCheckReply("AT+HTTPACTION=", method, "OK"))
00985         return false;
00986     
00987     // Parse response status and size.
00988     readline(timeout);
00989     if (! parseReply("+HTTPACTION:", status, ',', 1))
00990         return false;
00991     if (! parseReply("+HTTPACTION:", datalen, ',', 2))
00992         return false;
00993     
00994     return true;
00995 }
00996 
00997 bool Adafruit_FONA::HTTP_readall(uint16_t *datalen) {
00998     getReply("AT+HTTPREAD");
00999     if (! parseReply("+HTTPREAD:", datalen, ',', 0))
01000         return false;
01001     
01002     return true;
01003 }
01004 
01005 bool Adafruit_FONA::HTTP_ssl(bool onoff) {
01006     return sendCheckReply("AT+HTTPSSL=", onoff ? 1 : 0, "OK");
01007 }
01008 
01009 /********* HTTP HIGH LEVEL FUNCTIONS ***************************/
01010 
01011 bool Adafruit_FONA::HTTP_GET_start(char *url, uint16_t *status, uint16_t *datalen){
01012     if (! HTTP_setup(url))
01013         return false;
01014     
01015     // HTTP GET
01016     if (! HTTP_action(FONA_HTTP_GET, status, datalen))
01017         return false;
01018     
01019 #ifdef ADAFRUIT_FONA_DEBUG
01020     printf("Status: %d\r\n", *status);
01021     printf("Len: %d\r\n", *datalen);
01022 #endif
01023     
01024     // HTTP response data
01025     if (! HTTP_readall(datalen))
01026         return false;
01027     
01028     return true;
01029 }
01030 
01031 void Adafruit_FONA::HTTP_GET_end(void) {
01032     HTTP_term();
01033 }
01034 
01035 bool Adafruit_FONA::HTTP_POST_start(char *url, const char* contenttype, const uint8_t *postdata, uint16_t postdatalen, uint16_t *status, uint16_t *datalen) {
01036     if (! HTTP_setup(url))
01037         return false;
01038     
01039     if (! HTTP_para("CONTENT", contenttype)) {
01040         return false;
01041     }
01042     
01043     // HTTP POST data
01044     if (! HTTP_data(postdatalen, 10000))
01045         return false;
01046     for (uint16_t i = 0; i < postdatalen; i++) {
01047         mySerial.putc(postdata[i]);
01048     }
01049     if (! expectReply("OK"))
01050         return false;
01051     
01052     // HTTP POST
01053     if (! HTTP_action(FONA_HTTP_POST, status, datalen))
01054         return false;
01055     
01056 #ifdef ADAFRUIT_FONA_DEBUG
01057     printf("Status: %d\r\n", *status);
01058     printf("Len: %d\r\n", *datalen);
01059 #endif
01060     
01061     // HTTP response data
01062     if (! HTTP_readall(datalen))
01063         return false;
01064     
01065     return true;
01066 }
01067 
01068 void Adafruit_FONA::HTTP_POST_end(void) {
01069     HTTP_term();
01070 }
01071 
01072 void Adafruit_FONA::setUserAgent(const char* useragent) {
01073     this->useragent = (char*) useragent;
01074 }
01075 
01076 void Adafruit_FONA::setHTTPSRedirect(bool onoff) {
01077     httpsredirect = onoff;
01078 }
01079 
01080 /********* HTTP HELPERS ****************************************/
01081 
01082 bool Adafruit_FONA::HTTP_setup(char *url) {
01083     // Handle any pending
01084     HTTP_term();
01085     
01086     // Initialize and set parameters
01087     if (! HTTP_init())
01088         return false;
01089     if (! HTTP_para("CID", 1))
01090         return false;
01091     if (! HTTP_para("UA", useragent))
01092         return false;
01093     if (! HTTP_para("URL", url))
01094         return false;
01095     
01096     // HTTPS redirect
01097     if (httpsredirect) {
01098         if (! HTTP_para("REDIR",1))
01099             return false;
01100         
01101         if (! HTTP_ssl(true))
01102             return false;
01103     }
01104     
01105     return true;
01106 }
01107 
01108 
01109 /********* HELPERS *********************************************/
01110 
01111 bool Adafruit_FONA::expectReply(const char* reply, uint16_t timeout) {
01112     readline(timeout);
01113 #ifdef ADAFRUIT_FONA_DEBUG
01114     printf("\t<--- %s\r\n", replybuffer);
01115 #endif
01116     return (strcmp(replybuffer, reply) == 0);
01117 }
01118 
01119 /********* LOW LEVEL *******************************************/
01120 
01121 void Adafruit_FONA::flushInput() {
01122     // Read all available serial input to flush pending data.
01123     uint16_t timeoutloop = 0;
01124     while (timeoutloop++ < 40) {
01125         while(readable()) {
01126             getc();
01127             timeoutloop = 0;  // If char was received reset the timer
01128         }
01129         wait_ms(1);
01130     }
01131 }
01132 
01133 uint16_t Adafruit_FONA::readRaw(uint16_t b) {
01134     uint16_t idx = 0;
01135     
01136     while (b && (idx < sizeof(replybuffer)-1)) {
01137         if (readable()) {
01138             replybuffer[idx] = getc();
01139             idx++;
01140             b--;
01141         }
01142     }
01143     replybuffer[idx] = 0;
01144     
01145     return idx;
01146 }
01147 
01148 uint8_t Adafruit_FONA::readline(uint16_t timeout, bool multiline) {
01149     uint16_t replyidx = 0;
01150     
01151     while (timeout--) {
01152         if (replyidx >= 254) {
01153             break;
01154         }
01155     
01156         while(readable()) {
01157             char c =  getc();
01158             if (c == '\r') continue;
01159             if (c == 0xA) {
01160                 if (replyidx == 0)   // the first 0x0A is ignored
01161                     continue;
01162                 
01163                 if (!multiline) {
01164                     timeout = 0;         // the second 0x0A is the end of the line
01165                     break;
01166                 }
01167             }
01168             replybuffer[replyidx] = c;
01169             replyidx++;
01170         }
01171     
01172         if (timeout == 0) {
01173             break;
01174         }
01175         wait_ms(1);
01176     }
01177     replybuffer[replyidx] = 0;  // null term
01178     return replyidx;
01179 }
01180 
01181 uint8_t Adafruit_FONA::getReply(const char* send, uint16_t timeout) {
01182     flushInput();
01183 
01184 #ifdef ADAFRUIT_FONA_DEBUG
01185     printf("\t---> %s\r\n", send);
01186 #endif
01187 
01188     mySerial.printf("%s\r\n",send);
01189 
01190     uint8_t l = readline(timeout);
01191 #ifdef ADAFRUIT_FONA_DEBUG
01192     printf("\t<--- %s\r\n", replybuffer);
01193 #endif
01194     return l;
01195 }
01196 
01197 // Send prefix, suffix, and newline. Return response (and also set replybuffer with response).
01198 uint8_t Adafruit_FONA::getReply(const char* prefix, char* suffix, uint16_t timeout) {
01199     flushInput();
01200     
01201 #ifdef ADAFRUIT_FONA_DEBUG
01202     printf("\t---> %s%s\r\n", prefix, suffix);
01203 #endif
01204     
01205     mySerial.printf("%s%s\r\n", prefix, suffix);
01206     
01207     uint8_t l = readline(timeout);
01208 #ifdef ADAFRUIT_FONA_DEBUG
01209     printf("\t<--- %s\r\n", replybuffer);
01210 #endif
01211     return l;
01212 }
01213 
01214 // Send prefix, suffix, and newline. Return response (and also set replybuffer with response).
01215 uint8_t Adafruit_FONA::getReply(const char* prefix, int32_t suffix, uint16_t timeout) {
01216     flushInput();
01217     
01218 #ifdef ADAFRUIT_FONA_DEBUG
01219     printf("\t---> %s%d\r\n", prefix, suffix);
01220 #endif
01221     
01222     mySerial.printf("%s%d\r\n", prefix, suffix);
01223     
01224     uint8_t l = readline(timeout);
01225 #ifdef ADAFRUIT_FONA_DEBUG
01226     printf("\t<--- %s\r\n", replybuffer);
01227 #endif
01228     return l;
01229 }
01230 
01231 // Send prefix, suffix, suffix2, and newline. Return response (and also set replybuffer with response).
01232 uint8_t Adafruit_FONA::getReply(const char* prefix, int32_t suffix1, int32_t suffix2, uint16_t timeout) {
01233     flushInput();
01234     
01235 #ifdef ADAFRUIT_FONA_DEBUG
01236     printf("\t---> %s%d,%d\r\n", prefix, suffix1, suffix2);
01237 #endif
01238     
01239     mySerial.printf("%s%d,%d\r\n", prefix, suffix1, suffix2);
01240     
01241     uint8_t l = readline(timeout);
01242 #ifdef ADAFRUIT_FONA_DEBUG
01243     printf("\t<--- %s\r\n", replybuffer);
01244 #endif
01245     return l;
01246 }
01247 
01248 // Send prefix, ", suffix, ", and newline. Return response (and also set replybuffer with response).
01249 uint8_t Adafruit_FONA::getReplyQuoted(const char* prefix, const char* suffix, uint16_t timeout) {
01250     flushInput();
01251     
01252 #ifdef ADAFRUIT_FONA_DEBUG
01253     printf("\t---> %s\"%s\"\r\n", prefix, suffix);
01254 #endif
01255     
01256     mySerial.printf("%s\"%s\"\r\n", prefix, suffix);
01257     
01258     uint8_t l = readline(timeout);
01259 #ifdef ADAFRUIT_FONA_DEBUG
01260     printf("\t<--- %s\r\n", replybuffer);
01261 #endif
01262     return l;
01263 }
01264 
01265 
01266 bool Adafruit_FONA::sendCheckReply(const char *send, const char *reply, uint16_t timeout) {
01267     getReply(send, timeout);
01268     
01269     return (strcmp(replybuffer, reply) == 0);
01270 }
01271 
01272 // Send prefix, suffix, and newline.  Verify FONA response matches reply parameter.
01273 bool Adafruit_FONA::sendCheckReply(const char* prefix, char *suffix, const char* reply, uint16_t timeout) {
01274     getReply(prefix, suffix, timeout);
01275     return (strcmp(replybuffer, reply) == 0);
01276 }
01277 
01278 // Send prefix, suffix, and newline.  Verify FONA response matches reply parameter.
01279 bool Adafruit_FONA::sendCheckReply(const char* prefix, int32_t suffix, const char* reply, uint16_t timeout) {
01280     getReply(prefix, suffix, timeout);
01281     return (strcmp(replybuffer, reply) == 0);
01282 }
01283 
01284 // Send prefix, suffix, suffix2, and newline.  Verify FONA response matches reply parameter.
01285 bool Adafruit_FONA::sendCheckReply(const char* prefix, int32_t suffix1, int32_t suffix2, const char* reply, uint16_t timeout) {
01286     getReply(prefix, suffix1, suffix2, timeout);
01287     return (strcmp(replybuffer, reply) == 0);
01288 }
01289 
01290 // Send prefix, ", suffix, ", and newline.  Verify FONA response matches reply parameter.
01291 bool Adafruit_FONA::sendCheckReplyQuoted(const char* prefix, const char* suffix, const char* reply, uint16_t timeout) {
01292   getReplyQuoted(prefix, suffix, timeout);
01293   return (strcmp(replybuffer, reply) == 0);
01294 }
01295 
01296 bool Adafruit_FONA::parseReply(const char* toreply, uint16_t *v, char divider, uint8_t index) {
01297     char *p = strstr(replybuffer, toreply);  // get the pointer to the voltage
01298     if (p == 0) return false;
01299     p += strlen(toreply);
01300     
01301     for (uint8_t i=0; i<index;i++) {
01302         // increment dividers
01303         p = strchr(p, divider);
01304         if (!p) return false;
01305         p++;
01306     }
01307     
01308     *v = atoi(p);
01309     
01310     return true;
01311 }
01312 
01313 bool Adafruit_FONA::parseReply(const char* toreply, char *v, char divider, uint8_t index) {
01314     uint8_t i=0;
01315     char *p = strstr(replybuffer, toreply);
01316     if (p == 0) return false;
01317     p+=strlen(toreply);
01318     
01319     for (i=0; i<index;i++) {
01320         // increment dividers
01321         p = strchr(p, divider);
01322         if (!p) return false;
01323         p++;
01324     }
01325     
01326     for(i=0; i<strlen(p);i++) {
01327         if(p[i] == divider)
01328             break;
01329         v[i] = p[i];
01330     }
01331     
01332     v[i] = '\0';
01333     
01334     return true;
01335 }
01336 
01337 // Parse a quoted string in the response fields and copy its value (without quotes)
01338 // to the specified character array (v).  Only up to maxlen characters are copied
01339 // into the result buffer, so make sure to pass a large enough buffer to handle the
01340 // response.
01341 bool Adafruit_FONA::parseReplyQuoted(const char* toreply, char* v, int maxlen, char divider, uint8_t index) {
01342     uint8_t i=0, j;
01343     // Verify response starts with toreply.
01344     char *p = strstr(replybuffer, toreply);
01345     if (p == 0) return false;
01346     p+=strlen(toreply);
01347     
01348     // Find location of desired response field.
01349     for (i=0; i<index;i++) {
01350         // increment dividers
01351         p = strchr(p, divider);
01352         if (!p) return false;
01353             p++;
01354     }
01355     
01356     // Copy characters from response field into result string.
01357     for(i=0, j=0; j<maxlen && i<strlen(p); ++i) {
01358         // Stop if a divier is found.
01359         if(p[i] == divider)
01360             break;
01361         // Skip any quotation marks.
01362         else if(p[i] == '"')
01363             continue;
01364         v[j++] = p[i];
01365     }
01366     
01367     // Add a null terminator if result string buffer was not filled.
01368     if (j < maxlen)
01369         v[j] = '\0';
01370     
01371     return true;
01372 }
01373 
01374 bool Adafruit_FONA::sendParseReply(const char* tosend, const char* toreply, uint16_t *v, char divider, uint8_t index) {
01375     getReply(tosend);
01376     
01377     if (! parseReply(toreply, v, divider, index)) return false;
01378     
01379     readline(); // eat 'OK'
01380     
01381     return true;
01382 }