Based on https://developer.mbed.org/users/jbaker66/notebook/adafruit-fona-800-minigsm/
Diff: Adafruit_FONA.cpp
- Revision:
- 0:58ff0234d7bb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_FONA.cpp Wed May 03 21:05:54 2017 +0000 @@ -0,0 +1,1447 @@ +/*************************************************** + 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 + ****************************************************/ + + /* + * Modified by George Tzintzarov & Jesse Baker 03/14/2016 for use in mbed LPC1768 + */ +#include <algorithm> +#include "Adafruit_FONA.h" + +#define HIGH 1 +#define LOW 0 + +/* +Notes George is taking: +sendCheckReply returns a boolean where its true when FONA responds with the string of its second input + +*/ + + +bool Adafruit_FONA::begin(int baudrate) { + mySerial.baud(baudrate); //set the baud rate of the fona serial connection + mySerial.attach(this, &Adafruit_FONA::onSerialDataReceived, Serial::RxIrq); //attached onSerialDataReceived as Serial receive interrupt + + // INIT Reboot process + _rstpin = HIGH; + wait_ms(10); + _rstpin = LOW; + wait_ms(100); + _rstpin = HIGH; + + // give 3 seconds to reboot + wait_ms(3000); + + // flushes the serial port + while (readable()) getc(); + + // Make sure FONA is alive + sendCheckReply("AT", "OK"); + wait_ms(100); + sendCheckReply("AT", "OK"); + wait_ms(100); + sendCheckReply("AT", "OK"); + wait_ms(100); + + // turn off Echo! + sendCheckReply("ATE0", "OK"); + wait_ms(100); + + // Just checks if the FONA even responds, if it doesnt, then return false + if (! sendCheckReply("ATE0", "OK")) { + return false; + } + + return true; +} + +void Adafruit_FONA::setEventListener(EventListener *eventListener) { + this->eventListener = eventListener; +} + +/********* Stream ********************************************/ + +int Adafruit_FONA::_putc(int value) { + return mySerial.putc(value); +} + +int Adafruit_FONA::_getc() { + __disable_irq(); // Start Critical Section - don't interrupt while changing global buffer variables + + // Wait for data if the buffer is empty + if (isRxBufferEmpty()) { + __enable_irq(); // End Critical Section - need to allow rx interrupt to get new characters for buffer + + while(isRxBufferEmpty()); + + __disable_irq(); // Start Critical Section - don't interrupt while changing global buffer variables + } + + int data = rxBuffer[rxBufferOutIndex]; + incrementRxBufferOutIndex(); + + __enable_irq(); // End Critical Section + + return data; +} + +int Adafruit_FONA::readable() { + return !isRxBufferEmpty(); +} + +void Adafruit_FONA::onSerialDataReceived() { + while (mySerial.readable() && !isRxBufferFull()) { + int data = mySerial.getc(); + rxBuffer[rxBufferInIndex] = data; + + // + // Analyze the received data in order to detect events like RING or NO CARRIER + // + + // Copy the data in the current line + if (currentReceivedLineSize < RX_BUFFER_SIZE && data != '\r' && data != '\n') { + currentReceivedLine[currentReceivedLineSize] = (char) data; + currentReceivedLineSize++; + } + + // Check if the line is complete + if (data == '\n') { + currentReceivedLine[currentReceivedLineSize] = 0; + + if (eventListener != NULL) { + // Check if we have a special event + if (strcmp(currentReceivedLine, "RING") == 0) { + eventListener->onRing(); + } else if (strcmp(currentReceivedLine, "NO CARRIER") == 0) { + eventListener->onNoCarrier(); + } + } + + currentReceivedLineSize = 0; + } + + incrementRxBufferInIndex(); + } +} + +/********* Real Time Clock ********************************************/ + +bool Adafruit_FONA::enableRTC(uint8_t i) { + if (! sendCheckReply("AT+CLTS=", i, "OK")) + return false; + return sendCheckReply("AT&W", "OK"); +} + +/********* BATTERY & ADC ********************************************/ + +/* returns value in mV (uint16_t) */ +bool Adafruit_FONA::getBattVoltage(uint16_t *v) { + return sendParseReply("AT+CBC", "+CBC: ", v, ',', 2); +} + +/* returns the percentage charge of battery as reported by sim800 */ +bool Adafruit_FONA::getBattPercent(uint16_t *p) { + return sendParseReply("AT+CBC", "+CBC: ", p, ',', 1); +} + +bool Adafruit_FONA::getADCVoltage(uint16_t *v) { + return sendParseReply("AT+CADC?", "+CADC: 1,", v); +} + +/********* SIM ***********************************************************/ + +bool 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] = NULL; + + return sendCheckReply(sendbuff, "OK"); +} + +uint8_t Adafruit_FONA::getSIMCCID(char *ccid) { + getReply("AT+CCID"); + // up to 20 chars + strncpy(ccid, replybuffer, 20); + ccid[20] = 0; + + readline(); // eat 'OK' + + return strlen(ccid); +} + +/********* IMEI **********************************************************/ + +uint8_t Adafruit_FONA::getIMEI(char *imei) { + getReply("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("AT+CREG?", "+CREG: ", &status, ',', 1)) return 0; + + return status; +} + + +uint8_t Adafruit_FONA::getRSSI(void) { + uint16_t reply; + + if (! sendParseReply("AT+CSQ", "+CSQ: ", &reply) ) return 0; + + return reply; +} + +/********* AUDIO *******************************************************/ + +bool Adafruit_FONA::setAudio(uint8_t a) { + // 0 is headset, 1 is external audio + if (a > 1) return false; + + return sendCheckReply("AT+CHFA=", a, "OK"); +} + +uint8_t Adafruit_FONA::getVolume(void) { + uint16_t reply; + + if (! sendParseReply("AT+CLVL?", "+CLVL: ", &reply) ) return 0; + + return reply; +} + +bool Adafruit_FONA::setVolume(uint8_t i) { + return sendCheckReply("AT+CLVL=", i, "OK"); +} + + +bool Adafruit_FONA::playDTMF(char dtmf) { + char str[4]; + str[0] = '\"'; + str[1] = dtmf; + str[2] = '\"'; + str[3] = 0; + return sendCheckReply("AT+CLDTMF=3,", str, "OK"); +} + +bool Adafruit_FONA::playToolkitTone(uint8_t t, uint16_t len) { + return sendCheckReply("AT+STTONE=1,", t, len, "OK"); +} + +bool Adafruit_FONA::setMicVolume(uint8_t a, uint8_t level) { + // 0 is headset, 1 is external audio + if (a > 1) return false; + + return sendCheckReply("AT+CMIC=", a, level, "OK"); +} + +/********* FM RADIO *******************************************************/ + + +bool Adafruit_FONA::FMradio(bool onoff, uint8_t a) { + if (! onoff) { + return sendCheckReply("AT+FMCLOSE", "OK"); + } + + // 0 is headset, 1 is external audio + if (a > 1) return false; + + return sendCheckReply("AT+FMOPEN=", a, "OK"); +} + +bool Adafruit_FONA::tuneFMradio(uint16_t station) { + // Fail if FM station is outside allowed range. + if ((station < 870) || (station > 1090)) + return false; + + return sendCheckReply("AT+FMFREQ=", station, "OK"); +} + +bool 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("AT+FMVOLUME=", i, "OK"); +} + +int8_t Adafruit_FONA::getFMVolume() { + uint16_t level; + + if (! sendParseReply("AT+FMVOLUME?", "+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("AT+FMSIGNAL=", station, FONA_DEFAULT_TIMEOUT_MS); + // Check response starts with expected value. + char *p = strstr(replybuffer, "+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 **************************************************/ + +bool Adafruit_FONA::setPWM(uint16_t period, uint8_t duty) { + if (period > 2000) return false; + if (duty > 100) return false; + + return sendCheckReply("AT+SPWM=0,", period, duty, "OK"); +} + +/********* CALL PHONES **************************************************/ +bool Adafruit_FONA::callPhone(char *number) { + char sendbuff[35] = "ATD"; + strncpy(sendbuff+3, number, min((int)30, (int)strlen(number))); + uint8_t x = strlen(sendbuff); + sendbuff[x] = ';'; + sendbuff[x+1] = 0; + + return sendCheckReply(sendbuff, "OK"); +} + +bool Adafruit_FONA::hangUp(void) { + return sendCheckReply("ATH0", "OK"); +} + +bool Adafruit_FONA::pickUp(void) { + return sendCheckReply("ATA", "OK"); +} + +void Adafruit_FONA::onIncomingCall() { +#ifdef ADAFRUIT_FONA_DEBUG + printf("> Incoming call...\r\n"); +#endif + _incomingCall = true; +} + +bool Adafruit_FONA::callerIdNotification(bool enable) { + if(enable){ + _ringIndicatorInterruptIn.fall(this, &Adafruit_FONA::onIncomingCall); + return sendCheckReply("AT+CLIP=1", "OK"); + } + + _ringIndicatorInterruptIn.fall(NULL); + return sendCheckReply("AT+CLIP=0", "OK"); +} + +bool Adafruit_FONA::incomingCallNumber(char* phonenum) { + //+CLIP: "<incoming phone number>",145,"",0,"",0 + if(!_incomingCall) + return false; + + readline(); + while(!strcmp(replybuffer, "RING") == 0) { + flushInput(); + readline(); + } + + readline(); //reads incoming phone number line + + parseReply("+CLIP: \"", phonenum, '"'); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("Phone Number: %s\r\n", replybuffer); +#endif + + _incomingCall = false; + return true; +} + +/********* SMS **********************************************************/ + +uint8_t Adafruit_FONA::getSMSInterrupt(void) { + uint16_t reply; + + if (! sendParseReply("AT+CFGRI?", "+CFGRI: ", &reply) ) return 0; + + return reply; +} + +bool Adafruit_FONA::setSMSInterrupt(uint8_t i) { + return sendCheckReply("AT+CFGRI=", i, "OK"); +} + +int8_t Adafruit_FONA::getNumSMS(void) { + uint16_t numsms; + + if (! sendCheckReply("AT+CMGF=1", "OK")) return -1; + // ask how many sms are stored + + if (! sendParseReply("AT+CPMS?", "+CPMS: \"SM_P\",", &numsms) ) return -1; + + return numsms; +} + +// Reading SMS's is a bit involved so we don't use helpers that may cause delays or debug +// printouts! +bool Adafruit_FONA::readSMS(uint8_t i, char *smsbuff, uint16_t maxlen, uint16_t *readlen) { + // text mode + if (! sendCheckReply("AT+CMGF=1", "OK")) return false; + + // show all text mode parameters + if (! sendCheckReply("AT+CSDH=1", "OK")) return false; + + // parse out the SMS len + uint16_t thesmslen = 0; + + //getReply(F("AT+CMGR="), i, 1000); // do not print debug! + mySerial.printf("AT+CMGR=%d\r\n", i); + readline(1000); // timeout + + // parse it out... + if (! parseReply("+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 + +#ifdef ADAFRUIT_FONA_DEBUG + printf("%s\r\n", replybuffer); +#endif + *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. +bool Adafruit_FONA::getSMSSender(uint8_t i, char *sender, int senderlen) { + // Ensure text mode and all text mode parameters are sent. + if (! sendCheckReply("AT+CMGF=1", "OK")) return false; + if (! sendCheckReply("AT+CSDH=1", "OK")) return false; + // Send command to retrieve SMS message and parse a line of response. + mySerial.printf("AT+CMGR=%d\r\n", i); + readline(1000); + // Parse the second field in the response. + bool result = parseReplyQuoted("+CMGR:", sender, senderlen, ',', 1); + // Drop any remaining data from the response. + flushInput(); + return result; +} + +bool Adafruit_FONA::sendSMS(char *smsaddr, char *smsmsg) { + if (! sendCheckReply("AT+CMGF=1", "OK")) return -1; + + 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, "> ")) return false; +#ifdef ADAFRUIT_FONA_DEBUG + printf("> %s\r\n", smsmsg); +#endif + mySerial.printf("%s\r\n\r\n", smsmsg); + mySerial.putc(0x1A); +#ifdef ADAFRUIT_FONA_DEBUG + printf("^Z\r\n"); +#endif + readline(10000); // read the +CMGS reply, wait up to 10 seconds!!! + //Serial.print("* "); Serial.println(replybuffer); + if (strstr(replybuffer, "+CMGS") == 0) { + return false; + } + readline(1000); // read OK + //Serial.print("* "); Serial.println(replybuffer); + + if (strcmp(replybuffer, "OK") != 0) { + return false; + } + + return true; +} + + +bool Adafruit_FONA::deleteSMS(uint8_t i) { + if (! sendCheckReply("AT+CMGF=1", "OK")) return -1; + // 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", 2000); +} + +/********* TIME **********************************************************/ + +bool Adafruit_FONA::enableNetworkTimeSync(bool onoff) { + if (onoff) { + if (! sendCheckReply("AT+CLTS=1", "OK")) + return false; + } else { + if (! sendCheckReply("AT+CLTS=0", "OK")) + return false; + } + + flushInput(); // eat any 'Unsolicted Result Code' + + return true; +} + +bool Adafruit_FONA::enableNTPTimeSync(bool onoff, const char* ntpserver) { + if (onoff) { + if (! sendCheckReply("AT+CNTPCID=1", "OK")) + return false; + + mySerial.printf("AT+CNTP=\""); + if (ntpserver != 0) { + mySerial.printf(ntpserver); + } else { + mySerial.printf("pool.ntp.org"); + } + mySerial.printf("\",0\r\n"); + readline(FONA_DEFAULT_TIMEOUT_MS); + if (strcmp(replybuffer, "OK") != 0) + return false; + + if (! sendCheckReply("AT+CNTP", "OK", 10000)) + return false; + + uint16_t status; + readline(10000); + if (! parseReply("+CNTP:", &status)) + return false; + } else { + if (! sendCheckReply("AT+CNTPCID=0", "OK")) + return false; + } + + return true; +} + +bool Adafruit_FONA::getTime(char* buff, uint16_t maxlen) { + getReply("AT+CCLK?", (uint16_t) 10000); + if (strncmp(replybuffer, "+CCLK: ", 7) != 0) + return false; + + char *p = replybuffer+7; + uint16_t lentocopy = min((uint16_t)(maxlen-1), (uint16_t)strlen(p)); + strncpy(buff, p, lentocopy+1); + buff[lentocopy] = 0; + + readline(); // eat OK + + return true; +} + +/********* GPS **********************************************************/ + + +bool Adafruit_FONA::enableGPS(bool onoff) { + uint16_t state; + + // first check if its already on or off + if (! sendParseReply("AT+CGPSPWR?", "+CGPSPWR: ", &state) ) + return false; + + if (onoff && !state) { + if (! sendCheckReply("AT+CGPSPWR=1", "OK")) + return false; + } else if (!onoff && state) { + if (! sendCheckReply("AT+CGPSPWR=0", "OK")) + return false; + } + return true; +} + +int8_t Adafruit_FONA::GPSstatus(void) { + getReply("AT+CGPSSTATUS?"); + + char *p = strstr(replybuffer, "+CGPSSTATUS: Location "); + if (p == 0) return -1; + + p+=22; + + readline(); // eat 'OK' + + + 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; + + getReply("AT+CGPSINF=", x); + + char *p = strstr(replybuffer, "CGPSINF: "); + if (p == 0){ + buffer[0] = 0; + return 0; + } + p+=9; + uint8_t len = max((uint8_t)(maxbuff-1), (uint8_t)strlen(p)); + strncpy(buffer, p, len); + buffer[len] = 0; + + readline(); // eat 'OK' + return len; +} + +bool 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; + + // 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; +} + +bool 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'; + + return sendCheckReply(sendbuff, "OK", 2000); +} + + +/********* GPRS **********************************************************/ + + +bool Adafruit_FONA::enableGPRS(bool onoff) { + if (onoff) { + // disconnect all sockets + sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000); + + if (! sendCheckReply("AT+CGATT=1", "OK", 10000)) + return false; + + // set bearer profile! connection type GPRS + if (! sendCheckReply("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", "OK", 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("AT+SAPBR=3,1,\"APN\",", apn, "OK", 10000)) + return false; + + // set username/password + if (apnusername) { + // Send command AT+SAPBR=3,1,"USER","<user>" where <user> is the configured APN username. + if (! sendCheckReplyQuoted("AT+SAPBR=3,1,\"USER\",", apnusername, "OK", 10000)) + return false; + } + if (apnpassword) { + // Send command AT+SAPBR=3,1,"PWD","<password>" where <password> is the configured APN password. + if (! sendCheckReplyQuoted("AT+SAPBR=3,1,\"PWD\",", apnpassword, "OK", 10000)) + return false; + } + } + + // open GPRS context + if (! sendCheckReply("AT+SAPBR=1,1", "OK", 10000)) + return false; + } else { + // disconnect all sockets + if (! sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000)) + return false; + + // close GPRS context + if (! sendCheckReply("AT+SAPBR=0,1", "OK", 10000)) + return false; + + if (! sendCheckReply("AT+CGATT=0", "OK", 10000)) + return false; + } + return true; +} + +uint8_t Adafruit_FONA::GPRSstate(void) { + uint16_t state; + + if (! sendParseReply("AT+CGATT?", "+CGATT: ", &state) ) + return -1; + + return state; +} + +void Adafruit_FONA::setGPRSNetworkSettings(const char* apn, const char* ausername, const char* apassword) { + this->apn = (char*) apn; + this->apnusername = (char*) ausername; + this->apnpassword = (char*) apassword; +} + +bool Adafruit_FONA::getGSMLoc(uint16_t *errorcode, char *buff, uint16_t maxlen) { + getReply("AT+CIPGSMLOC=1,1", (uint16_t)10000); + + if (! parseReply("+CIPGSMLOC: ", errorcode)) + return false; + + char *p = replybuffer+14; + uint16_t lentocopy = min((uint16_t)(maxlen-1), (uint16_t)strlen(p)); + strncpy(buff, p, lentocopy+1); + + readline(); // eat OK + + return true; +} + +bool 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; + + // tokenize the gps buffer to locate the lat & long + char *latp = strtok(gpsbuffer, ","); + if (! latp) return false; + + char *longp = strtok(NULL, ","); + if (! longp) return false; + + *lat = atof(latp); + *lon = atof(longp); + + return true; +} + +/********* TCP FUNCTIONS ************************************/ + + +bool Adafruit_FONA::TCPconnect(char *server, uint16_t port) { + flushInput(); + + // close all old connections + if (! sendCheckReply("AT+CIPSHUT", "SHUT OK", 5000) ) return false; + + // single connection at a time + if (! sendCheckReply("AT+CIPMUX=0", "OK") ) return false; + + // manually read data + if (! sendCheckReply("AT+CIPRXGET=1", "OK") ) return false; + +#ifdef ADAFRUIT_FONA_DEBUG + printf("AT+CIPSTART=\"TCP\",\"%s\",\"%d\"\r\n", server, port); +#endif + + mySerial.printf("AT+CIPSTART=\"TCP\",\"%s\",\"%d\"\r\n", server, port); + + if (! expectReply("OK")) return false; + if (! expectReply("CONNECT OK")) return false; + return true; +} + +bool Adafruit_FONA::TCPclose(void) { + return sendCheckReply("AT+CIPCLOSE", "OK"); +} + +bool Adafruit_FONA::TCPconnected(void) { + if (! sendCheckReply("AT+CIPSTATUS", "OK", 100) ) return false; + readline(100); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return (strcmp(replybuffer, "STATE: CONNECT OK") == 0); +} + +bool Adafruit_FONA::TCPsend(char *packet, uint8_t len) { +#ifdef ADAFRUIT_FONA_DEBUG + printf("AT+CIPSEND=%d\r\n", len); + + for (uint16_t i=0; i<len; i++) { + printf(" 0x%#02x", packet[i]); + } + printf("\r\n"); +#endif + + + mySerial.printf("AT+CIPSEND=%d\r\n", len); + readline(); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + if (replybuffer[0] != '>') return false; + + for (uint16_t i=0; i<len; i++) { + mySerial.putc(packet[i]); + } + readline(3000); // wait up to 3 seconds to send the data +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + + return (strcmp(replybuffer, "SEND OK") == 0); +} + +uint16_t Adafruit_FONA::TCPavailable(void) { + uint16_t avail; + + if (! sendParseReply("AT+CIPRXGET=4", "+CIPRXGET: 4,", &avail, ',', 0) ) return false; + +#ifdef ADAFRUIT_FONA_DEBUG + printf("%d bytes available\r\n", avail); +#endif + + return avail; +} + + +uint16_t Adafruit_FONA::TCPread(uint8_t *buff, uint8_t len) { + uint16_t avail; + + mySerial.printf("AT+CIPRXGET=2,%d\r\n", len); + readline(); + if (! parseReply("+CIPRXGET: 2,", &avail, ',', 0)) return false; + + readRaw(avail); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("%d bytes read\r\n", avail); + for (uint8_t i=0;i<avail;i++) { + printf(" 0x%#02x", replybuffer[i]); + } + printf("\r\n"); +#endif + + memcpy(buff, replybuffer, avail); + + return avail; +} + +/********* HTTP LOW LEVEL FUNCTIONS ************************************/ + +bool Adafruit_FONA::HTTP_init() { + return sendCheckReply("AT+HTTPINIT", "OK"); +} + +bool Adafruit_FONA::HTTP_term() { + return sendCheckReply("AT+HTTPTERM", "OK"); +} + +void Adafruit_FONA::HTTP_para_start(const char* parameter, bool quoted) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> AT+HTTPPARA=\"%s\"\r\n", parameter); +#endif + + mySerial.printf("AT+HTTPPARA=\"%s", parameter); + if (quoted) + mySerial.printf("\",\""); + else + mySerial.printf("\","); +} + +bool Adafruit_FONA::HTTP_para_end(bool quoted) { + if (quoted) + mySerial.printf("\"\r\n"); + else + mySerial.printf("\r\n"); + + return expectReply("OK"); +} + +bool Adafruit_FONA::HTTP_para(const char* parameter, const char* value) { + HTTP_para_start(parameter, true); + mySerial.printf(value); + return HTTP_para_end(true); +} + +bool Adafruit_FONA::HTTP_para(const char* parameter, int32_t value) { + HTTP_para_start(parameter, false); + mySerial.printf("%d", value); + return HTTP_para_end(false); +} + +bool Adafruit_FONA::HTTP_data(uint32_t size, uint32_t maxTime) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> AT+HTTPDATA=%d,%d\r\n", size, maxTime); +#endif + + mySerial.printf("AT+HTTPDATA=%d,%d\r\n", size, maxTime); + + return expectReply("DOWNLOAD"); +} + +bool Adafruit_FONA::HTTP_action(uint8_t method, uint16_t *status, uint16_t *datalen, int32_t timeout) { + // Send request. + if (! sendCheckReply("AT+HTTPACTION=", method, "OK")) + return false; + + // Parse response status and size. + readline(timeout); + if (! parseReply("+HTTPACTION:", status, ',', 1)) + return false; + if (! parseReply("+HTTPACTION:", datalen, ',', 2)) + return false; + + return true; +} + +bool Adafruit_FONA::HTTP_readall(uint16_t *datalen) { + getReply("AT+HTTPREAD"); + if (! parseReply("+HTTPREAD:", datalen, ',', 0)) + return false; + + return true; +} + +bool Adafruit_FONA::HTTP_ssl(bool onoff) { + return sendCheckReply("AT+HTTPSSL=", onoff ? 1 : 0, "OK"); +} + +/********* HTTP HIGH LEVEL FUNCTIONS ***************************/ + +bool 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)) + return false; + +#ifdef ADAFRUIT_FONA_DEBUG + printf("Status: %d\r\n", *status); + printf("Len: %d\r\n", *datalen); +#endif + + // HTTP response data + if (! HTTP_readall(datalen)) + return false; + + return true; +} + +void Adafruit_FONA::HTTP_GET_end(void) { + HTTP_term(); +} + +bool Adafruit_FONA::HTTP_POST_start(char *url, const char* contenttype, const uint8_t *postdata, uint16_t postdatalen, uint16_t *status, uint16_t *datalen) { + if (! HTTP_setup(url)) + return false; + + if (! HTTP_para("CONTENT", contenttype)) { + return false; + } + + // HTTP POST data + if (! HTTP_data(postdatalen, 10000)) + return false; + for (uint16_t i = 0; i < postdatalen; i++) { + mySerial.putc(postdata[i]); + } + if (! expectReply("OK")) + return false; + + // HTTP POST + if (! HTTP_action(FONA_HTTP_POST, status, datalen)) + return false; + +#ifdef ADAFRUIT_FONA_DEBUG + printf("Status: %d\r\n", *status); + printf("Len: %d\r\n", *datalen); +#endif + + // HTTP response data + if (! HTTP_readall(datalen)) + return false; + + return true; +} + +void Adafruit_FONA::HTTP_POST_end(void) { + HTTP_term(); +} + +void Adafruit_FONA::setUserAgent(const char* useragent) { + this->useragent = (char*) useragent; +} + +void Adafruit_FONA::setHTTPSRedirect(bool onoff) { + httpsredirect = onoff; +} + +/********* HTTP HELPERS ****************************************/ + +bool Adafruit_FONA::HTTP_setup(char *url) { + // Handle any pending + HTTP_term(); + + // Initialize and set parameters + if (! HTTP_init()) + return false; + if (! HTTP_para("CID", 1)) + return false; + if (! HTTP_para("UA", useragent)) + return false; + if (! HTTP_para("URL", url)) + return false; + + // HTTPS redirect + if (httpsredirect) { + if (! HTTP_para("REDIR",1)) + return false; + + if (! HTTP_ssl(true)) + return false; + } + + return true; +} + + +/********* HELPERS *********************************************/ + +bool Adafruit_FONA::expectReply(const char* reply, uint16_t timeout) { + readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return (strcmp(replybuffer, reply) == 0); +} + +/********* LOW LEVEL *******************************************/ + +void Adafruit_FONA::flushInput() { + // Read all available serial input to flush pending data. + uint16_t timeoutloop = 0; + while (timeoutloop++ < 40) { + while(readable()) { + getc(); + timeoutloop = 0; // If char was received reset the timer + } + wait_ms(1); + } +} + +uint16_t Adafruit_FONA::readRaw(uint16_t b) { + uint16_t idx = 0; + + while (b && (idx < sizeof(replybuffer)-1)) { + if (readable()) { + replybuffer[idx] = getc(); + idx++; + b--; + } + } + replybuffer[idx] = 0; + + return idx; +} + +// This function just reads the output from FONA after an AT command is sent to it +uint8_t Adafruit_FONA::readline(uint16_t timeout, bool multiline) { + uint16_t replyidx = 0; + + while (timeout--) { + if (replyidx >= 254) { + break; + } + + while(readable()) { + char c = getc(); + 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; + replyidx++; + } + + if (timeout == 0) { + break; + } + wait_ms(1); + } + replybuffer[replyidx] = 0; // null term + return replyidx; +} + +uint8_t Adafruit_FONA::getReply(const char* send, uint16_t timeout) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> %s\r\n", send); +#endif + + mySerial.printf("%s\r\n",send); + + uint8_t l = readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return l; +} + +// Send prefix, suffix, and newline. Return response (and also set replybuffer with response). +uint8_t Adafruit_FONA::getReply(const char* prefix, char* suffix, uint16_t timeout) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> %s%s\r\n", prefix, suffix); +#endif + + mySerial.printf("%s%s\r\n", prefix, suffix); + + uint8_t l = readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return l; +} + +// Send prefix, suffix, and newline. Return response (and also set replybuffer with response). +uint8_t Adafruit_FONA::getReply(const char* prefix, int32_t suffix, uint16_t timeout) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> %s%d\r\n", prefix, suffix); +#endif + + mySerial.printf("%s%d\r\n", prefix, suffix); + + uint8_t l = readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return l; +} + +// Send prefix, suffix, suffix2, and newline. Return response (and also set replybuffer with response). +uint8_t Adafruit_FONA::getReply(const char* prefix, int32_t suffix1, int32_t suffix2, uint16_t timeout) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> %s%d,%d\r\n", prefix, suffix1, suffix2); +#endif + + mySerial.printf("%s%d,%d\r\n", prefix, suffix1, suffix2); + + uint8_t l = readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return l; +} + +// Send prefix, ", suffix, ", and newline. Return response (and also set replybuffer with response). +uint8_t Adafruit_FONA::getReplyQuoted(const char* prefix, const char* suffix, uint16_t timeout) { + flushInput(); + +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t---> %s\"%s\"\r\n", prefix, suffix); +#endif + + mySerial.printf("%s\"%s\"\r\n", prefix, suffix); + + uint8_t l = readline(timeout); +#ifdef ADAFRUIT_FONA_DEBUG + printf("\t<--- %s\r\n", replybuffer); +#endif + return l; +} + + +bool Adafruit_FONA::sendCheckReply(const char *send, const char *reply, uint16_t timeout) { + getReply(send, timeout); + + return (strcmp(replybuffer, reply) == 0); +} + +// Send prefix, suffix, and newline. Verify FONA response matches reply parameter. +bool Adafruit_FONA::sendCheckReply(const char* prefix, char *suffix, const char* reply, uint16_t timeout) { + getReply(prefix, suffix, timeout); + return (strcmp(replybuffer, reply) == 0); +} + +// Send prefix, suffix, and newline. Verify FONA response matches reply parameter. +bool Adafruit_FONA::sendCheckReply(const char* prefix, int32_t suffix, const char* reply, uint16_t timeout) { + getReply(prefix, suffix, timeout); + return (strcmp(replybuffer, reply) == 0); +} + +// Send prefix, suffix, suffix2, and newline. Verify FONA response matches reply parameter. +bool Adafruit_FONA::sendCheckReply(const char* prefix, int32_t suffix1, int32_t suffix2, const char* reply, uint16_t timeout) { + getReply(prefix, suffix1, suffix2, timeout); + return (strcmp(replybuffer, reply) == 0); +} + +// Send prefix, ", suffix, ", and newline. Verify FONA response matches reply parameter. +bool Adafruit_FONA::sendCheckReplyQuoted(const char* prefix, const char* suffix, const char* reply, uint16_t timeout) { + getReplyQuoted(prefix, suffix, timeout); + return (strcmp(replybuffer, reply) == 0); +} + +bool Adafruit_FONA::parseReply(const char* toreply, uint16_t *v, char divider, uint8_t index) { + char *p = strstr(replybuffer, toreply); // get the pointer to the voltage + if (p == 0) return false; + p += strlen(toreply); + + for (uint8_t i=0; i<index;i++) { + // increment dividers + p = strchr(p, divider); + if (!p) return false; + p++; + } + + *v = atoi(p); + + return true; +} + +bool Adafruit_FONA::parseReply(const char* toreply, char *v, char divider, uint8_t index) { + uint8_t i=0; + char *p = strstr(replybuffer, toreply); + if (p == 0) return false; + p+=strlen(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. +bool Adafruit_FONA::parseReplyQuoted(const char* toreply, char* v, int maxlen, char divider, uint8_t index) { + uint8_t i=0, j; + // Verify response starts with toreply. + char *p = strstr(replybuffer, toreply); + if (p == 0) return false; + p+=strlen(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; +} + +bool Adafruit_FONA::sendParseReply(const char* tosend, const char* toreply, uint16_t *v, char divider, uint8_t index) { + getReply(tosend); + + if (! parseReply(toreply, v, divider, index)) return false; + + readline(); // eat 'OK' + + return true; +} \ No newline at end of file