Library For the Adafruit FONA 808
Revision 0:8b6028668696, committed 2016-10-17
- Comitter:
- lewisdaw3
- Date:
- Mon Oct 17 10:51:58 2016 +0000
- Commit message:
- publish library
Changed in this revision
Adafruit_FONA.cpp | Show annotated file Show diff for this revision Revisions of this file |
Adafruit_FONA.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 8b6028668696 Adafruit_FONA.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_FONA.cpp Mon Oct 17 10:51:58 2016 +0000 @@ -0,0 +1,2082 @@ +/*************************************************** + 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" + + + + +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 "\""), 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, 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: "<incoming phone number>",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("\"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, 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, 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, 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","<apn value>" where <apn value> 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","<user>" where <user> 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","<password>" where <password> 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","<apn value>" where <apn value> 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, 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; i<len; i++) { + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINT(packet[i], HEX); + } +#endif + DEBUG_PRINTLN(); + + + mySerial->print(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<avail;i++) { + DEBUG_PRINT(F(" 0x")); DEBUG_PRINT(replybuffer[i], HEX); + } + DEBUG_PRINTLN(); +#endif + + memcpy(buff, replybuffer, avail); + + return avail; +} + + + +/********* HTTP LOW LEVEL FUNCTIONS ************************************/ + +boolean Adafruit_FONA::HTTP_init() { + return sendCheckReply(F("AT+HTTPINIT"), ok_reply); +} + +boolean Adafruit_FONA::HTTP_term() { + return sendCheckReply(F("AT+HTTPTERM"), ok_reply); +} + +void Adafruit_FONA::HTTP_para_start(FONAFlashStringPtr parameter, + boolean quoted) { + flushInput(); + + + DEBUG_PRINT(F("\t---> ")); + 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<strlen(replybuffer); i++) { + DEBUG_PRINT(replybuffer[i], HEX); DEBUG_PRINT(" "); + } + DEBUG_PRINTLN(); + for (uint8_t i=0; i<strlen(reply); i++) { + DEBUG_PRINT(reply[i], HEX); DEBUG_PRINT(" "); + } + DEBUG_PRINTLN(); + */ + return (strcmp(replybuffer, reply) == 0); +} + +boolean Adafruit_FONA::sendCheckReply(FONAFlashStringPtr send, FONAFlashStringPtr reply, uint16_t timeout) { + if (! getReply(send, timeout) ) + return false; + + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + +boolean Adafruit_FONA::sendCheckReply(char* send, FONAFlashStringPtr reply, uint16_t timeout) { + if (! getReply(send, timeout) ) + return false; + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + + +// Send prefix, suffix, and newline. Verify FONA response matches reply parameter. +boolean Adafruit_FONA::sendCheckReply(FONAFlashStringPtr prefix, char *suffix, FONAFlashStringPtr reply, uint16_t timeout) { + getReply(prefix, suffix, timeout); + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + +// Send prefix, suffix, and newline. Verify FONA response matches reply parameter. +boolean Adafruit_FONA::sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix, FONAFlashStringPtr reply, uint16_t timeout) { + getReply(prefix, suffix, timeout); + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + +// Send prefix, suffix, suffix2, and newline. Verify FONA response matches reply parameter. +boolean Adafruit_FONA::sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix1, int32_t suffix2, FONAFlashStringPtr reply, uint16_t timeout) { + getReply(prefix, suffix1, suffix2, timeout); + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + +// Send prefix, ", suffix, ", and newline. Verify FONA response matches reply parameter. +boolean Adafruit_FONA::sendCheckReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, FONAFlashStringPtr reply, uint16_t timeout) { + getReplyQuoted(prefix, suffix, timeout); + return (prog_char_strcmp(replybuffer, (prog_char*)reply) == 0); +} + + +boolean Adafruit_FONA::parseReply(FONAFlashStringPtr toreply, + uint16_t *v, char divider, uint8_t index) { + char *p = prog_char_strstr(replybuffer, (prog_char*)toreply); // get the pointer to the voltage + if (p == 0) return false; + p+=prog_char_strlen((prog_char*)toreply); + //DEBUG_PRINTLN(p); + for (uint8_t i=0; i<index;i++) { + // increment dividers + p = strchr(p, divider); + if (!p) return false; + p++; + //DEBUG_PRINTLN(p); + + } + *v = atoi(p); + + return true; +} + +boolean Adafruit_FONA::parseReply(FONAFlashStringPtr toreply, + char *v, char divider, uint8_t index) { + uint8_t i=0; + char *p = prog_char_strstr(replybuffer, (prog_char*)toreply); + if (p == 0) return false; + p+=prog_char_strlen((prog_char*)toreply); + + for (i=0; i<index;i++) { + // increment dividers + p = strchr(p, divider); + if (!p) return false; + p++; + } + + for(i=0; i<strlen(p);i++) { + if(p[i] == divider) + break; + v[i] = p[i]; + } + + v[i] = '\0'; + + return true; +} + +// Parse a quoted string in the response fields and copy its value (without quotes) +// to the specified character array (v). Only up to maxlen characters are copied +// into the result buffer, so make sure to pass a large enough buffer to handle the +// response. +boolean Adafruit_FONA::parseReplyQuoted(FONAFlashStringPtr toreply, + char *v, int maxlen, char divider, uint8_t index) { + uint8_t i=0, j; + // Verify response starts with toreply. + char *p = prog_char_strstr(replybuffer, (prog_char*)toreply); + if (p == 0) return false; + p+=prog_char_strlen((prog_char*)toreply); + + // Find location of desired response field. + for (i=0; i<index;i++) { + // increment dividers + p = strchr(p, divider); + if (!p) return false; + p++; + } + + // Copy characters from response field into result string. + for(i=0, j=0; j<maxlen && i<strlen(p); ++i) { + // Stop if a divier is found. + if(p[i] == divider) + break; + // Skip any quotation marks. + else if(p[i] == '"') + continue; + v[j++] = p[i]; + } + + // Add a null terminator if result string buffer was not filled. + if (j < maxlen) + v[j] = '\0'; + + return true; +} + +boolean Adafruit_FONA::sendParseReply(FONAFlashStringPtr tosend, + FONAFlashStringPtr toreply, + uint16_t *v, char divider, uint8_t index) { + getReply(tosend); + + if (! parseReply(toreply, v, divider, index)) return false; + + readline(); // eat 'OK' + + return true; +} + + +// needed for CBC and others + +boolean Adafruit_FONA_3G::sendParseReply(FONAFlashStringPtr tosend, + FONAFlashStringPtr toreply, + float *f, char divider, uint8_t index) { + getReply(tosend); + + if (! parseReply(toreply, f, divider, index)) return false; + + readline(); // eat 'OK' + + return true; +} + + +boolean Adafruit_FONA_3G::parseReply(FONAFlashStringPtr toreply, + float *f, char divider, uint8_t index) { + char *p = prog_char_strstr(replybuffer, (prog_char*)toreply); // get the pointer to the voltage + if (p == 0) return false; + p+=prog_char_strlen((prog_char*)toreply); + //DEBUG_PRINTLN(p); + for (uint8_t i=0; i<index;i++) { + // increment dividers + p = strchr(p, divider); + if (!p) return false; + p++; + //DEBUG_PRINTLN(p); + + } + *f = atof(p); + + return true; +}
diff -r 000000000000 -r 8b6028668696 Adafruit_FONA.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_FONA.h Mon Oct 17 10:51:58 2016 +0000 @@ -0,0 +1,263 @@ +/*************************************************** + 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 + ****************************************************/ +#ifndef ADAFRUIT_FONA_H +#define ADAFRUIT_FONA_H + +#include "includes/FONAConfig.h" +#include "includes/FONAExtIncludes.h" +#include "includes/platform/FONAPlatform.h" + + + +#define FONA800L 1 +#define FONA800H 6 + +#define FONA808_V1 2 +#define FONA808_V2 3 + +#define FONA3G_A 4 +#define FONA3G_E 5 + +// Uncomment to changed the preferred SMS storage +//#define FONA_PREF_SMS_STORAGE "SM" + +#define FONA_HEADSETAUDIO 0 +#define FONA_EXTAUDIO 1 + +#define FONA_STTONE_DIALTONE 1 +#define FONA_STTONE_BUSY 2 +#define FONA_STTONE_CONGESTION 3 +#define FONA_STTONE_PATHACK 4 +#define FONA_STTONE_DROPPED 5 +#define FONA_STTONE_ERROR 6 +#define FONA_STTONE_CALLWAIT 7 +#define FONA_STTONE_RINGING 8 +#define FONA_STTONE_BEEP 16 +#define FONA_STTONE_POSTONE 17 +#define FONA_STTONE_ERRTONE 18 +#define FONA_STTONE_INDIANDIALTONE 19 +#define FONA_STTONE_USADIALTONE 20 + +#define FONA_DEFAULT_TIMEOUT_MS 500 + +#define FONA_HTTP_GET 0 +#define FONA_HTTP_POST 1 +#define FONA_HTTP_HEAD 2 + +#define FONA_CALL_READY 0 +#define FONA_CALL_FAILED 1 +#define FONA_CALL_UNKNOWN 2 +#define FONA_CALL_RINGING 3 +#define FONA_CALL_INPROGRESS 4 + +class Adafruit_FONA : public FONAStreamType { + public: + Adafruit_FONA(int8_t r); + boolean begin(FONAStreamType &port); + uint8_t type(); + + // Stream + int available(void); + size_t write(uint8_t x); + int read(void); + int peek(void); + void flush(); + + // FONA 3G requirements + boolean setBaudrate(uint16_t baud); + + // RTC + boolean enableRTC(uint8_t i); + boolean readRTC(uint8_t *year, uint8_t *month, uint8_t *date, uint8_t *hr, uint8_t *min, uint8_t *sec); + + // Battery and ADC + boolean getADCVoltage(uint16_t *v); + boolean getBattPercent(uint16_t *p); + boolean getBattVoltage(uint16_t *v); + + // SIM query + uint8_t unlockSIM(char *pin); + uint8_t getSIMCCID(char *ccid); + uint8_t getNetworkStatus(void); + uint8_t getRSSI(void); + + // IMEI + uint8_t getIMEI(char *imei); + + // set Audio output + boolean setAudio(uint8_t a); + boolean setVolume(uint8_t i); + uint8_t getVolume(void); + boolean playToolkitTone(uint8_t t, uint16_t len); + boolean setMicVolume(uint8_t a, uint8_t level); + boolean playDTMF(char tone); + + // FM radio functions. + boolean tuneFMradio(uint16_t station); + boolean FMradio(boolean onoff, uint8_t a = FONA_HEADSETAUDIO); + boolean setFMVolume(uint8_t i); + int8_t getFMVolume(); + int8_t getFMSignalLevel(uint16_t station); + + // SMS handling + boolean setSMSInterrupt(uint8_t i); + uint8_t getSMSInterrupt(void); + int8_t getNumSMS(void); + boolean readSMS(uint8_t i, char *smsbuff, uint16_t max, uint16_t *readsize); + boolean sendSMS(char *smsaddr, char *smsmsg); + boolean deleteSMS(uint8_t i); + boolean getSMSSender(uint8_t i, char *sender, int senderlen); + boolean sendUSSD(char *ussdmsg, char *ussdbuff, uint16_t maxlen, uint16_t *readlen); + + // Time + boolean enableNetworkTimeSync(boolean onoff); + boolean enableNTPTimeSync(boolean onoff, FONAFlashStringPtr ntpserver=0); + boolean getTime(char *buff, uint16_t maxlen); + + // GPRS handling + boolean enableGPRS(boolean onoff); + uint8_t GPRSstate(void); + boolean getGSMLoc(uint16_t *replycode, char *buff, uint16_t maxlen); + boolean getGSMLoc(float *lat, float *lon); + void setGPRSNetworkSettings(FONAFlashStringPtr apn, FONAFlashStringPtr username=0, FONAFlashStringPtr password=0); + + // GPS handling + boolean enableGPS(boolean onoff); + int8_t GPSstatus(void); + uint8_t getGPS(uint8_t arg, char *buffer, uint8_t maxbuff); + boolean getGPS(float *lat, float *lon, float *speed_kph=0, float *heading=0, float *altitude=0); + boolean enableGPSNMEA(uint8_t nmea); + + // TCP raw connections + boolean TCPconnect(char *server, uint16_t port); + boolean TCPclose(void); + boolean TCPconnected(void); + boolean TCPsend(char *packet, uint8_t len); + uint16_t TCPavailable(void); + uint16_t TCPread(uint8_t *buff, uint8_t len); + + // HTTP low level interface (maps directly to SIM800 commands). + boolean HTTP_init(); + boolean HTTP_term(); + void HTTP_para_start(FONAFlashStringPtr parameter, boolean quoted = true); + boolean HTTP_para_end(boolean quoted = true); + boolean HTTP_para(FONAFlashStringPtr parameter, const char *value); + boolean HTTP_para(FONAFlashStringPtr parameter, FONAFlashStringPtr value); + boolean HTTP_para(FONAFlashStringPtr parameter, int32_t value); + boolean HTTP_data(uint32_t size, uint32_t maxTime=10000); + boolean HTTP_action(uint8_t method, uint16_t *status, uint16_t *datalen, int32_t timeout = 10000); + boolean HTTP_readall(uint16_t *datalen); + boolean HTTP_ssl(boolean onoff); + + // HTTP high level interface (easier to use, less flexible). + boolean HTTP_GET_start(char *url, uint16_t *status, uint16_t *datalen); + void HTTP_GET_end(void); + boolean HTTP_POST_start(char *url, FONAFlashStringPtr contenttype, const uint8_t *postdata, uint16_t postdatalen, uint16_t *status, uint16_t *datalen); + void HTTP_POST_end(void); + void setUserAgent(FONAFlashStringPtr useragent); + + // HTTPS + void setHTTPSRedirect(boolean onoff); + + // PWM (buzzer) + boolean setPWM(uint16_t period, uint8_t duty = 50); + + // Phone calls + boolean callPhone(char *phonenum); + uint8_t getCallStatus(void); + boolean hangUp(void); + boolean pickUp(void); + boolean callerIdNotification(boolean enable, uint8_t interrupt = 0); + boolean incomingCallNumber(char* phonenum); + + // Helper functions to verify responses. + boolean expectReply(FONAFlashStringPtr reply, uint16_t timeout = 10000); + boolean sendCheckReply(char *send, char *reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + boolean sendCheckReply(FONAFlashStringPtr send, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + boolean sendCheckReply(char* send, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + + + protected: + int8_t _rstpin; + uint8_t _type; + + char replybuffer[255]; + FONAFlashStringPtr apn; + FONAFlashStringPtr apnusername; + FONAFlashStringPtr apnpassword; + boolean httpsredirect; + FONAFlashStringPtr useragent; + FONAFlashStringPtr ok_reply; + + // HTTP helpers + boolean HTTP_setup(char *url); + + void flushInput(); + uint16_t readRaw(uint16_t b); + uint8_t readline(uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS, boolean multiline = false); + uint8_t getReply(char *send, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + uint8_t getReply(FONAFlashStringPtr send, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + uint8_t getReply(FONAFlashStringPtr prefix, char *suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + uint8_t getReply(FONAFlashStringPtr prefix, int32_t suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + uint8_t getReply(FONAFlashStringPtr prefix, int32_t suffix1, int32_t suffix2, uint16_t timeout); // Don't set default value or else function call is ambiguous. + uint8_t getReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + + boolean sendCheckReply(FONAFlashStringPtr prefix, char *suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + boolean sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + boolean sendCheckReply(FONAFlashStringPtr prefix, int32_t suffix, int32_t suffix2, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + boolean sendCheckReplyQuoted(FONAFlashStringPtr prefix, FONAFlashStringPtr suffix, FONAFlashStringPtr reply, uint16_t timeout = FONA_DEFAULT_TIMEOUT_MS); + + + boolean parseReply(FONAFlashStringPtr toreply, + uint16_t *v, char divider = ',', uint8_t index=0); + boolean parseReply(FONAFlashStringPtr toreply, + char *v, char divider = ',', uint8_t index=0); + boolean parseReplyQuoted(FONAFlashStringPtr toreply, + char *v, int maxlen, char divider, uint8_t index); + + boolean sendParseReply(FONAFlashStringPtr tosend, + FONAFlashStringPtr toreply, + uint16_t *v, char divider = ',', uint8_t index=0); + + static boolean _incomingCall; + static void onIncomingCall(); + + FONAStreamType *mySerial; +}; + +class Adafruit_FONA_3G : public Adafruit_FONA { + + public: + Adafruit_FONA_3G (int8_t r) : Adafruit_FONA(r) { _type = FONA3G_A; } + + boolean getBattVoltage(uint16_t *v); + boolean playToolkitTone(uint8_t t, uint16_t len); + boolean hangUp(void); + boolean pickUp(void); + boolean enableGPRS(boolean onoff); + boolean enableGPS(boolean onoff); + + protected: + boolean parseReply(FONAFlashStringPtr toreply, + float *f, char divider, uint8_t index); + + boolean sendParseReply(FONAFlashStringPtr tosend, + FONAFlashStringPtr toreply, + float *f, char divider = ',', uint8_t index=0); +}; + +#endif