/*************************************************** This is a library for our Adafruit FONA Cellular Module Designed specifically to work with the Adafruit FONA ----> http://www.adafruit.com/products/1946 ----> http://www.adafruit.com/products/1963 These displays use TTL Serial to communicate, 2 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ // next line per http://postwarrior.com/arduino-ethershield-error-prog_char-does-not-name-a-type/ #include "Adafruit_FONA.h" #if defined(ESP8266) // ESP8266 doesn't have the min and max functions natively available like // AVR libc seems to provide. Include the STL algorithm library to get these. // Unfortunately algorithm isn't available in AVR libc so this is ESP8266 // specific (and likely needed for ARM or other platforms, but they lack // software serial and are currently incompatible with the FONA library). #include using namespace std; #endif Adafruit_FONA::Adafruit_FONA(int8_t rst) { _rstpin = rst; apn = F("FONAnet"); apnusername = 0; apnpassword = 0; mySerial = 0; httpsredirect = false; useragent = F("FONA"); ok_reply = F("OK"); } uint8_t Adafruit_FONA::type(void) { return _type; } boolean Adafruit_FONA::begin(Stream &port) { mySerial = &port; pinMode(_rstpin, OUTPUT); digitalWrite(_rstpin, HIGH); delay(10); digitalWrite(_rstpin, LOW); delay(100); digitalWrite(_rstpin, HIGH); DEBUG_PRINTLN(F("Attempting to open comm with ATs")); // give 7 seconds to reboot int16_t timeout = 7000; while (timeout > 0) { while (mySerial->available()) mySerial->read(); if (sendCheckReply(F("AT"), ok_reply)) break; while (mySerial->available()) mySerial->read(); if (sendCheckReply(F("AT"), F("AT"))) break; delay(500); timeout-=500; } if (timeout <= 0) { #ifdef ADAFRUIT_FONA_DEBUG DEBUG_PRINTLN(F("Timeout: No response to AT... last ditch attempt.")); #endif sendCheckReply(F("AT"), ok_reply); delay(100); sendCheckReply(F("AT"), ok_reply); delay(100); sendCheckReply(F("AT"), ok_reply); delay(100); } // turn off Echo! sendCheckReply(F("ATE0"), ok_reply); delay(100); if (! sendCheckReply(F("ATE0"), ok_reply)) { return false; } // turn on hangupitude sendCheckReply(F("AT+CVHU=0"), ok_reply); delay(100); flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINTLN("ATI"); mySerial->println("ATI"); readline(500, true); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); if (prog_char_strstr(replybuffer, (prog_char *)F("SIM808 R14")) != 0) { _type = FONA808_V2; } else if (prog_char_strstr(replybuffer, (prog_char *)F("SIM808 R13")) != 0) { _type = FONA808_V1; } else if (prog_char_strstr(replybuffer, (prog_char *)F("SIM800 R13")) != 0) { _type = FONA800L; } else if (prog_char_strstr(replybuffer, (prog_char *)F("SIMCOM_SIM5320A")) != 0) { _type = FONA3G_A; } else if (prog_char_strstr(replybuffer, (prog_char *)F("SIMCOM_SIM5320E")) != 0) { _type = FONA3G_E; } if (_type == FONA800L) { // determine if L or H DEBUG_PRINT(F("\t---> ")); DEBUG_PRINTLN("AT+GMM"); mySerial->println("AT+GMM"); readline(500, true); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); if (prog_char_strstr(replybuffer, (prog_char *)F("SIM800H")) != 0) { _type = FONA800H; } } #if defined(FONA_PREF_SMS_STORAGE) sendCheckReply(F("AT+CPMS=" FONA_PREF_SMS_STORAGE "," FONA_PREF_SMS_STORAGE "," FONA_PREF_SMS_STORAGE), ok_reply); #endif return true; } /********* Serial port ********************************************/ boolean Adafruit_FONA::setBaudrate(uint16_t baud) { return sendCheckReply(F("AT+IPREX="), baud, ok_reply); } /********* Real Time Clock ********************************************/ boolean Adafruit_FONA::readRTC(uint8_t *year, uint8_t *month, uint8_t *date, uint8_t *hr, uint8_t *min, uint8_t *sec) { uint16_t v; sendParseReply(F("AT+CCLK?"), F("+CCLK: "), &v, '/', 0); *year = v; DEBUG_PRINTLN(*year); } boolean Adafruit_FONA::enableRTC(uint8_t i) { if (! sendCheckReply(F("AT+CLTS="), i, ok_reply)) return false; return sendCheckReply(F("AT&W"), ok_reply); } /********* BATTERY & ADC ********************************************/ /* returns value in mV (uint16_t) */ boolean Adafruit_FONA::getBattVoltage(uint16_t *v) { return sendParseReply(F("AT+CBC"), F("+CBC: "), v, ',', 2); } /* returns value in mV (uint16_t) */ boolean Adafruit_FONA_3G::getBattVoltage(uint16_t *v) { float f; boolean b = sendParseReply(F("AT+CBC"), F("+CBC: "), &f, ',', 2); *v = f*1000; return b; } /* returns the percentage charge of battery as reported by sim800 */ boolean Adafruit_FONA::getBattPercent(uint16_t *p) { return sendParseReply(F("AT+CBC"), F("+CBC: "), p, ',', 1); } boolean Adafruit_FONA::getADCVoltage(uint16_t *v) { return sendParseReply(F("AT+CADC?"), F("+CADC: 1,"), v); } /********* SIM ***********************************************************/ uint8_t Adafruit_FONA::unlockSIM(char *pin) { char sendbuff[14] = "AT+CPIN="; sendbuff[8] = pin[0]; sendbuff[9] = pin[1]; sendbuff[10] = pin[2]; sendbuff[11] = pin[3]; sendbuff[12] = '\0'; return sendCheckReply(sendbuff, ok_reply); } uint8_t Adafruit_FONA::getSIMCCID(char *ccid) { getReply(F("AT+CCID")); // up to 28 chars for reply, 20 char total ccid if (replybuffer[0] == '+') { // fona 3g? strncpy(ccid, replybuffer+8, 20); } else { // fona 800 or 800 strncpy(ccid, replybuffer, 20); } ccid[20] = 0; readline(); // eat 'OK' return strlen(ccid); } /********* IMEI **********************************************************/ uint8_t Adafruit_FONA::getIMEI(char *imei) { getReply(F("AT+GSN")); // up to 15 chars strncpy(imei, replybuffer, 15); imei[15] = 0; readline(); // eat 'OK' return strlen(imei); } /********* NETWORK *******************************************************/ uint8_t Adafruit_FONA::getNetworkStatus(void) { uint16_t status; if (! sendParseReply(F("AT+CREG?"), F("+CREG: "), &status, ',', 1)) return 0; return status; } uint8_t Adafruit_FONA::getRSSI(void) { uint16_t reply; if (! sendParseReply(F("AT+CSQ"), F("+CSQ: "), &reply) ) return 0; return reply; } /********* AUDIO *******************************************************/ boolean Adafruit_FONA::setAudio(uint8_t a) { // 0 is headset, 1 is external audio if (a > 1) return false; return sendCheckReply(F("AT+CHFA="), a, ok_reply); } uint8_t Adafruit_FONA::getVolume(void) { uint16_t reply; if (! sendParseReply(F("AT+CLVL?"), F("+CLVL: "), &reply) ) return 0; return reply; } boolean Adafruit_FONA::setVolume(uint8_t i) { return sendCheckReply(F("AT+CLVL="), i, ok_reply); } boolean Adafruit_FONA::playDTMF(char dtmf) { char str[4]; str[0] = '\"'; str[1] = dtmf; str[2] = '\"'; str[3] = 0; return sendCheckReply(F("AT+CLDTMF=3,"), str, ok_reply); } boolean Adafruit_FONA::playToolkitTone(uint8_t t, uint16_t len) { return sendCheckReply(F("AT+STTONE=1,"), t, len, ok_reply); } boolean Adafruit_FONA_3G::playToolkitTone(uint8_t t, uint16_t len) { if (! sendCheckReply(F("AT+CPTONE="), t, ok_reply)) return false; delay(len); return sendCheckReply(F("AT+CPTONE=0"), ok_reply); } boolean Adafruit_FONA::setMicVolume(uint8_t a, uint8_t level) { // 0 is headset, 1 is external audio if (a > 1) return false; return sendCheckReply(F("AT+CMIC="), a, level, ok_reply); } /********* FM RADIO *******************************************************/ boolean Adafruit_FONA::FMradio(boolean onoff, uint8_t a) { if (! onoff) { return sendCheckReply(F("AT+FMCLOSE"), ok_reply); } // 0 is headset, 1 is external audio if (a > 1) return false; return sendCheckReply(F("AT+FMOPEN="), a, ok_reply); } boolean Adafruit_FONA::tuneFMradio(uint16_t station) { // Fail if FM station is outside allowed range. if ((station < 870) || (station > 1090)) return false; return sendCheckReply(F("AT+FMFREQ="), station, ok_reply); } boolean Adafruit_FONA::setFMVolume(uint8_t i) { // Fail if volume is outside allowed range (0-6). if (i > 6) { return false; } // Send FM volume command and verify response. return sendCheckReply(F("AT+FMVOLUME="), i, ok_reply); } int8_t Adafruit_FONA::getFMVolume() { uint16_t level; if (! sendParseReply(F("AT+FMVOLUME?"), F("+FMVOLUME: "), &level) ) return 0; return level; } int8_t Adafruit_FONA::getFMSignalLevel(uint16_t station) { // Fail if FM station is outside allowed range. if ((station < 875) || (station > 1080)) { return -1; } // Send FM signal level query command. // Note, need to explicitly send timeout so right overload is chosen. getReply(F("AT+FMSIGNAL="), station, FONA_DEFAULT_TIMEOUT_MS); // Check response starts with expected value. char *p = prog_char_strstr(replybuffer, PSTR("+FMSIGNAL: ")); if (p == 0) return -1; p+=11; // Find second colon to get start of signal quality. p = strchr(p, ':'); if (p == 0) return -1; p+=1; // Parse signal quality. int8_t level = atoi(p); readline(); // eat the "OK" return level; } /********* PWM/BUZZER **************************************************/ boolean Adafruit_FONA::setPWM(uint16_t period, uint8_t duty) { if (period > 2000) return false; if (duty > 100) return false; return sendCheckReply(F("AT+SPWM=0,"), period, duty, ok_reply); } /********* CALL PHONES **************************************************/ boolean Adafruit_FONA::callPhone(char *number) { char sendbuff[35] = "ATD"; strncpy(sendbuff+3, number, min(30, (int)strlen(number))); uint8_t x = strlen(sendbuff); sendbuff[x] = ';'; sendbuff[x+1] = 0; //DEBUG_PRINTLN(sendbuff); return sendCheckReply(sendbuff, ok_reply); } uint8_t Adafruit_FONA::getCallStatus(void) { uint16_t phoneStatus; if (! sendParseReply(F("AT+CPAS"), F("+CPAS: "), &phoneStatus)) return FONA_CALL_FAILED; // 1, since 0 is actually a known, good reply return phoneStatus; // 0 ready, 2 unkown, 3 ringing, 4 call in progress } boolean Adafruit_FONA::hangUp(void) { return sendCheckReply(F("ATH0"), ok_reply); } boolean Adafruit_FONA_3G::hangUp(void) { getReply(F("ATH")); return (prog_char_strstr(replybuffer, (prog_char *)F("VOICE CALL: END")) != 0); } boolean Adafruit_FONA::pickUp(void) { return sendCheckReply(F("ATA"), ok_reply); } boolean Adafruit_FONA_3G::pickUp(void) { return sendCheckReply(F("ATA"), F("VOICE CALL: BEGIN")); } void Adafruit_FONA::onIncomingCall() { DEBUG_PRINT(F("> ")); DEBUG_PRINTLN(F("Incoming call...")); Adafruit_FONA::_incomingCall = true; } boolean Adafruit_FONA::_incomingCall = false; boolean Adafruit_FONA::callerIdNotification(boolean enable, uint8_t interrupt) { if(enable){ attachInterrupt(interrupt, onIncomingCall, FALLING); return sendCheckReply(F("AT+CLIP=1"), ok_reply); } detachInterrupt(interrupt); return sendCheckReply(F("AT+CLIP=0"), ok_reply); } boolean Adafruit_FONA::incomingCallNumber(char* phonenum) { //+CLIP: "",145,"",0,"",0 if(!Adafruit_FONA::_incomingCall) return false; readline(); while(!prog_char_strcmp(replybuffer, (prog_char*)F("RING")) == 0) { flushInput(); readline(); } readline(); //reads incoming phone number line parseReply(F("+CLIP: \""), phonenum, '"'); DEBUG_PRINT(F("Phone Number: ")); DEBUG_PRINTLN(replybuffer); Adafruit_FONA::_incomingCall = false; return true; } /********* SMS **********************************************************/ uint8_t Adafruit_FONA::getSMSInterrupt(void) { uint16_t reply; if (! sendParseReply(F("AT+CFGRI?"), F("+CFGRI: "), &reply) ) return 0; return reply; } boolean Adafruit_FONA::setSMSInterrupt(uint8_t i) { return sendCheckReply(F("AT+CFGRI="), i, ok_reply); } int8_t Adafruit_FONA::getNumSMS(void) { uint16_t numsms; // get into text mode if (! sendCheckReply(F("AT+CMGF=1"), ok_reply)) return -1; // ask how many sms are stored if (sendParseReply(F("AT+CPMS?"), F(FONA_PREF_SMS_STORAGE ","), &numsms)) return numsms; if (sendParseReply(F("AT+CPMS?"), F("\"SM\","), &numsms)) return numsms; if (sendParseReply(F("AT+CPMS?"), F("\"SM_P\","), &numsms)) return numsms; return -1; } // Reading SMS's is a bit involved so we don't use helpers that may cause delays or debug // printouts! boolean Adafruit_FONA::readSMS(uint8_t i, char *smsbuff, uint16_t maxlen, uint16_t *readlen) { // text mode if (! sendCheckReply(F("AT+CMGF=1"), ok_reply)) return false; // show all text mode parameters if (! sendCheckReply(F("AT+CSDH=1"), ok_reply)) return false; // parse out the SMS len uint16_t thesmslen = 0; DEBUG_PRINT(F("AT+CMGR=")); DEBUG_PRINTLN(i); //getReply(F("AT+CMGR="), i, 1000); // do not print debug! mySerial->print(F("AT+CMGR=")); mySerial->println(i); readline(1000); // timeout //DEBUG_PRINT(F("Reply: ")); DEBUG_PRINTLN(replybuffer); // parse it out... DEBUG_PRINTLN(replybuffer); if (! parseReply(F("+CMGR:"), &thesmslen, ',', 11)) { *readlen = 0; return false; } readRaw(thesmslen); flushInput(); uint16_t thelen = min(maxlen, (uint16_t)strlen(replybuffer)); strncpy(smsbuff, replybuffer, thelen); smsbuff[thelen] = 0; // end the string DEBUG_PRINTLN(replybuffer); *readlen = thelen; return true; } // Retrieve the sender of the specified SMS message and copy it as a string to // the sender buffer. Up to senderlen characters of the sender will be copied // and a null terminator will be added if less than senderlen charactesr are // copied to the result. Returns true if a result was successfully retrieved, // otherwise false. boolean Adafruit_FONA::getSMSSender(uint8_t i, char *sender, int senderlen) { // Ensure text mode and all text mode parameters are sent. if (! sendCheckReply(F("AT+CMGF=1"), ok_reply)) return false; if (! sendCheckReply(F("AT+CSDH=1"), ok_reply)) return false; DEBUG_PRINT(F("AT+CMGR=")); DEBUG_PRINTLN(i); // Send command to retrieve SMS message and parse a line of response. mySerial->print(F("AT+CMGR=")); mySerial->println(i); readline(1000); DEBUG_PRINTLN(replybuffer); // Parse the second field in the response. boolean result = parseReplyQuoted(F("+CMGR:"), sender, senderlen, ',', 1); // Drop any remaining data from the response. flushInput(); return result; } boolean Adafruit_FONA::sendSMS(char *smsaddr, char *smsmsg) { if (! sendCheckReply(F("AT+CMGF=1"), ok_reply)) return false; char sendcmd[30] = "AT+CMGS=\""; strncpy(sendcmd+9, smsaddr, 30-9-2); // 9 bytes beginning, 2 bytes for close quote + null sendcmd[strlen(sendcmd)] = '\"'; if (! sendCheckReply(sendcmd, F("> "))) return false; DEBUG_PRINT(F("> ")); DEBUG_PRINTLN(smsmsg); mySerial->println(smsmsg); mySerial->println(); mySerial->write(0x1A); DEBUG_PRINTLN("^Z"); if ( (_type == FONA3G_A) || (_type == FONA3G_E) ) { // Eat two sets of CRLF readline(200); //DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer)); readline(200); //DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer)); } readline(10000); // read the +CMGS reply, wait up to 10 seconds!!! //DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer)); if (strstr(replybuffer, "+CMGS") == 0) { return false; } readline(1000); // read OK //DEBUG_PRINT("* "); DEBUG_PRINTLN(replybuffer); if (strcmp(replybuffer, "OK") != 0) { return false; } return true; } boolean Adafruit_FONA::deleteSMS(uint8_t i) { if (! sendCheckReply(F("AT+CMGF=1"), ok_reply)) return false; // read an sms char sendbuff[12] = "AT+CMGD=000"; sendbuff[8] = (i / 100) + '0'; i %= 100; sendbuff[9] = (i / 10) + '0'; i %= 10; sendbuff[10] = i + '0'; return sendCheckReply(sendbuff, ok_reply, 2000); } /********* USSD *********************************************************/ boolean Adafruit_FONA::sendUSSD(char *ussdmsg, char *ussdbuff, uint16_t maxlen, uint16_t *readlen) { if (! sendCheckReply(F("AT+CUSD=1"), ok_reply)) return false; char sendcmd[30] = "AT+CUSD=1,\""; strncpy(sendcmd+11, ussdmsg, 30-11-2); // 11 bytes beginning, 2 bytes for close quote + null sendcmd[strlen(sendcmd)] = '\"'; if (! sendCheckReply(sendcmd, ok_reply)) { *readlen = 0; return false; } else { readline(10000); // read the +CUSD reply, wait up to 10 seconds!!! //DEBUG_PRINT("* "); DEBUG_PRINTLN(replybuffer); char *p = prog_char_strstr(replybuffer, PSTR("+CUSD: ")); if (p == 0) { *readlen = 0; return false; } p+=7; //+CUSD // Find " to get start of ussd message. p = strchr(p, '\"'); if (p == 0) { *readlen = 0; return false; } p+=1; //" // Find " to get end of ussd message. char *strend = strchr(p, '\"'); uint16_t lentocopy = min(maxlen-1, strend - p); strncpy(ussdbuff, p, lentocopy+1); ussdbuff[lentocopy] = 0; *readlen = lentocopy; } return true; } /********* TIME **********************************************************/ boolean Adafruit_FONA::enableNetworkTimeSync(boolean onoff) { if (onoff) { if (! sendCheckReply(F("AT+CLTS=1"), ok_reply)) return false; } else { if (! sendCheckReply(F("AT+CLTS=0"), ok_reply)) return false; } flushInput(); // eat any 'Unsolicted Result Code' return true; } boolean Adafruit_FONA::enableNTPTimeSync(boolean onoff, FONAFlashStringPtr ntpserver) { if (onoff) { if (! sendCheckReply(F("AT+CNTPCID=1"), ok_reply)) return false; mySerial->print(F("AT+CNTP=\"")); if (ntpserver != 0) { mySerial->print(ntpserver); } else { mySerial->print(F("pool.ntp.org")); } mySerial->println(F("\",0")); readline(FONA_DEFAULT_TIMEOUT_MS); if (strcmp(replybuffer, "OK") != 0) return false; if (! sendCheckReply(F("AT+CNTP"), ok_reply, 10000)) return false; uint16_t status; readline(10000); if (! parseReply(F("+CNTP:"), &status)) return false; } else { if (! sendCheckReply(F("AT+CNTPCID=0"), ok_reply)) return false; } return true; } boolean Adafruit_FONA::getTime(char *buff, uint16_t maxlen) { getReply(F("AT+CCLK?"), (uint16_t) 10000); if (strncmp(replybuffer, "+CCLK: ", 7) != 0) return false; char *p = replybuffer+7; uint16_t lentocopy = min(maxlen-1, (int)strlen(p)); strncpy(buff, p, lentocopy+1); buff[lentocopy] = 0; readline(); // eat OK return true; } /********* GPS **********************************************************/ boolean Adafruit_FONA::enableGPS(boolean onoff) { uint16_t state; // first check if its already on or off if (_type == FONA808_V2) { if (! sendParseReply(F("AT+CGNSPWR?"), F("+CGNSPWR: "), &state) ) return false; } else { if (! sendParseReply(F("AT+CGPSPWR?"), F("+CGPSPWR: "), &state)) return false; } if (onoff && !state) { if (_type == FONA808_V2) { if (! sendCheckReply(F("AT+CGNSPWR=1"), ok_reply)) // try GNS command return false; } else { if (! sendCheckReply(F("AT+CGPSPWR=1"), ok_reply)) return false; } } else if (!onoff && state) { if (_type == FONA808_V2) { if (! sendCheckReply(F("AT+CGNSPWR=0"), ok_reply)) // try GNS command return false; } else { if (! sendCheckReply(F("AT+CGPSPWR=0"), ok_reply)) return false; } } return true; } boolean Adafruit_FONA_3G::enableGPS(boolean onoff) { uint16_t state; // first check if its already on or off if (! Adafruit_FONA::sendParseReply(F("AT+CGPS?"), F("+CGPS: "), &state) ) return false; if (onoff && !state) { if (! sendCheckReply(F("AT+CGPS=1"), ok_reply)) return false; } else if (!onoff && state) { if (! sendCheckReply(F("AT+CGPS=0"), ok_reply)) return false; // this takes a little time readline(2000); // eat '+CGPS: 0' } return true; } int8_t Adafruit_FONA::GPSstatus(void) { if (_type == FONA808_V2) { // 808 V2 uses GNS commands and doesn't have an explicit 2D/3D fix status. // Instead just look for a fix and if found assume it's a 3D fix. getReply(F("AT+CGNSINF")); char *p = prog_char_strstr(replybuffer, (prog_char*)F("+CGNSINF: ")); if (p == 0) return -1; p+=10; readline(); // eat 'OK' if (p[0] == '0') return 0; // GPS is not even on! p+=2; // Skip to second value, fix status. //DEBUG_PRINTLN(p); // Assume if the fix status is '1' then we have a 3D fix, otherwise no fix. if (p[0] == '1') return 3; else return 1; } if (_type == FONA3G_A || _type == FONA3G_E) { // FONA 3G doesn't have an explicit 2D/3D fix status. // Instead just look for a fix and if found assume it's a 3D fix. getReply(F("AT+CGPSINFO")); char *p = prog_char_strstr(replybuffer, (prog_char*)F("+CGPSINFO:")); if (p == 0) return -1; if (p[10] != ',') return 3; // if you get anything, its 3D fix return 0; } else { // 808 V1 looks for specific 2D or 3D fix state. getReply(F("AT+CGPSSTATUS?")); char *p = prog_char_strstr(replybuffer, (prog_char*)F("SSTATUS: Location ")); if (p == 0) return -1; p+=18; readline(); // eat 'OK' //DEBUG_PRINTLN(p); if (p[0] == 'U') return 0; if (p[0] == 'N') return 1; if (p[0] == '2') return 2; if (p[0] == '3') return 3; } // else return 0; } uint8_t Adafruit_FONA::getGPS(uint8_t arg, char *buffer, uint8_t maxbuff) { int32_t x = arg; if ( (_type == FONA3G_A) || (_type == FONA3G_E) ) { getReply(F("AT+CGPSINFO")); } else if (_type == FONA808_V1) { getReply(F("AT+CGPSINF="), x); } else { getReply(F("AT+CGNSINF")); } char *p = prog_char_strstr(replybuffer, (prog_char*)F("SINF")); if (p == 0) { buffer[0] = 0; return 0; } p+=6; uint8_t len = max(maxbuff-1, (int)strlen(p)); strncpy(buffer, p, len); buffer[len] = 0; readline(); // eat 'OK' return len; } boolean Adafruit_FONA::getGPS(float *lat, float *lon, float *speed_kph, float *heading, float *altitude) { char gpsbuffer[120]; // we need at least a 2D fix if (GPSstatus() < 2) return false; // grab the mode 2^5 gps csv from the sim808 uint8_t res_len = getGPS(32, gpsbuffer, 120); // make sure we have a response if (res_len == 0) return false; if (_type == FONA3G_A || _type == FONA3G_E) { // Parse 3G respose // +CGPSINFO:4043.000000,N,07400.000000,W,151015,203802.1,-12.0,0.0,0 // skip beginning char *tok; // grab the latitude char *latp = strtok(gpsbuffer, ","); if (! latp) return false; // grab latitude direction char *latdir = strtok(NULL, ","); if (! latdir) return false; // grab longitude char *longp = strtok(NULL, ","); if (! longp) return false; // grab longitude direction char *longdir = strtok(NULL, ","); if (! longdir) return false; // skip date & time tok = strtok(NULL, ","); tok = strtok(NULL, ","); // only grab altitude if needed if (altitude != NULL) { // grab altitude char *altp = strtok(NULL, ","); if (! altp) return false; *altitude = atof(altp); } // only grab speed if needed if (speed_kph != NULL) { // grab the speed in km/h char *speedp = strtok(NULL, ","); if (! speedp) return false; *speed_kph = atof(speedp); } // only grab heading if needed if (heading != NULL) { // grab the speed in knots char *coursep = strtok(NULL, ","); if (! coursep) return false; *heading = atof(coursep); } double latitude = atof(latp); double longitude = atof(longp); // convert latitude from minutes to decimal float degrees = floor(latitude / 100); double minutes = latitude - (100 * degrees); minutes /= 60; degrees += minutes; // turn direction into + or - if (latdir[0] == 'S') degrees *= -1; *lat = degrees; // convert longitude from minutes to decimal degrees = floor(longitude / 100); minutes = longitude - (100 * degrees); minutes /= 60; degrees += minutes; // turn direction into + or - if (longdir[0] == 'W') degrees *= -1; *lon = degrees; } else if (_type == FONA808_V2) { // Parse 808 V2 response. See table 2-3 from here for format: // http://www.adafruit.com/datasheets/SIM800%20Series_GNSS_Application%20Note%20V1.00.pdf // skip GPS run status char *tok = strtok(gpsbuffer, ","); if (! tok) return false; // skip fix status tok = strtok(NULL, ","); if (! tok) return false; // skip date tok = strtok(NULL, ","); if (! tok) return false; // grab the latitude char *latp = strtok(NULL, ","); if (! latp) return false; // grab longitude char *longp = strtok(NULL, ","); if (! longp) return false; *lat = atof(latp); *lon = atof(longp); // only grab altitude if needed if (altitude != NULL) { // grab altitude char *altp = strtok(NULL, ","); if (! altp) return false; *altitude = atof(altp); } // only grab speed if needed if (speed_kph != NULL) { // grab the speed in km/h char *speedp = strtok(NULL, ","); if (! speedp) return false; *speed_kph = atof(speedp); } // only grab heading if needed if (heading != NULL) { // grab the speed in knots char *coursep = strtok(NULL, ","); if (! coursep) return false; *heading = atof(coursep); } } else { // Parse 808 V1 response. // skip mode char *tok = strtok(gpsbuffer, ","); if (! tok) return false; // skip date tok = strtok(NULL, ","); if (! tok) return false; // skip fix tok = strtok(NULL, ","); if (! tok) return false; // grab the latitude char *latp = strtok(NULL, ","); if (! latp) return false; // grab latitude direction char *latdir = strtok(NULL, ","); if (! latdir) return false; // grab longitude char *longp = strtok(NULL, ","); if (! longp) return false; // grab longitude direction char *longdir = strtok(NULL, ","); if (! longdir) return false; double latitude = atof(latp); double longitude = atof(longp); // convert latitude from minutes to decimal float degrees = floor(latitude / 100); double minutes = latitude - (100 * degrees); minutes /= 60; degrees += minutes; // turn direction into + or - if (latdir[0] == 'S') degrees *= -1; *lat = degrees; // convert longitude from minutes to decimal degrees = floor(longitude / 100); minutes = longitude - (100 * degrees); minutes /= 60; degrees += minutes; // turn direction into + or - if (longdir[0] == 'W') degrees *= -1; *lon = degrees; // only grab speed if needed if (speed_kph != NULL) { // grab the speed in knots char *speedp = strtok(NULL, ","); if (! speedp) return false; // convert to kph *speed_kph = atof(speedp) * 1.852; } // only grab heading if needed if (heading != NULL) { // grab the speed in knots char *coursep = strtok(NULL, ","); if (! coursep) return false; *heading = atof(coursep); } // no need to continue if (altitude == NULL) return true; // we need at least a 3D fix for altitude if (GPSstatus() < 3) return false; // grab the mode 0 gps csv from the sim808 res_len = getGPS(0, gpsbuffer, 120); // make sure we have a response if (res_len == 0) return false; // skip mode tok = strtok(gpsbuffer, ","); if (! tok) return false; // skip lat tok = strtok(NULL, ","); if (! tok) return false; // skip long tok = strtok(NULL, ","); if (! tok) return false; // grab altitude char *altp = strtok(NULL, ","); if (! altp) return false; *altitude = atof(altp); } return true; } boolean Adafruit_FONA::enableGPSNMEA(uint8_t i) { char sendbuff[15] = "AT+CGPSOUT=000"; sendbuff[11] = (i / 100) + '0'; i %= 100; sendbuff[12] = (i / 10) + '0'; i %= 10; sendbuff[13] = i + '0'; if (_type == FONA808_V2) { if (i) return sendCheckReply(F("AT+CGNSTST=1"), ok_reply); else return sendCheckReply(F("AT+CGNSTST=0"), ok_reply); } else { return sendCheckReply(sendbuff, ok_reply, 2000); } } /********* GPRS **********************************************************/ boolean Adafruit_FONA::enableGPRS(boolean onoff) { if (onoff) { // disconnect all sockets sendCheckReply(F("AT+CIPSHUT"), F("SHUT OK"), 20000); if (! sendCheckReply(F("AT+CGATT=1"), ok_reply, 10000)) return false; // set bearer profile! connection type GPRS if (! sendCheckReply(F("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\""), ok_reply, 10000)) return false; // set bearer profile access point name if (apn) { // Send command AT+SAPBR=3,1,"APN","" where is the configured APN value. if (! sendCheckReplyQuoted(F("AT+SAPBR=3,1,\"APN\","), apn, ok_reply, 10000)) return false; // send AT+CSTT,"apn","user","pass" flushInput(); mySerial->print(F("AT+CSTT=\"")); mySerial->print(apn); if (apnusername) { mySerial->print("\",\""); mySerial->print(apnusername); } if (apnpassword) { mySerial->print("\",\""); mySerial->print(apnpassword); } mySerial->println("\""); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(F("AT+CSTT=\"")); DEBUG_PRINT(apn); if (apnusername) { DEBUG_PRINT("\",\""); DEBUG_PRINT(apnusername); } if (apnpassword) { DEBUG_PRINT("\",\""); DEBUG_PRINT(apnpassword); } DEBUG_PRINTLN("\""); if (! expectReply(ok_reply)) return false; // set username/password if (apnusername) { // Send command AT+SAPBR=3,1,"USER","" where is the configured APN username. if (! sendCheckReplyQuoted(F("AT+SAPBR=3,1,\"USER\","), apnusername, ok_reply, 10000)) return false; } if (apnpassword) { // Send command AT+SAPBR=3,1,"PWD","" where is the configured APN password. if (! sendCheckReplyQuoted(F("AT+SAPBR=3,1,\"PWD\","), apnpassword, ok_reply, 10000)) return false; } } // open GPRS context if (! sendCheckReply(F("AT+SAPBR=1,1"), ok_reply, 30000)) return false; // bring up wireless connection if (! sendCheckReply(F("AT+CIICR"), ok_reply, 10000)) return false; } else { // disconnect all sockets if (! sendCheckReply(F("AT+CIPSHUT"), F("SHUT OK"), 20000)) return false; // close GPRS context if (! sendCheckReply(F("AT+SAPBR=0,1"), ok_reply, 10000)) return false; if (! sendCheckReply(F("AT+CGATT=0"), ok_reply, 10000)) return false; } return true; } boolean Adafruit_FONA_3G::enableGPRS(boolean onoff) { if (onoff) { // disconnect all sockets //sendCheckReply(F("AT+CIPSHUT"), F("SHUT OK"), 5000); if (! sendCheckReply(F("AT+CGATT=1"), ok_reply, 10000)) return false; // set bearer profile access point name if (apn) { // Send command AT+CGSOCKCONT=1,"IP","" where is the configured APN name. if (! sendCheckReplyQuoted(F("AT+CGSOCKCONT=1,\"IP\","), apn, ok_reply, 10000)) return false; // set username/password if (apnusername) { char authstring[100] = "AT+CGAUTH=1,1,\""; char *strp = authstring + strlen(authstring); prog_char_strcpy(strp, (prog_char *)apnusername); strp+=prog_char_strlen((prog_char *)apnusername); strp[0] = '\"'; strp++; strp[0] = 0; if (apnpassword) { strp[0] = ','; strp++; strp[0] = '\"'; strp++; prog_char_strcpy(strp, (prog_char *)apnpassword); strp+=prog_char_strlen((prog_char *)apnpassword); strp[0] = '\"'; strp++; strp[0] = 0; } if (! sendCheckReply(authstring, ok_reply, 10000)) return false; } } // connect in transparent if (! sendCheckReply(F("AT+CIPMODE=1"), ok_reply, 10000)) return false; // open network (?) if (! sendCheckReply(F("AT+NETOPEN=,,1"), F("Network opened"), 10000)) return false; readline(); // eat 'OK' } else { // close GPRS context if (! sendCheckReply(F("AT+NETCLOSE"), F("Network closed"), 10000)) return false; readline(); // eat 'OK' } return true; } uint8_t Adafruit_FONA::GPRSstate(void) { uint16_t state; if (! sendParseReply(F("AT+CGATT?"), F("+CGATT: "), &state) ) return -1; return state; } void Adafruit_FONA::setGPRSNetworkSettings(FONAFlashStringPtr apn, FONAFlashStringPtr username, FONAFlashStringPtr password) { this->apn = apn; this->apnusername = username; this->apnpassword = password; } boolean Adafruit_FONA::getGSMLoc(uint16_t *errorcode, char *buff, uint16_t maxlen) { getReply(F("AT+CIPGSMLOC=1,1"), (uint16_t)10000); if (! parseReply(F("+CIPGSMLOC: "), errorcode)) return false; char *p = replybuffer+14; uint16_t lentocopy = min(maxlen-1, (int)strlen(p)); strncpy(buff, p, lentocopy+1); readline(); // eat OK return true; } boolean Adafruit_FONA::getGSMLoc(float *lat, float *lon) { uint16_t returncode; char gpsbuffer[120]; // make sure we could get a response if (! getGSMLoc(&returncode, gpsbuffer, 120)) return false; // make sure we have a valid return code if (returncode != 0) return false; // +CIPGSMLOC: 0,-74.007729,40.730160,2015/10/15,19:24:55 // tokenize the gps buffer to locate the lat & long char *longp = strtok(gpsbuffer, ","); if (! longp) return false; char *latp = strtok(NULL, ","); if (! latp) return false; *lat = atof(latp); *lon = atof(longp); return true; } /********* TCP FUNCTIONS ************************************/ boolean Adafruit_FONA::TCPconnect(char *server, uint16_t port) { flushInput(); // close all old connections if (! sendCheckReply(F("AT+CIPSHUT"), F("SHUT OK"), 20000) ) return false; // single connection at a time if (! sendCheckReply(F("AT+CIPMUX=0"), ok_reply) ) return false; // manually read data if (! sendCheckReply(F("AT+CIPRXGET=1"), ok_reply) ) return false; DEBUG_PRINT(F("AT+CIPSTART=\"TCP\",\"")); DEBUG_PRINT(server); DEBUG_PRINT(F("\",\"")); DEBUG_PRINT(port); DEBUG_PRINTLN(F("\"")); mySerial->print(F("AT+CIPSTART=\"TCP\",\"")); mySerial->print(server); mySerial->print(F("\",\"")); mySerial->print(port); mySerial->println(F("\"")); if (! expectReply(ok_reply)) return false; if (! expectReply(F("CONNECT OK"))) return false; // looks like it was a success (?) return true; } boolean Adafruit_FONA::TCPclose(void) { return sendCheckReply(F("AT+CIPCLOSE"), ok_reply); } boolean Adafruit_FONA::TCPconnected(void) { if (! sendCheckReply(F("AT+CIPSTATUS"), ok_reply, 100) ) return false; readline(100); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return (strcmp(replybuffer, "STATE: CONNECT OK") == 0); } boolean Adafruit_FONA::TCPsend(char *packet, uint8_t len) { DEBUG_PRINT(F("AT+CIPSEND=")); DEBUG_PRINTLN(len); #ifdef ADAFRUIT_FONA_DEBUG for (uint16_t i=0; iprint(F("AT+CIPSEND=")); mySerial->println(len); readline(); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); if (replybuffer[0] != '>') return false; mySerial->write(packet, len); readline(3000); // wait up to 3 seconds to send the data DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return (strcmp(replybuffer, "SEND OK") == 0); } uint16_t Adafruit_FONA::TCPavailable(void) { uint16_t avail; if (! sendParseReply(F("AT+CIPRXGET=4"), F("+CIPRXGET: 4,"), &avail, ',', 0) ) return false; DEBUG_PRINT (avail); DEBUG_PRINTLN(F(" bytes available")); return avail; } uint16_t Adafruit_FONA::TCPread(uint8_t *buff, uint8_t len) { uint16_t avail; mySerial->print(F("AT+CIPRXGET=2,")); mySerial->println(len); readline(); if (! parseReply(F("+CIPRXGET: 2,"), &avail, ',', 0)) return false; readRaw(avail); #ifdef ADAFRUIT_FONA_DEBUG DEBUG_PRINT (avail); DEBUG_PRINTLN(F(" bytes read")); for (uint8_t i=0;i ")); DEBUG_PRINT(F("AT+HTTPPARA=\"")); DEBUG_PRINT(parameter); DEBUG_PRINTLN('"'); mySerial->print(F("AT+HTTPPARA=\"")); mySerial->print(parameter); if (quoted) mySerial->print(F("\",\"")); else mySerial->print(F("\",")); } boolean Adafruit_FONA::HTTP_para_end(boolean quoted) { if (quoted) mySerial->println('"'); else mySerial->println(); return expectReply(ok_reply); } boolean Adafruit_FONA::HTTP_para(FONAFlashStringPtr parameter, const char *value) { HTTP_para_start(parameter, true); mySerial->print(value); return HTTP_para_end(true); } boolean Adafruit_FONA::HTTP_para(FONAFlashStringPtr parameter, FONAFlashStringPtr value) { HTTP_para_start(parameter, true); mySerial->print(value); return HTTP_para_end(true); } boolean Adafruit_FONA::HTTP_para(FONAFlashStringPtr parameter, int32_t value) { HTTP_para_start(parameter, false); mySerial->print(value); return HTTP_para_end(false); } boolean Adafruit_FONA::HTTP_data(uint32_t size, uint32_t maxTime) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(F("AT+HTTPDATA=")); DEBUG_PRINT(size); DEBUG_PRINT(','); DEBUG_PRINTLN(maxTime); mySerial->print(F("AT+HTTPDATA=")); mySerial->print(size); mySerial->print(","); mySerial->println(maxTime); return expectReply(F("DOWNLOAD")); } boolean Adafruit_FONA::HTTP_action(uint8_t method, uint16_t *status, uint16_t *datalen, int32_t timeout) { // Send request. if (! sendCheckReply(F("AT+HTTPACTION="), method, ok_reply)) return false; // Parse response status and size. readline(timeout); if (! parseReply(F("+HTTPACTION:"), status, ',', 1)) return false; if (! parseReply(F("+HTTPACTION:"), datalen, ',', 2)) return false; return true; } boolean Adafruit_FONA::HTTP_readall(uint16_t *datalen) { getReply(F("AT+HTTPREAD")); if (! parseReply(F("+HTTPREAD:"), datalen, ',', 0)) return false; return true; } boolean Adafruit_FONA::HTTP_ssl(boolean onoff) { return sendCheckReply(F("AT+HTTPSSL="), onoff ? 1 : 0, ok_reply); } /********* HTTP HIGH LEVEL FUNCTIONS ***************************/ boolean Adafruit_FONA::HTTP_GET_start(char *url, uint16_t *status, uint16_t *datalen){ if (! HTTP_setup(url)) return false; // HTTP GET if (! HTTP_action(FONA_HTTP_GET, status, datalen, 30000)) return false; DEBUG_PRINT(F("Status: ")); DEBUG_PRINTLN(*status); DEBUG_PRINT(F("Len: ")); DEBUG_PRINTLN(*datalen); // HTTP response data if (! HTTP_readall(datalen)) return false; return true; } /* boolean Adafruit_FONA_3G::HTTP_GET_start(char *ipaddr, char *path, uint16_t port uint16_t *status, uint16_t *datalen){ char send[100] = "AT+CHTTPACT=\""; char *sendp = send + strlen(send); memset(sendp, 0, 100 - strlen(send)); strcpy(sendp, ipaddr); sendp+=strlen(ipaddr); sendp[0] = '\"'; sendp++; sendp[0] = ','; itoa(sendp, port); getReply(send, 500); return; if (! HTTP_setup(url)) return false; // HTTP GET if (! HTTP_action(FONA_HTTP_GET, status, datalen)) return false; DEBUG_PRINT("Status: "); DEBUG_PRINTLN(*status); DEBUG_PRINT("Len: "); DEBUG_PRINTLN(*datalen); // HTTP response data if (! HTTP_readall(datalen)) return false; return true; } */ void Adafruit_FONA::HTTP_GET_end(void) { HTTP_term(); } boolean Adafruit_FONA::HTTP_POST_start(char *url, FONAFlashStringPtr contenttype, const uint8_t *postdata, uint16_t postdatalen, uint16_t *status, uint16_t *datalen){ if (! HTTP_setup(url)) return false; if (! HTTP_para(F("CONTENT"), contenttype)) { return false; } // HTTP POST data if (! HTTP_data(postdatalen, 10000)) return false; mySerial->write(postdata, postdatalen); if (! expectReply(ok_reply)) return false; // HTTP POST if (! HTTP_action(FONA_HTTP_POST, status, datalen)) return false; DEBUG_PRINT(F("Status: ")); DEBUG_PRINTLN(*status); DEBUG_PRINT(F("Len: ")); DEBUG_PRINTLN(*datalen); // HTTP response data if (! HTTP_readall(datalen)) return false; return true; } void Adafruit_FONA::HTTP_POST_end(void) { HTTP_term(); } void Adafruit_FONA::setUserAgent(FONAFlashStringPtr useragent) { this->useragent = useragent; } void Adafruit_FONA::setHTTPSRedirect(boolean onoff) { httpsredirect = onoff; } /********* HTTP HELPERS ****************************************/ boolean Adafruit_FONA::HTTP_setup(char *url) { // Handle any pending HTTP_term(); // Initialize and set parameters if (! HTTP_init()) return false; if (! HTTP_para(F("CID"), 1)) return false; if (! HTTP_para(F("UA"), useragent)) return false; if (! HTTP_para(F("URL"), url)) return false; // HTTPS redirect if (httpsredirect) { if (! HTTP_para(F("REDIR"),1)) return false; if (! HTTP_ssl(true)) return false; } return true; } /********* HELPERS *********************************************/ boolean Adafruit_FONA::expectReply(FONAFlashStringPtr reply, uint16_t timeout) { readline(timeout); DEBUG_PRINT(F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); } /********* LOW LEVEL *******************************************/ inline int Adafruit_FONA::available(void) { return mySerial->available(); } inline size_t Adafruit_FONA::write(uint8_t x) { return mySerial->write(x); } inline int Adafruit_FONA::read(void) { return mySerial->read(); } inline int Adafruit_FONA::peek(void) { return mySerial->peek(); } inline void Adafruit_FONA::flush() { mySerial->flush(); } void Adafruit_FONA::flushInput() { // Read all available serial input to flush pending data. uint16_t timeoutloop = 0; while (timeoutloop++ < 40) { while(available()) { read(); timeoutloop = 0; // If char was received reset the timer } delay(1); } } uint16_t Adafruit_FONA::readRaw(uint16_t b) { uint16_t idx = 0; while (b && (idx < sizeof(replybuffer)-1)) { if (mySerial->available()) { replybuffer[idx] = mySerial->read(); idx++; b--; } } replybuffer[idx] = 0; return idx; } uint8_t Adafruit_FONA::readline(uint16_t timeout, boolean multiline) { uint16_t replyidx = 0; while (timeout--) { if (replyidx >= 254) { //DEBUG_PRINTLN(F("SPACE")); break; } while(mySerial->available()) { char c = mySerial->read(); if (c == '\r') continue; if (c == 0xA) { if (replyidx == 0) // the first 0x0A is ignored continue; if (!multiline) { timeout = 0; // the second 0x0A is the end of the line break; } } replybuffer[replyidx] = c; //DEBUG_PRINT(c, HEX); DEBUG_PRINT("#"); DEBUG_PRINTLN(c); replyidx++; } if (timeout == 0) { //DEBUG_PRINTLN(F("TIMEOUT")); break; } delay(1); } replybuffer[replyidx] = 0; // null term return replyidx; } uint8_t Adafruit_FONA::getReply(char *send, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINTLN(send); mySerial->println(send); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } uint8_t Adafruit_FONA::getReply(FONAFlashStringPtr send, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINTLN(send); mySerial->println(send); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } // Send prefix, suffix, and newline. Return response (and also set replybuffer with response). uint8_t Adafruit_FONA::getReply(FONAFlashStringPtr prefix, char *suffix, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(prefix); DEBUG_PRINTLN(suffix); mySerial->print(prefix); mySerial->println(suffix); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } // Send prefix, suffix, and newline. Return response (and also set replybuffer with response). uint8_t Adafruit_FONA::getReply(FONAFlashStringPtr prefix, int32_t suffix, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(prefix); DEBUG_PRINTLN(suffix, DEC); mySerial->print(prefix); mySerial->println(suffix, DEC); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } // Send prefix, suffix, suffix2, and newline. Return response (and also set replybuffer with response). uint8_t Adafruit_FONA::getReply(FONAFlashStringPtr prefix, int32_t suffix1, int32_t suffix2, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(prefix); DEBUG_PRINT(suffix1, DEC); DEBUG_PRINT(','); DEBUG_PRINTLN(suffix2, DEC); mySerial->print(prefix); mySerial->print(suffix1); mySerial->print(','); mySerial->println(suffix2, DEC); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } // Send prefix, ", suffix, ", and newline. Return response (and also set replybuffer with response). uint8_t Adafruit_FONA::getReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, uint16_t timeout) { flushInput(); DEBUG_PRINT(F("\t---> ")); DEBUG_PRINT(prefix); DEBUG_PRINT('"'); DEBUG_PRINT(suffix); DEBUG_PRINTLN('"'); mySerial->print(prefix); mySerial->print('"'); mySerial->print(suffix); mySerial->println('"'); uint8_t l = readline(timeout); DEBUG_PRINT (F("\t<--- ")); DEBUG_PRINTLN(replybuffer); return l; } boolean Adafruit_FONA::sendCheckReply(char *send, char *reply, uint16_t timeout) { if (! getReply(send, timeout) ) return false; /* for (uint8_t i=0; i