You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

936 lines
28 KiB

/********************************************************************************
* Arduino-SIM800L-driver *
* ---------------------- *
* Arduino driver for GSM/GPRS module SIMCom SIM800L to make HTTP/S connections *
* with GET and POST methods *
* Author: Olivier Staquet *
* Last version available on https://github.com/ostaquet/Arduino-SIM800L-driver *
********************************************************************************
* MIT License
*
* Copyright (c) 2019 Olivier Staquet
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
#include "SIM800L.h"
/**
* AT commands required (const char in PROGMEM to save memory usage)
*/
const char AT_CMD_BASE[] PROGMEM = "AT"; // Basic AT command to check the link
const char AT_CMD_CSQ[] PROGMEM = "AT+CSQ"; // Check the signal strengh
const char AT_CMD_ATI[] PROGMEM = "ATI"; // Output version of the module
const char AT_CMD_GMR[] PROGMEM = "AT+GMR"; // Output version of the firmware
const char AT_CMD_SIM_CARD[] PROGMEM = "AT+CCID"; // Get Sim Card version
const char AT_CMD_CFUN_TEST[] PROGMEM = "AT+CFUN?"; // Check the current power mode
const char AT_CMD_CFUN0[] PROGMEM = "AT+CFUN=0"; // Switch minimum power mode
const char AT_CMD_CFUN1[] PROGMEM = "AT+CFUN=1"; // Switch normal power mode
const char AT_CMD_CFUN4[] PROGMEM = "AT+CFUN=4"; // Switch sleep power mode
const char AT_CMD_CREG_TEST[] PROGMEM = "AT+CREG?"; // Check the network registration status
const char AT_CMD_SAPBR_GPRS[] PROGMEM = "AT+SAPBR=3,1,\"Contype\",\"GPRS\""; // Configure the GPRS bearer
const char AT_CMD_SAPBR_APN[] PROGMEM = "AT+SAPBR=3,1,\"APN\","; // Configure the APN for the GPRS
const char AT_CMD_SAPBR1[] PROGMEM = "AT+SAPBR=1,1"; // Connect GPRS
const char AT_CMD_SAPBR0[] PROGMEM = "AT+SAPBR=0,1"; // Disconnect GPRS
const char AT_CMD_HTTPINIT[] PROGMEM = "AT+HTTPINIT"; // Init HTTP connection
const char AT_CMD_HTTPPARA_CID[] PROGMEM = "AT+HTTPPARA=\"CID\",1"; // Connect HTTP through GPRS bearer
const char AT_CMD_HTTPPARA_URL[] PROGMEM = "AT+HTTPPARA=\"URL\","; // Define the URL to connect in HTTP
const char AT_CMD_HTTPPARA_USERDATA[] PROGMEM = "AT+HTTPPARA=\"USERDATA\","; // Define the header(s)
const char AT_CMD_HTTPPARA_CONTENT[] PROGMEM = "AT+HTTPPARA=\"CONTENT\","; // Define the content type for the HTTP POST
const char AT_CMD_HTTPSSL_Y[] PROGMEM = "AT+HTTPSSL=1"; // Enable SSL for HTTP connection
const char AT_CMD_HTTPSSL_N[] PROGMEM = "AT+HTTPSSL=0"; // Disable SSL for HTTP connection
const char AT_CMD_HTTPACTION0[] PROGMEM = "AT+HTTPACTION=0"; // Launch HTTP GET action
const char AT_CMD_HTTPACTION1[] PROGMEM = "AT+HTTPACTION=1"; // Launch HTTP POST action
const char AT_CMD_HTTPREAD[] PROGMEM = "AT+HTTPREAD"; // Start reading HTTP return data
const char AT_CMD_HTTPTERM[] PROGMEM = "AT+HTTPTERM"; // Terminate HTTP connection
const char AT_RSP_OK[] PROGMEM = "OK"; // Expected answer OK
const char AT_RSP_DOWNLOAD[] PROGMEM = "DOWNLOAD"; // Expected answer DOWNLOAD
const char AT_RSP_HTTPREAD[] PROGMEM = "+HTTPREAD: "; // Expected answer HTTPREAD
/**
* Constructor; Init the driver, communication with the module and shared
* buffer used by the driver (to avoid multiples allocation)
*/
SIM800L::SIM800L(Stream* _stream, uint8_t _pinRst, uint16_t _internalBufferSize, uint16_t _recvBufferSize, Stream* _debugStream) {
// Store local variables
stream = _stream;
enableDebug = _debugStream != NULL;
debugStream = _debugStream;
pinReset = _pinRst;
if(pinReset != RESET_PIN_NOT_USED) {
// Setup the reset pin and force a reset of the module
pinMode(pinReset, OUTPUT);
reset();
}
// Prepare internal buffers
if(enableDebug) {
debugStream->print(F("SIM800L : Prepare internal buffer of "));
debugStream->print(_internalBufferSize);
debugStream->println(F(" bytes"));
}
internalBufferSize = _internalBufferSize;
internalBuffer = (char*) malloc(internalBufferSize);
if(enableDebug) {
debugStream->print(F("SIM800L : Prepare reception buffer of "));
debugStream->print(_recvBufferSize);
debugStream->println(F(" bytes"));
}
recvBufferSize = _recvBufferSize;
recvBuffer = (char *) malloc(recvBufferSize);
}
/**
* Destructor; cleanup the memory allocated by the driver
*/
SIM800L::~SIM800L() {
free(internalBuffer);
free(recvBuffer);
}
/**
* Do HTTP/S POST to a specific URL
*/
uint16_t SIM800L::doPost(const char* url, const char* contentType, const char* payload, uint16_t clientWriteTimeoutMs, uint16_t serverReadTimeoutMs) {
return doPost(url, NULL, contentType, payload, clientWriteTimeoutMs , serverReadTimeoutMs);
}
/**
* Do HTTP/S POST to a specific URL with headers
*/
uint16_t SIM800L::doPost(const char* url, const char* headers, const char* contentType, const char* payload, uint16_t clientWriteTimeoutMs, uint16_t serverReadTimeoutMs) {
// Cleanup the receive buffer
initRecvBuffer();
dataSize = 0;
// Initiate HTTP/S session with the module
uint16_t initRC = initiateHTTP(url, headers);
if(initRC > 0) {
return initRC;
}
// Define the content type
sendCommand_P(AT_CMD_HTTPPARA_CONTENT, contentType);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Unable to define the content type"));
return 702;
}
// Prepare to send the payload
char* tmpBuf = (char*)malloc(30);
sprintf(tmpBuf, "AT+HTTPDATA=%d,%d", strlen(payload), clientWriteTimeoutMs);
sendCommand(tmpBuf);
free(tmpBuf);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_DOWNLOAD)) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Unable to send payload to module"));
return 707;
}
// Write the payload on the module
if(enableDebug) {
debugStream->print(F("SIM800L : doPost() - Payload to send : "));
debugStream->println(payload);
}
purgeSerial();
stream->write(payload);
stream->flush();
delay(500);
// Start HTTP POST action
sendCommand_P(AT_CMD_HTTPACTION1);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Unable to initiate POST action"));
return 703;
}
// Wait answer from the server
if(!readResponse(serverReadTimeoutMs)) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Server timeout"));
return 408;
}
// Extract status information
int16_t idxBase = strIndex(internalBuffer, "+HTTPACTION: 1,");
if(idxBase < 0) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Invalid answer on HTTP POST"));
return 703;
}
// Get the HTTP return code
uint16_t httpRC = 0;
httpRC += (internalBuffer[idxBase + 15] - '0') * 100;
httpRC += (internalBuffer[idxBase + 16] - '0') * 10;
httpRC += (internalBuffer[idxBase + 17] - '0') * 1;
if(enableDebug) {
debugStream->print(F("SIM800L : doPost() - HTTP status "));
debugStream->println(httpRC);
}
if(httpRC == 200) {
// Get the size of the data to receive
dataSize = 0;
for(uint16_t i = 0; (internalBuffer[idxBase + 19 + i] - '0') >= 0 && (internalBuffer[idxBase + 19 + i] - '0') <= 9; i++) {
if(i != 0) {
dataSize = dataSize * 10;
}
dataSize += (internalBuffer[idxBase + 19 + i] - '0');
}
if(enableDebug) {
debugStream->print(F("SIM800L : doPost() - Data size received of "));
debugStream->print(dataSize);
debugStream->println(F(" bytes"));
}
// Ask for reading and detect the start of the reading...
sendCommand_P(AT_CMD_HTTPREAD);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_HTTPREAD, 2)) {
return 705;
}
// Read number of bytes defined in the dataSize
for(uint16_t i = 0; i < dataSize && i < recvBufferSize; i++) {
while(!stream->available());
if(stream->available()) {
// Load the next char
recvBuffer[i] = stream->read();
// If the character is CR or LF, ignore it (it's probably part of the module communication schema)
if((recvBuffer[i] == '\r') || (recvBuffer[i] == '\n')) {
i--;
}
}
}
if(recvBufferSize < dataSize) {
dataSize = recvBufferSize;
if(enableDebug) {
debugStream->println(F("SIM800L : doPost() - Buffer overflow while loading data from HTTP. Keep only first bytes..."));
}
}
// We are expecting a final OK
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : doPost() - Invalid end of data while reading HTTP result from the module"));
return 705;
}
if(enableDebug) {
debugStream->print(F("SIM800L : doPost() - Received from HTTP POST : "));
debugStream->println(recvBuffer);
}
}
// Terminate HTTP/S session
uint16_t termRC = terminateHTTP();
if(termRC > 0) {
return termRC;
}
return httpRC;
}
/**
* Do HTTP/S GET on a specific URL
*/
uint16_t SIM800L::doGet(const char* url, uint16_t serverReadTimeoutMs) {
return doGet(url, NULL, serverReadTimeoutMs);
}
/**
* Do HTTP/S GET on a specific URL with headers
*/
uint16_t SIM800L::doGet(const char* url, const char* headers, uint16_t serverReadTimeoutMs) {
// Cleanup the receive buffer
initRecvBuffer();
dataSize = 0;
// Initiate HTTP/S session
uint16_t initRC = initiateHTTP(url, headers);
if(initRC > 0) {
return initRC;
}
// Start HTTP GET action
sendCommand_P(AT_CMD_HTTPACTION0);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : doGet() - Unable to initiate GET action"));
return 703;
}
// Wait answer from the server
if(!readResponse(serverReadTimeoutMs)) {
if(enableDebug) debugStream->println(F("SIM800L : doGet() - Server timeout"));
return 408;
}
// Extract status information
int16_t idxBase = strIndex(internalBuffer, "+HTTPACTION: 0,");
if(idxBase < 0) {
if(enableDebug) debugStream->println(F("SIM800L : doGet() - Invalid answer on HTTP GET"));
return 703;
}
// Get the HTTP return code
uint16_t httpRC = 0;
httpRC += (internalBuffer[idxBase + 15] - '0') * 100;
httpRC += (internalBuffer[idxBase + 16] - '0') * 10;
httpRC += (internalBuffer[idxBase + 17] - '0') * 1;
if(enableDebug) {
debugStream->print(F("SIM800L : doGet() - HTTP status "));
debugStream->println(httpRC);
}
if(httpRC == 200) {
// Get the size of the data to receive
dataSize = 0;
for(uint16_t i = 0; (internalBuffer[idxBase + 19 + i] - '0') >= 0 && (internalBuffer[idxBase + 19 + i] - '0') <= 9; i++) {
if(i != 0) {
dataSize = dataSize * 10;
}
dataSize += (internalBuffer[idxBase + 19 + i] - '0');
}
if(enableDebug) {
debugStream->print(F("SIM800L : doGet() - Data size received of "));
debugStream->print(dataSize);
debugStream->println(F(" bytes"));
}
// Ask for reading and detect the start of the reading...
sendCommand_P(AT_CMD_HTTPREAD);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_HTTPREAD, 2)) {
return 705;
}
// Read number of bytes defined in the dataSize
for(uint16_t i = 0; i < dataSize && i < recvBufferSize; i++) {
while(!stream->available());
if(stream->available()) {
// Load the next char
recvBuffer[i] = stream->read();
// If the character is CR or LF, ignore it (it's probably part of the module communication schema)
if((recvBuffer[i] == '\r') || (recvBuffer[i] == '\n')) {
i--;
}
}
}
if(recvBufferSize < dataSize) {
dataSize = recvBufferSize;
if(enableDebug) {
debugStream->println(F("SIM800L : doGet() - Buffer overflow while loading data from HTTP. Keep only first bytes..."));
}
}
// We are expecting a final OK
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : doGet() - Invalid end of data while reading HTTP result from the module"));
return 705;
}
if(enableDebug) {
debugStream->print(F("SIM800L : doGet() - Received from HTTP GET : "));
debugStream->println(recvBuffer);
}
}
// Terminate HTTP/S session
uint16_t termRC = terminateHTTP();
if(termRC > 0) {
return termRC;
}
return httpRC;
}
/**
* Meta method to initiate the HTTP/S session on the module
*/
uint16_t SIM800L::initiateHTTP(const char* url, const char* headers) {
// Init HTTP connection
sendCommand_P(AT_CMD_HTTPINIT);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to init HTTP"));
return 701;
}
// Use the GPRS bearer
sendCommand_P(AT_CMD_HTTPPARA_CID);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to define bearer"));
return 702;
}
// Define URL to look for
sendCommand_P(AT_CMD_HTTPPARA_URL, url);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to define the URL"));
return 702;
}
// Set Headers
if (headers != NULL) {
sendCommand_P(AT_CMD_HTTPPARA_USERDATA, headers);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to define Headers"));
return 702;
}
}
// Check if the firmware support HTTPSSL command
bool isSupportSSL = false;
char* version = getVersion();
int16_t rIdx = strIndex(version, "R");
if(rIdx > 0) {
uint8_t releaseInt = (version[rIdx + 1] - '0') * 10 + (version[rIdx + 2] - '0');
// The release should be greater or equals to 14 to support SSL stack
if(releaseInt >= 14) {
isSupportSSL = true;
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Support of SSL enabled"));
} else {
isSupportSSL = false;
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Support of SSL disabled (SIM800L firware below R14)"));
}
}
// Send HTTPSSL command only if the version is greater or equals to 14
if(isSupportSSL) {
// HTTP or HTTPS
if(strIndex(url, "https://") == 0) {
sendCommand_P(AT_CMD_HTTPSSL_Y);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to switch to HTTPS"));
return 702;
}
} else {
sendCommand_P(AT_CMD_HTTPSSL_N);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : initiateHTTP() - Unable to switch to HTTP"));
return 702;
}
}
}
return 0;
}
/**
* Meta method to terminate the HTTP/S session on the module
*/
uint16_t SIM800L::terminateHTTP() {
// Close HTTP connection
sendCommand_P(AT_CMD_HTTPTERM);
if(!readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK)) {
if(enableDebug) debugStream->println(F("SIM800L : terminateHTTP() - Unable to close HTTP session"));
return 706;
}
return 0;
}
/**
* Force a reset of the module
*/
void SIM800L::reset() {
if(pinReset != RESET_PIN_NOT_USED)
{
// Some logging
if(enableDebug) debugStream->println(F("SIM800L : Reset"));
// Reset the device
digitalWrite(pinReset, HIGH);
delay(500);
digitalWrite(pinReset, LOW);
delay(500);
digitalWrite(pinReset, HIGH);
delay(1000);
} else {
// Some logging
if(enableDebug) debugStream->println(F("SIM800L : Reset requested but reset pin undefined"));
if(enableDebug) debugStream->println(F("SIM800L : No reset"));
}
// Purge the serial
stream->flush();
while (stream->available()) {
stream->read();
}
}
/**
* Return the size of data received after the last successful HTTP connection
*/
uint16_t SIM800L::getDataSizeReceived() {
return dataSize;
}
/**
* Return the buffer of data received after the last successful HTTP connection
*/
char* SIM800L::getDataReceived() {
return recvBuffer;
}
/**
* Status function: Check if AT command works
*/
bool SIM800L::isReady() {
sendCommand_P(AT_CMD_BASE);
return readResponseCheckAnswer_P(DEFAULT_TIMEOUT, AT_RSP_OK);
}
/**
* Status function: Check the power mode
*/
PowerMode SIM800L::getPowerMode() {
sendCommand_P(AT_CMD_CFUN_TEST);
if(readResponse(DEFAULT_TIMEOUT)) {
// Check if there is an error
int16_t errIdx = strIndex(internalBuffer, "ERROR");
if(errIdx > 0) {
return POW_ERROR;
}
// Extract the value
int16_t idx = strIndex(internalBuffer, "+CFUN: ");
char value = internalBuffer[idx + 7];
// Prepare the clear output
switch(value) {
case '0' : return MINIMUM;
case '1' : return NORMAL;
case '4' : return SLEEP;
default : return POW_UNKNOWN;
}
}
return POW_ERROR;
}
/**
* Status function: Get version of the module
*/
char* SIM800L::getVersion() {
sendCommand_P(AT_CMD_ATI);
if(readResponse(DEFAULT_TIMEOUT)) {
// Extract the value
int16_t idx = strIndex(internalBuffer, "SIM");
int16_t idxEnd = strIndex(internalBuffer, "\r", idx+1);
// Store it on the recv buffer (not used at the moment)
initRecvBuffer();
for(uint16_t i = 0; i < idxEnd - idx; i++) {
recvBuffer[i] = internalBuffer[idx + i];
}
return getDataReceived();
} else {
return NULL;
}
}
/**
* Status function: Get firmware version
*/
char* SIM800L::getFirmware() {
sendCommand_P(AT_CMD_GMR);
if(readResponse(DEFAULT_TIMEOUT)) {
// Extract the value
int16_t idx = strIndex(internalBuffer, "AT+GMR") + 9;
int16_t idxEnd = strIndex(internalBuffer, "\r", idx+1);
// Store it on the recv buffer (not used at the moment)
initRecvBuffer();
for(uint16_t i = 0; i < idxEnd - idx; i++) {
recvBuffer[i] = internalBuffer[idx + i];
}
return getDataReceived();
} else {
return NULL;
}
}
/**
* Status function: Requests the simcard number
*/
char* SIM800L::getSimCardNumber() {
sendCommand_P(AT_CMD_SIM_CARD);
if(readResponse(DEFAULT_TIMEOUT)) {
int16_t idx = strIndex(internalBuffer, "AT+CCID") + 10;
int16_t idxEnd = strIndex(internalBuffer, "\r", idx+1);
// Store it on the recv buffer (not used at the moment)
initRecvBuffer();
for(uint16_t i = 0; i < idxEnd - idx; i++) {
recvBuffer[i] = internalBuffer[idx + i];
}
return getDataReceived();
} else {
return NULL;
}
}
/**
* Status function: Check if the module is registered on the network
*/
NetworkRegistration SIM800L::getRegistrationStatus() {
sendCommand_P(AT_CMD_CREG_TEST);
if(readResponse(DEFAULT_TIMEOUT)) {
// Check if there is an error
int16_t errIdx = strIndex(internalBuffer, "ERROR");
if(errIdx > 0) {
return NET_ERROR;
}
// Extract the value
int16_t idx = strIndex(internalBuffer, "+CREG: ");
char value = internalBuffer[idx + 9];
// Prepare the clear output
switch(value) {
case '0' : return NOT_REGISTERED;
case '1' : return REGISTERED_HOME;
case '2' : return SEARCHING;
case '3' : return DENIED;
case '5' : return REGISTERED_ROAMING;
default : return NET_UNKNOWN;
}
}
return NET_ERROR;
}
/**
* Setup the GPRS connectivity
* As input, give the APN string of the operator
*/
bool SIM800L::setupGPRS(const char* apn) {
// Prepare the GPRS connection as the bearer
sendCommand_P(AT_CMD_SAPBR_GPRS);
if(!readResponseCheckAnswer_P(20000, AT_RSP_OK)) {
return false;
}
// Set the config of the bearer with the APN
sendCommand_P(AT_CMD_SAPBR_APN, apn);
return readResponseCheckAnswer_P(20000, AT_RSP_OK);
}
/**
* Open the GPRS connectivity
*/
bool SIM800L::connectGPRS() {
sendCommand_P(AT_CMD_SAPBR1);
// Timout is max 85 seconds according to SIM800 specifications
// We will wait for 65s to be within uint16_t
return readResponseCheckAnswer_P(65000, AT_RSP_OK);
}
/**
* Close the GPRS connectivity
*/
bool SIM800L::disconnectGPRS() {
sendCommand_P(AT_CMD_SAPBR0);
// Timout is max 65 seconds according to SIM800 specifications
return readResponseCheckAnswer_P(65000, AT_RSP_OK);
}
/**
* Define the power mode
* Available : MINIMUM, NORMAL, SLEEP
* Return true is the mode is correctly switched
*/
bool SIM800L::setPowerMode(PowerMode powerMode) {
// Check if the power mode requested is not ERROR or UNKNOWN
if(powerMode == POW_ERROR || powerMode == POW_UNKNOWN) {
return false;
}
// Check the current power mode
PowerMode currentPowerMode = getPowerMode();
// If the current power mode is undefined, abord
if(currentPowerMode == POW_ERROR || currentPowerMode == POW_UNKNOWN) {
return false;
}
// If the current power mode is the same that the requested power mode, say it's OK
if(currentPowerMode == powerMode) {
return true;
}
// If SLEEP or MINIMUM, only NORMAL is allowed
if((currentPowerMode == SLEEP || currentPowerMode == MINIMUM) && (powerMode != NORMAL)) {
return false;
}
// Send the command
char value;
switch(powerMode) {
case MINIMUM :
sendCommand_P(AT_CMD_CFUN0);
break;
case SLEEP :
sendCommand_P(AT_CMD_CFUN4);
break;
case NORMAL :
default :
sendCommand_P(AT_CMD_CFUN1);
}
// Read but don't care about the result
purgeSerial();
// Check the current power mode
currentPowerMode = getPowerMode();
// If the current power mode is the same that the requested power mode, say it's OK
return currentPowerMode == powerMode;
}
/**
* Status function: Check the strengh of the signal
*/
uint8_t SIM800L::getSignal() {
sendCommand_P(AT_CMD_CSQ);
if(readResponse(DEFAULT_TIMEOUT)) {
int16_t idxBase = strIndex(internalBuffer, "AT+CSQ");
if(idxBase != 0) {
return 0;
}
int16_t idxEnd = strIndex(internalBuffer, ",", idxBase);
uint8_t value = internalBuffer[idxEnd - 1] - '0';
if(internalBuffer[idxEnd - 2] != ' ') {
value += (internalBuffer[idxEnd - 2] - '0') * 10;
}
if(value > 31) {
return 0;
}
return value;
}
return 0;
}
/*****************************************************************************************
* HELPERS
*****************************************************************************************/
/**
* Find string "findStr" in another string "str"
* Returns true if found, false elsewhere
*/
int16_t SIM800L::strIndex(const char* str, const char* findStr, uint16_t startIdx) {
int16_t firstIndex = -1;
int16_t sizeMatch = 0;
for(int16_t i = startIdx; i < strlen(str); i++) {
if(sizeMatch >= strlen(findStr)) {
break;
}
if(str[i] == findStr[sizeMatch]) {
if(firstIndex < 0) {
firstIndex = i;
}
sizeMatch++;
} else {
firstIndex = -1;
sizeMatch = 0;
}
}
if(sizeMatch >= strlen(findStr)) {
return firstIndex;
} else {
return -1;
}
}
/**
* Init internal buffer
*/
void SIM800L::initInternalBuffer() {
for(uint16_t i = 0; i < internalBufferSize; i++) {
internalBuffer[i] = '\0';
}
}
/**
* Init recv buffer
*/
void SIM800L::initRecvBuffer() {
// Cleanup the receive buffer
for(uint16_t i = 0; i < recvBufferSize; i++) {
recvBuffer[i] = 0;
}
}
/*****************************************************************************************
* LOW LEVEL FUNCTIONS TO COMMUNICATE WITH THE SIM800L MODULE
*****************************************************************************************/
/**
* Send AT command to the module
*/
void SIM800L::sendCommand(const char* command) {
if(enableDebug) {
debugStream->print(F("SIM800L : Send \""));
debugStream->print(command);
debugStream->println(F("\""));
}
purgeSerial();
stream->write(command);
stream->write("\r\n");
purgeSerial();
}
/**
* Send AT command coming from the PROGMEM
*/
void SIM800L::sendCommand_P(const char* command) {
char cmdBuff[32];
strcpy_P(cmdBuff, command);
sendCommand(cmdBuff);
}
/**
* Send AT command to the module with a parameter
*/
void SIM800L::sendCommand(const char* command, const char* parameter) {
if(enableDebug) {
debugStream->print(F("SIM800L : Send \""));
debugStream->print(command);
debugStream->print(F("\""));
debugStream->print(parameter);
debugStream->print(F("\""));
debugStream->println(F("\""));
}
purgeSerial();
stream->write(command);
stream->write("\"");
stream->write(parameter);
stream->write("\"");
stream->write("\r\n");
purgeSerial();
}
/**
* Send AT command coming from the PROGMEM with a parameter
*/
void SIM800L::sendCommand_P(const char* command, const char* parameter) {
char cmdBuff[32];
strcpy_P(cmdBuff, command);
sendCommand(cmdBuff, parameter);
}
/**
* Purge the serial data
*/
void SIM800L::purgeSerial() {
stream->flush();
while (stream->available()) {
stream->read();
}
stream->flush();
}
/**
* Read from module and expect a specific answer (timeout in millisec)
*/
bool SIM800L::readResponseCheckAnswer_P(uint16_t timeout, const char* expectedAnswer, uint8_t crlfToWait) {
if(readResponse(timeout, crlfToWait)) {
// Prepare the local expected answer
char rspBuff[16];
strcpy_P(rspBuff, expectedAnswer);
// Check if it's the expected answer
int16_t idx = strIndex(internalBuffer, rspBuff);
if(idx > 0) {
return true;
}
}
return false;
}
/**
* Read from the module for a specific number of CRLF
* True if we have some data
*/
bool SIM800L::readResponse(uint16_t timeout, uint8_t crlfToWait) {
uint16_t currentSizeResponse = 0;
bool seenCR = false;
uint8_t countCRLF = 0;
// First of all, cleanup the buffer
initInternalBuffer();
uint32_t timerStart = millis();
while(1) {
// While there is data available on the buffer, read it until the max size of the response
if(stream->available()) {
// Load the next char
internalBuffer[currentSizeResponse] = stream->read();
// Detect end of transmission (CRLF)
if(internalBuffer[currentSizeResponse] == '\r') {
seenCR = true;
} else if (internalBuffer[currentSizeResponse] == '\n' && seenCR) {
countCRLF++;
if(countCRLF == crlfToWait) {
if(enableDebug) debugStream->println(F("SIM800L : End of transmission"));
break;
}
} else {
seenCR = false;
}
// Prepare for next read
currentSizeResponse++;
// Avoid buffer overflow
if(currentSizeResponse == internalBufferSize) {
if(enableDebug) debugStream->println(F("SIM800L : Received maximum buffer size"));
break;
}
}
// If timeout, abord the reading
if(millis() - timerStart > timeout) {
if(enableDebug) debugStream->println(F("SIM800L : Receive timeout"));
// Timeout, return false to parent function
return false;
}
}
if(enableDebug) {
debugStream->print(F("SIM800L : Receive \""));
debugStream->print(internalBuffer);
debugStream->println(F("\""));
}
// If we are here, it's OK ;-)
return true;
}