Merge branch 'master' into master

This commit is contained in:
Sara Damiano
2018-04-16 14:01:26 -04:00
committed by GitHub
30 changed files with 918 additions and 611 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,18 +1,25 @@
{ {
"name": "TinyGSM", "name": "TinyGSM",
"version": "0.3.3", "version": "0.3.30",
"description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports GSM modules with AT command interface: SIM800, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900, SIM900A, SIM900D, SIM908, SIM968", "description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports GSM modules with AT command interface: SIM800, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900, SIM900A, SIM900D, SIM908, SIM968",
"keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968", "keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968",
"authors": "authors":
{ [
"name": "Volodymyr Shymanskyy", {
"url": "https://github.com/vshymanskyy", "name": "Sara Damiano",
"maintainer": true "email": "sdamiano@stroudcenter.org",
}, "url": "https://envirodiy.org/",
"maintainer": true
},
{
"name": "Volodymyr Shymanskyy",
"url": "https://github.com/vshymanskyy"
}
],
"repository": "repository":
{ {
"type": "git", "type": "git",
"url": "https://github.com/vshymanskyy/TinyGSM.git" "url": "https://github.com/EnviroDIY/TinyGSM.git"
}, },
"homepage": "https://github.com/vshymanskyy/TinyGSM", "homepage": "https://github.com/vshymanskyy/TinyGSM",
"export": { "export": {

View File

@@ -1,5 +1,5 @@
name=TinyGSM name=TinyGSM
version=0.3.3 version=0.3.30
author=Volodymyr Shymanskyy author=Volodymyr Shymanskyy
maintainer=Volodymyr Shymanskyy maintainer=Volodymyr Shymanskyy
sentence=A small Arduino library for GPRS modules, that just works. sentence=A small Arduino library for GPRS modules, that just works.

View File

@@ -34,11 +34,13 @@
#elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7) #elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7)
#define TINY_GSM_MODEM_HAS_GPRS #define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientA6.h> #include <TinyGsmClientA6.h>
typedef TinyGsmA6 TinyGsm;
typedef TinyGsm::GsmClient TinyGsmClient; typedef TinyGsm::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_M590) #elif defined(TINY_GSM_MODEM_M590)
#define TINY_GSM_MODEM_HAS_GPRS #define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientM590.h> #include <TinyGsmClientM590.h>
typedef TinyGsmM590 TinyGsm;
typedef TinyGsm::GsmClient TinyGsmClient; typedef TinyGsm::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_U201) #elif defined(TINY_GSM_MODEM_U201)
@@ -51,6 +53,7 @@
#elif defined(TINY_GSM_MODEM_ESP8266) #elif defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_WIFI #define TINY_GSM_MODEM_HAS_WIFI
#include <TinyGsmClientESP8266.h> #include <TinyGsmClientESP8266.h>
typedef TinyGsmESP8266 TinyGsm;
typedef TinyGsm::GsmClient TinyGsmClient; typedef TinyGsm::GsmClient TinyGsmClient;
typedef TinyGsm::GsmClientSecure TinyGsmClientSecure; typedef TinyGsm::GsmClientSecure TinyGsmClientSecure;
@@ -58,7 +61,9 @@
#define TINY_GSM_MODEM_HAS_GPRS #define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_WIFI #define TINY_GSM_MODEM_HAS_WIFI
#include <TinyGsmClientXBee.h> #include <TinyGsmClientXBee.h>
typedef TinyGsm::GsmClient TinyGsmClient; typedef TinyGsmXBee TinyGsm;
typedef TinyGsmXBee::GsmClient TinyGsmClient;
typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure;
#else #else
#error "Please define GSM modem model" #error "Please define GSM modem model"

View File

@@ -39,24 +39,40 @@ enum RegStatus {
}; };
class TinyGsm //============================================================================//
//============================================================================//
// Declaration of the TinyGsmA6 Class
//============================================================================//
//============================================================================//
class TinyGsmA6
{ {
public: public:
//============================================================================//
//============================================================================//
// The A6 Client Class
//============================================================================//
//============================================================================//
class GsmClient : public Client class GsmClient : public Client
{ {
friend class TinyGsm; friend class TinyGsmA6;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public: public:
GsmClient() {} GsmClient() {}
GsmClient(TinyGsm& modem) { GsmClient(TinyGsmA6& modem) {
init(&modem); init(&modem);
} }
bool init(TinyGsm* modem) { bool init(TinyGsmA6* modem) {
this->at = modem; this->at = modem;
this->mux = -1; this->mux = -1;
sock_connected = false; sock_connected = false;
@@ -119,7 +135,8 @@ public:
virtual int read(uint8_t *buf, size_t size) { virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
size_t cnt = 0; size_t cnt = 0;
while (cnt < size) { uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size()); size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) { if (chunk > 0) {
rx.get(buf, chunk); rx.get(buf, chunk);
@@ -162,15 +179,34 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private: private:
TinyGsm* at; TinyGsmA6* at;
uint8_t mux; uint8_t mux;
bool sock_connected; bool sock_connected;
RxFifo rx; RxFifo rx;
}; };
//============================================================================//
//============================================================================//
// The A6 Does not have a secure client!
//============================================================================//
//============================================================================//
//============================================================================//
//============================================================================//
// The A6 Modem Functions
//============================================================================//
//============================================================================//
public: public:
TinyGsm(Stream& stream) #ifdef GSM_DEFAULT_STREAM
TinyGsmA6(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmA6(Stream& stream)
#endif
: stream(stream) : stream(stream)
{ {
memset(sockets, 0, sizeof(sockets)); memset(sockets, 0, sizeof(sockets));
@@ -316,17 +352,6 @@ public:
return SIM_ERROR; return SIM_ERROR;
} }
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() { String getOperator() {
sendAT(GF("+COPS=3,0")); // Set format sendAT(GF("+COPS=3,0")); // Set format
waitResponse(); waitResponse();
@@ -345,6 +370,17 @@ public:
* Generic network functions * Generic network functions
*/ */
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
int getSignalQuality() { int getSignalQuality() {
sendAT(GF("+CSQ")); sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
@@ -370,6 +406,26 @@ public:
return false; return false;
} }
String getLocalIP() {
sendAT(GF("+CIFSR"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, "");
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* WiFi functions
*/
/* /*
* GPRS functions * GPRS functions
*/ */
@@ -428,104 +484,6 @@ public:
return (res == 1); return (res == 1);
} }
String getLocalIP() {
sendAT(GF("+CIFSR"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, "");
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() {
sendAT(GF("A"));
return waitResponse() == 1;
}
// Returns true on pick-up, false on error/busy
bool callNumber(const String& number) {
if (number == GF("last")) {
sendAT(GF("DLST"));
} else {
sendAT(GF("D\""), number, "\";");
}
if (waitResponse(5000L) != 1) {
return false;
}
if (waitResponse(60000L,
GF(GSM_NL "+CIEV: \"CALL\",1"),
GF(GSM_NL "+CIEV: \"CALL\",0"),
GFP(GSM_ERROR)) != 1)
{
return false;
}
int rsp = waitResponse(60000L,
GF(GSM_NL "+CIEV: \"SOUNDER\",0"),
GF(GSM_NL "+CIEV: \"CALL\",0"));
int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL));
return rsp == 1 && rsp2 == 0;
}
bool callHangup() {
sendAT(GF("H"));
return waitResponse() == 1;
}
// 0-9,*,#,A,B,C,D
bool dtmfSend(char cmd, unsigned duration_ms = 100) {
duration_ms = constrain(duration_ms, 100, 1000);
// The duration parameter is not working, so we simulate it using delay..
// TODO: Maybe there's another way...
//sendAT(GF("+VTD="), duration_ms / 100);
//waitResponse();
sendAT(GF("+VTS="), cmd);
if (waitResponse(10000L) == 1) {
delay(duration_ms);
return true;
}
return false;
}
/*
* Audio functions
*/
bool audioSetHeadphones() {
sendAT(GF("+SNFS=0"));
return waitResponse() == 1;
}
bool audioSetSpeaker() {
sendAT(GF("+SNFS=1"));
return waitResponse() == 1;
}
bool audioMuteMic(bool mute) {
sendAT(GF("+CMUT="), mute);
return waitResponse() == 1;
}
/* /*
* Messaging functions * Messaging functions
*/ */
@@ -650,9 +608,13 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
bool streamSkipUntil(char c) { //TODO: timeout bool streamSkipUntil(char c) {
while (true) { const unsigned long timeout = 1000L;
while (!stream.available()) { TINY_GSM_YIELD(); } unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) if (stream.read() == c)
return true; return true;
} }
@@ -664,7 +626,7 @@ public:
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -672,12 +634,12 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(64);
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
@@ -737,6 +699,7 @@ finish:
} }
data = ""; data = "";
} }
DBG('<', index, '>');
return index; return index;
} }

View File

@@ -9,7 +9,7 @@
#ifndef TinyGsmClientESP8266_h #ifndef TinyGsmClientESP8266_h
#define TinyGsmClientESP8266_h #define TinyGsmClientESP8266_h
//#define TINY_GSM_DEBUG Serial // #define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER) #if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512 #define TINY_GSM_RX_BUFFER 512
@@ -24,24 +24,51 @@ static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120; static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120;
class TinyGsm // <stat> status of ESP8266 station interface
// 2 : ESP8266 station connected to an AP and has obtained IP
// 3 : ESP8266 station created a TCP or UDP transmission
// 4 : the TCP or UDP transmission of ESP8266 station disconnected
// 5 : ESP8266 station did NOT connect to an AP
enum RegStatus {
REG_OK_IP = 2,
REG_OK_TCP = 3,
REG_UNREGISTERED = 4,
REG_DENIED = 5,
REG_UNKNOWN = 6,
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmESP8266 Class
//============================================================================//
//============================================================================//
class TinyGsmESP8266
{ {
//============================================================================//
//============================================================================//
// The ESP8266 Client Class
//============================================================================//
//============================================================================//
public: public:
class GsmClient : public Client class GsmClient : public Client
{ {
friend class TinyGsm; friend class TinyGsmESP8266;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public: public:
GsmClient() {} GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 1) { GsmClient(TinyGsmESP8266& modem, uint8_t mux = 1) {
init(&modem, mux); init(&modem, mux);
} }
bool init(TinyGsm* modem, uint8_t mux = 1) { bool init(TinyGsmESP8266* modem, uint8_t mux = 1) {
this->at = modem; this->at = modem;
this->mux = mux; this->mux = mux;
sock_connected = false; sock_connected = false;
@@ -101,7 +128,8 @@ public:
virtual int read(uint8_t *buf, size_t size) { virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
size_t cnt = 0; size_t cnt = 0;
while (cnt < size) { uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size()); size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) { if (chunk > 0) {
rx.get(buf, chunk); rx.get(buf, chunk);
@@ -144,18 +172,25 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private: private:
TinyGsm* at; TinyGsmESP8266* at;
uint8_t mux; uint8_t mux;
bool sock_connected; bool sock_connected;
RxFifo rx; RxFifo rx;
}; };
//============================================================================//
//============================================================================//
// The Secure ESP8266 Client Class
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient class GsmClientSecure : public GsmClient
{ {
public: public:
GsmClientSecure() {} GsmClientSecure() {}
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1) GsmClientSecure(TinyGsmESP8266& modem, uint8_t mux = 1)
: GsmClient(modem, mux) : GsmClient(modem, mux)
{} {}
@@ -169,9 +204,20 @@ public:
} }
}; };
//============================================================================//
//============================================================================//
// The ESP8266 Modem Functions
//============================================================================//
//============================================================================//
public: public:
TinyGsm(Stream& stream) #ifdef GSM_DEFAULT_STREAM
TinyGsmESP8266(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmESP8266(Stream& stream)
#endif
: stream(stream) : stream(stream)
{ {
memset(sockets, 0, sizeof(sockets)); memset(sockets, 0, sizeof(sockets));
@@ -192,6 +238,14 @@ public:
if (waitResponse() != 1) { if (waitResponse() != 1) {
return false; return false;
} }
sendAT(GF("+CIPMUX=1")); // Enable Multiple Connections
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CWMODE_CUR=1")); // Put into "station" mode
if (waitResponse() != 1) {
return false;
}
return true; return true;
} }
@@ -232,9 +286,7 @@ public:
return res; return res;
} }
bool hasSSL() { bool hasSSL() { return true; }
return true;
}
/* /*
* Power functions * Power functions
@@ -255,11 +307,30 @@ public:
return init(); return init();
} }
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* SIM card functions
*/
/* /*
* Generic network functions * Generic network functions
*/ */
RegStatus getRegistrationStatus() {
sendAT(GF("+CIPSTATUS"));
if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN;
int status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
waitResponse(); // Returns an OK after the status
return (RegStatus)status;
}
int getSignalQuality() { int getSignalQuality() {
sendAT(GF("+CWJAP_CUR?")); sendAT(GF("+CWJAP_CUR?"));
int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:")); int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
@@ -275,21 +346,9 @@ public:
return res2; return res2;
} }
bool isNetworkConnected() { bool isNetworkConnected() {
sendAT(GF("+CIPSTATUS")); RegStatus s = getRegistrationStatus();
int res1 = waitResponse(3000, GF("STATUS:")); return (s == REG_OK_IP || s == REG_OK_TCP);
int res2 = 0;
if (res1 == 1) {
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
}
// <stat> status of ESP8266 station interface
// 2 : ESP8266 station connected to an AP and has obtained IP
// 3 : ESP8266 station created a TCP or UDP transmission
// 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
// 5 : ESP8266 station did NOT connect to an AP
waitResponse(); // Returns an OK after the status
if (res2 == 2 || res2 == 3 || res2 == 4) return true;
else return false;
} }
bool waitForNetwork(unsigned long timeout = 60000L) { bool waitForNetwork(unsigned long timeout = 60000L) {
@@ -298,7 +357,7 @@ public:
int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:"));
if (res1 == 2) { if (res1 == 2) {
int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
if (res2 == 2 || res2 == 3 || res2 == 4) { if (res2 == 2 || res2 == 3) {
waitResponse(); waitResponse();
return true; return true;
} }
@@ -308,36 +367,6 @@ public:
return false; return false;
} }
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) {
sendAT(GF("+CIPMUX=1"));
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CWMODE_CUR=1"));
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
return false;
}
return true;
}
bool networkDisconnect() {
sendAT(GF("+CWQAP"));
bool retVal = waitResponse(10000L) == 1;
waitResponse(GF("WIFI DISCONNECT"));
return retVal;
}
String getLocalIP() { String getLocalIP() {
sendAT(GF("+CIPSTA_CUR??")); sendAT(GF("+CIPSTA_CUR??"));
int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:")); int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
@@ -353,6 +382,46 @@ public:
return TinyGsmIpFromString(getLocalIP()); return TinyGsmIpFromString(getLocalIP());
} }
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) {
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
return false;
}
return true;
}
bool networkDisconnect() {
sendAT(GF("+CWQAP"));
bool retVal = waitResponse(10000L) == 1;
waitResponse(GF("WIFI DISCONNECT"));
return retVal;
}
/*
* GPRS functions
*/
/*
* Messaging functions
*/
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
protected: protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
@@ -384,21 +453,8 @@ protected:
} }
bool modemGetConnected(uint8_t mux) { bool modemGetConnected(uint8_t mux) {
// TODO: re-check this RegStatus s = getRegistrationStatus();
sendAT(GF("+CIPSTATUS="), mux); return (s == REG_OK_IP || s == REG_OK_TCP);
int res1 = waitResponse(3000, GF("STATUS:"));
int res2;
if (res1 == 1) {
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
}
// <stat> status of ESP8266 station interface
// 2 : ESP8266 station connected to an AP and has obtained IP
// 3 : ESP8266 station created a TCP or UDP transmission
// 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
// 5 : ESP8266 station did NOT connect to an AP
waitResponse(); // Returns an OK after the status
if (res2 == 2 || res2 == 3 || res2 == 4) return true;
else return false;
} }
public: public:
@@ -416,9 +472,13 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
bool streamSkipUntil(char c) { //TODO: timeout bool streamSkipUntil(char c) {
while (true) { const unsigned long timeout = 1000L;
while (!stream.available()) { TINY_GSM_YIELD(); } unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) if (stream.read() == c)
return true; return true;
} }
@@ -430,7 +490,7 @@ public:
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -438,12 +498,12 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(64);
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
@@ -505,6 +565,7 @@ finish:
} }
data = ""; data = "";
} }
DBG('<', index, '>');
return index; return index;
} }

View File

@@ -9,7 +9,7 @@
#ifndef TinyGsmClientM590_h #ifndef TinyGsmClientM590_h
#define TinyGsmClientM590_h #define TinyGsmClientM590_h
//#define TINY_GSM_DEBUG Serial // #define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER) #if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256 #define TINY_GSM_RX_BUFFER 256
@@ -39,24 +39,36 @@ enum RegStatus {
}; };
class TinyGsm //============================================================================//
//============================================================================//
// Declaration of the TinyGsmM590 Class
//============================================================================//
//============================================================================//
class TinyGsmM590
{ {
//============================================================================//
//============================================================================//
// The M590 Client Class
//============================================================================//
//============================================================================//
public: public:
class GsmClient : public Client class GsmClient : public Client
{ {
friend class TinyGsm; friend class TinyGsmM590;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public: public:
GsmClient() {} GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 1) { GsmClient(TinyGsmM590& modem, uint8_t mux = 1) {
init(&modem, mux); init(&modem, mux);
} }
bool init(TinyGsm* modem, uint8_t mux = 1) { bool init(TinyGsmM590* modem, uint8_t mux = 1) {
this->at = modem; this->at = modem;
this->mux = mux; this->mux = mux;
sock_connected = false; sock_connected = false;
@@ -116,7 +128,8 @@ public:
virtual int read(uint8_t *buf, size_t size) { virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
size_t cnt = 0; size_t cnt = 0;
while (cnt < size) { uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size()); size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) { if (chunk > 0) {
rx.get(buf, chunk); rx.get(buf, chunk);
@@ -159,15 +172,33 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private: private:
TinyGsm* at; TinyGsmM590* at;
uint8_t mux; uint8_t mux;
bool sock_connected; bool sock_connected;
RxFifo rx; RxFifo rx;
}; };
//============================================================================//
//============================================================================//
// The M590 Has no Secure client!
//============================================================================//
//============================================================================//
//============================================================================//
//============================================================================//
// The M590 Modem Functions
//============================================================================//
//============================================================================//
public: public:
TinyGsm(Stream& stream) #ifdef GSM_DEFAULT_STREAM
TinyGsmM590(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmM590(Stream& stream)
#endif
: stream(stream) : stream(stream)
{ {
memset(sockets, 0, sizeof(sockets)); memset(sockets, 0, sizeof(sockets));
@@ -327,17 +358,6 @@ public:
return SIM_ERROR; return SIM_ERROR;
} }
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() { String getOperator() {
sendAT(GF("+COPS?")); sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
@@ -353,6 +373,17 @@ public:
* Generic network functions * Generic network functions
*/ */
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
int getSignalQuality() { int getSignalQuality() {
sendAT(GF("+CSQ")); sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
@@ -378,6 +409,26 @@ public:
return false; return false;
} }
String getLocalIP() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
return "";
}
stream.readStringUntil(',');
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* WiFi functions
*/
/* /*
* GPRS functions * GPRS functions
*/ */
@@ -408,12 +459,12 @@ public:
} }
return false; return false;
set_dns: // set_dns: // TODO
sendAT(GF("+DNSSERVER=1,8.8.8.8")); // sendAT(GF("+DNSSERVER=1,8.8.8.8"));
waitResponse(); // waitResponse();
//
sendAT(GF("+DNSSERVER=2,8.8.4.4")); // sendAT(GF("+DNSSERVER=2,8.8.4.4"));
waitResponse(); // waitResponse();
return true; return true;
} }
@@ -434,34 +485,6 @@ set_dns:
return res == 1; return res == 1;
} }
String getLocalIP() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
return "";
}
stream.readStringUntil(',');
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE;
/* /*
* Messaging functions * Messaging functions
*/ */
@@ -595,9 +618,13 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
bool streamSkipUntil(char c) { //TODO: timeout bool streamSkipUntil(char c) {
while (true) { const unsigned long timeout = 1000L;
while (!stream.available()) { TINY_GSM_YIELD(); } unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) if (stream.read() == c)
return true; return true;
} }
@@ -609,7 +636,7 @@ public:
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -617,12 +644,12 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(64);
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
@@ -683,6 +710,7 @@ finish:
} }
data = ""; data = "";
} }
DBG('<', index, '>');
return index; return index;
} }

View File

@@ -9,8 +9,8 @@
#ifndef TinyGsmClientSIM800_h #ifndef TinyGsmClientSIM800_h
#define TinyGsmClientSIM800_h #define TinyGsmClientSIM800_h
//#define TINY_GSM_DEBUG Serial // #define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX // #define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER) #if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64 #define TINY_GSM_RX_BUFFER 64
@@ -40,9 +40,22 @@ enum RegStatus {
}; };
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmSim800 Class
//============================================================================//
//============================================================================//
class TinyGsmSim800 class TinyGsmSim800
{ {
//============================================================================//
//============================================================================//
// The Sim800 Client Class
//============================================================================//
//============================================================================//
public: public:
class GsmClient : public Client class GsmClient : public Client
@@ -173,15 +186,22 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private: private:
TinyGsmSim800* at; TinyGsmSim800* at;
uint8_t mux; uint8_t mux;
uint16_t sock_available; uint16_t sock_available;
uint32_t prev_check; uint32_t prev_check;
bool sock_connected; bool sock_connected;
bool got_data; bool got_data;
RxFifo rx; RxFifo rx;
}; };
//============================================================================//
//============================================================================//
// The SIM800 Secure Client
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient class GsmClientSecure : public GsmClient
{ {
public: public:
@@ -201,9 +221,19 @@ public:
} }
}; };
//============================================================================//
//============================================================================//
// The SIM800 Modem Functions
//============================================================================//
//============================================================================//
public: public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmSim800(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmSim800(Stream& stream) TinyGsmSim800(Stream& stream)
#endif
: stream(stream) : stream(stream)
{ {
memset(sockets, 0, sizeof(sockets)); memset(sockets, 0, sizeof(sockets));
@@ -226,6 +256,11 @@ public:
if (waitResponse() != 1) { if (waitResponse() != 1) {
return false; return false;
} }
// PREFERRED SMS STORAGE
sendAT(GF("+CPMS="), GF("\"SM\""), GF(","), GF("\"SM\""), GF(","), GF("\"SM\""));
waitResponse();
getSimStatus(); getSimStatus();
return true; return true;
} }
@@ -393,17 +428,6 @@ public:
return SIM_ERROR; return SIM_ERROR;
} }
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() { String getOperator() {
sendAT(GF("+COPS?")); sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
@@ -419,6 +443,17 @@ public:
* Generic network functions * Generic network functions
*/ */
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
int getSignalQuality() { int getSignalQuality() {
sendAT(GF("+CSQ")); sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
@@ -444,6 +479,26 @@ public:
return false; return false;
} }
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, "");
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* WiFi functions
*/
/* /*
* GPRS functions * GPRS functions
*/ */
@@ -564,72 +619,6 @@ public:
return true; return true;
} }
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, "");
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) {
sendAT(GF("+GSMBUSY="), busy ? 1 : 0);
return waitResponse() == 1;
}
bool callAnswer() {
sendAT(GF("A"));
return waitResponse() == 1;
}
// Returns true on pick-up, false on error/busy
bool callNumber(const String& number) {
if (number == GF("last")) {
sendAT(GF("DL"));
} else {
sendAT(GF("D"), number, ";");
}
int status = waitResponse(60000L,
GFP(GSM_OK),
GF("BUSY" GSM_NL),
GF("NO ANSWER" GSM_NL),
GF("NO CARRIER" GSM_NL));
switch (status) {
case 1: return true;
case 2:
case 3: return false;
default: return false;
}
}
bool callHangup() {
sendAT(GF("H"));
return waitResponse() == 1;
}
// 0-9,*,#,A,B,C,D
bool dtmfSend(char cmd, int duration_ms = 100) {
duration_ms = constrain(duration_ms, 100, 1000);
sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second
waitResponse();
sendAT(GF("+VTS="), cmd);
return waitResponse(10000L) == 1;
}
/* /*
* Messaging functions * Messaging functions
*/ */
@@ -722,6 +711,7 @@ public:
/* /*
* Battery functions * Battery functions
*/ */
// Use: float vBatt = modem.getBattVoltage() / 1000.0; // Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() { uint16_t getBattVoltage() {
sendAT(GF("+CBC")); sendAT(GF("+CBC"));
@@ -854,9 +844,13 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
bool streamSkipUntil(char c) { //TODO: timeout bool streamSkipUntil(char c) {
while (true) { const unsigned long timeout = 1000L;
while (!stream.available()) { TINY_GSM_YIELD(); } unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) if (stream.read() == c)
return true; return true;
} }
@@ -868,7 +862,7 @@ public:
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -876,12 +870,12 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(64);
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
@@ -937,6 +931,7 @@ finish:
} }
data = ""; data = "";
} }
DBG('<', index, '>');
return index; return index;
} }

View File

@@ -11,6 +11,13 @@
#include <TinyGsmClientSIM800.h> #include <TinyGsmClientSIM800.h>
//============================================================================//
//============================================================================//
// Declaration and Definitio of the TinyGsmSim808 Class
//============================================================================//
//============================================================================//
class TinyGsmSim808: public TinyGsmSim800 class TinyGsmSim808: public TinyGsmSim800
{ {

View File

@@ -9,7 +9,7 @@
#ifndef TinyGsmClientU201_h #ifndef TinyGsmClientU201_h
#define TinyGsmClientU201_h #define TinyGsmClientU201_h
//#define TINY_GSM_DEBUG Serial // #define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER) #if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64 #define TINY_GSM_RX_BUFFER 64
@@ -39,9 +39,22 @@ enum RegStatus {
}; };
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmU201 Class
//============================================================================//
//============================================================================//
class TinyGsmU201 class TinyGsmU201
{ {
//============================================================================//
//============================================================================//
// The U201 Client Class
//============================================================================//
//============================================================================//
public: public:
class GsmClient : public Client class GsmClient : public Client
@@ -171,6 +184,13 @@ private:
RxFifo rx; RxFifo rx;
}; };
//============================================================================//
//============================================================================//
// The Secure U201 Client Class
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient class GsmClientSecure : public GsmClient
{ {
public: public:
@@ -191,6 +211,13 @@ public:
} }
}; };
//============================================================================//
//============================================================================//
// The U201 Modem Functions
//============================================================================//
//============================================================================//
public: public:
#ifdef GSM_DEFAULT_STREAM #ifdef GSM_DEFAULT_STREAM
@@ -285,6 +312,10 @@ public:
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED; bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/* /*
* SIM card functions * SIM card functions
*/ */
@@ -335,17 +366,6 @@ public:
return SIM_ERROR; return SIM_ERROR;
} }
RegStatus getRegistrationStatus() {
sendAT(GF("+CGREG?"));
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() { String getOperator() {
sendAT(GF("+COPS?")); sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
@@ -361,6 +381,17 @@ public:
* Generic network functions * Generic network functions
*/ */
RegStatus getRegistrationStatus() {
sendAT(GF("+CGREG?"));
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
int getSignalQuality() { int getSignalQuality() {
sendAT(GF("+CSQ")); sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
@@ -386,6 +417,24 @@ public:
return false; return false;
} }
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* WiFi functions
*/
/* /*
* GPRS functions * GPRS functions
*/ */
@@ -450,32 +499,6 @@ public:
return true; return true;
} }
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.trim();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/* /*
* Messaging functions * Messaging functions
*/ */
@@ -505,6 +528,7 @@ public:
/* /*
* Battery functions * Battery functions
*/ */
// Use: float vBatt = modem.getBattVoltage() / 1000.0; // Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() { uint16_t getBattVoltage() {
sendAT(GF("+CIND")); sendAT(GF("+CIND"));
@@ -615,9 +639,13 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
bool streamSkipUntil(char c) { //TODO: timeout bool streamSkipUntil(char c) {
while (true) { const unsigned long timeout = 1000L;
while (!stream.available()) { TINY_GSM_YIELD(); } unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) if (stream.read() == c)
return true; return true;
} }
@@ -629,7 +657,7 @@ public:
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -637,12 +665,12 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(64);
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
@@ -650,7 +678,7 @@ public:
TINY_GSM_YIELD(); TINY_GSM_YIELD();
while (stream.available() > 0) { while (stream.available() > 0) {
int a = stream.read(); int a = stream.read();
if (a < 0) continue; if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a; data += (char)a;
if (r1 && data.endsWith(r1)) { if (r1 && data.endsWith(r1)) {
index = 1; index = 1;
@@ -691,6 +719,7 @@ finish:
} }
data = ""; data = "";
} }
DBG('<', index, '>');
return index; return index;
} }

View File

@@ -11,9 +11,6 @@
//#define TINY_GSM_DEBUG Serial //#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode #define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode
@@ -29,38 +26,55 @@ enum SimStatus {
SIM_LOCKED = 2, SIM_LOCKED = 2,
}; };
enum XBeeType {
S6B = 0,
LTEC1 = 1,
};
enum RegStatus { enum RegStatus {
REG_UNREGISTERED = 0, REG_OK = 0,
REG_UNREGISTERED = 1,
REG_SEARCHING = 2, REG_SEARCHING = 2,
REG_DENIED = 3, REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4, REG_UNKNOWN = 4,
}; };
// These are responses to the HS command to get "hardware series"
enum XBeeType {
XBEE_S6B_WIFI = 0x601, // Digi XBee® Wi-Fi
XBEE_LTE1_VZN = 0xB01, // Digi XBee® Cellular LTE Cat 1
XBEE_3G = 0xB02, // Digi XBee® Cellular 3G
XBEE3_LTE1_ATT = 1, // Digi XBee3™ Cellular LTE CAT 1 -- HS unknown to SRGD
XBEE3_LTEM_ATT = 2, // Digi XBee3™ Cellular LTE-M -- HS unknown to SRGD
XBEE3_LTENB = 3, // Digi XBee3™ Cellular NB-IoT -- HS unknown to SRGD
};
class TinyGsm
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmXBee Class
//============================================================================//
//============================================================================//
class TinyGsmXBee
{ {
//============================================================================//
//============================================================================//
// The XBee Client Class
//============================================================================//
//============================================================================//
public: public:
class GsmClient : public Client class GsmClient : public Client
{ {
friend class TinyGsm; friend class TinyGsmXBee;
public: public:
GsmClient() {} GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 0) { GsmClient(TinyGsmXBee& modem, uint8_t mux = 0) {
init(&modem, mux); init(&modem, mux);
} }
bool init(TinyGsm* modem, uint8_t mux = 0) { bool init(TinyGsmXBee* modem, uint8_t mux = 0) {
this->at = modem; this->at = modem;
this->mux = mux; this->mux = mux;
sock_connected = false; sock_connected = false;
@@ -73,19 +87,25 @@ public:
public: public:
virtual int connect(const char *host, uint16_t port) { virtual int connect(const char *host, uint16_t port) {
at->streamClear(); // Empty anything remaining in the buffer; at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode(); bool sock_connected = false;
sock_connected = at->modemConnect(host, port, mux, false); if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
at->writeChanges(); sock_connected = at->modemConnect(host, port, mux, false);
at->exitCommand(); at->writeChanges();
at->exitCommand();
}
at->streamClear(); // Empty anything remaining in the buffer;
return sock_connected; return sock_connected;
} }
virtual int connect(IPAddress ip, uint16_t port) { virtual int connect(IPAddress ip, uint16_t port) {
at->streamClear(); // Empty anything remaining in the buffer; at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode(); bool sock_connected = false;
sock_connected = at->modemConnect(ip, port, mux, false); if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
at->writeChanges(); sock_connected = at->modemConnect(ip, port, mux, false);
at->exitCommand(); at->writeChanges();
at->exitCommand();
}
at->streamClear(); // Empty anything remaining in the buffer;
return sock_connected; return sock_connected;
} }
@@ -100,7 +120,7 @@ public:
at->exitCommand(); at->exitCommand();
at->modemSend("", 1, mux); at->modemSend("", 1, mux);
at->commandMode(); at->commandMode();
at->sendAT(GF("TM64")); // Set socket timeout back to 10seconds; at->sendAT(GF("TM64")); // Set socket timeout back to 10 seconds;
at->waitResponse(); at->waitResponse();
at->writeChanges(); at->writeChanges();
at->exitCommand(); at->exitCommand();
@@ -110,7 +130,6 @@ public:
virtual size_t write(const uint8_t *buf, size_t size) { virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux); return at->modemSend(buf, size, mux);
} }
@@ -125,7 +144,7 @@ public:
virtual int read(uint8_t *buf, size_t size) { virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
return at->stream.readBytes(buf, size); return at->stream.readBytes((char*)buf, size);
} }
virtual int read() { virtual int read() {
@@ -151,47 +170,69 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private: private:
TinyGsm* at; TinyGsmXBee* at;
uint8_t mux; uint8_t mux;
bool sock_connected; bool sock_connected;
}; };
//============================================================================//
//============================================================================//
// The Secure XBee Client Class
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient class GsmClientSecure : public GsmClient
{ {
public: public:
GsmClientSecure() {} GsmClientSecure() {}
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1) GsmClientSecure(TinyGsmXBee& modem, uint8_t mux = 1)
: GsmClient(modem, mux) : GsmClient(modem, mux)
{} {}
public: public:
virtual int connect(const char *host, uint16_t port) { virtual int connect(const char *host, uint16_t port) {
at->streamClear(); // Empty anything remaining in the buffer; at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode(); bool sock_connected = false;
sock_connected = at->modemConnect(host, port, mux, true); if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
at->writeChanges(); sock_connected = at->modemConnect(host, port, mux, true);
at->exitCommand(); at->writeChanges();
at->exitCommand();
}
at->streamClear(); // Empty anything remaining in the buffer;
return sock_connected; return sock_connected;
} }
virtual int connect(IPAddress ip, uint16_t port) { virtual int connect(IPAddress ip, uint16_t port) {
at->streamClear(); // Empty anything remaining in the buffer; at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode(); bool sock_connected = false;
sock_connected = at->modemConnect(ip, port, mux, true); if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
at->writeChanges(); sock_connected = at->modemConnect(ip, port, mux, true);
at->exitCommand(); at->writeChanges();
at->exitCommand();
}
at->streamClear(); // Empty anything remaining in the buffer;
return sock_connected; return sock_connected;
} }
}; };
//============================================================================//
//============================================================================//
// The XBee Modem Functions
//============================================================================//
//============================================================================//
public: public:
TinyGsm(Stream& stream) #ifdef GSM_DEFAULT_STREAM
TinyGsmXBee(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmXBee(Stream& stream)
#endif
: stream(stream) : stream(stream)
{ {}
memset(sockets, 0, sizeof(sockets));
}
/* /*
* Basic functions * Basic functions
@@ -201,23 +242,53 @@ public:
} }
bool init() { bool init() {
guardTime = 1100; guardTime = 1100; // Start with a default guard time of 1 second
commandMode();
if (!commandMode(10)) return false; // Try up to 10 times for the init
sendAT(GF("AP0")); // Put in transparent mode sendAT(GF("AP0")); // Put in transparent mode
waitResponse(); bool ret_val = waitResponse() == 1;
ret_val &= writeChanges();
sendAT(GF("GT64")); // shorten the guard time to 100ms sendAT(GF("GT64")); // shorten the guard time to 100ms
ret_val &= waitResponse();
ret_val &= writeChanges();
if (ret_val) guardTime = 125;
sendAT(GF("HS")); // Get the "Hardware Series";
String res = readResponse();
char buf[4] = {0,}; // Set up buffer for response
res.toCharArray(buf, 4);
int intRes = strtol(buf, 0, 16);
beeType = (XBeeType)intRes;
exitCommand();
return ret_val;
}
void setBaud(unsigned long baud) {
if (!commandMode()) return;
switch(baud)
{
case 2400: sendAT(GF("BD1")); break;
case 4800: sendAT(GF("BD2")); break;
case 9600: sendAT(GF("BD3")); break;
case 19200: sendAT(GF("BD4")); break;
case 38400: sendAT(GF("BD5")); break;
case 57600: sendAT(GF("BD6")); break;
case 115200: sendAT(GF("BD7")); break;
case 230400: sendAT(GF("BD8")); break;
case 460800: sendAT(GF("BD9")); break;
case 921600: sendAT(GF("BDA")); break;
default: {
DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud."));
sendAT(GF("BD3")); // Set to default of 9600
break;
}
}
waitResponse(); waitResponse();
writeChanges(); writeChanges();
sendAT(GF("HS")); // Get the "Hardware Series"; 0x601 for S6B (Wifi)
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand(); exitCommand();
if (res == "601") beeType = S6B;
else beeType = LTEC1;
guardTime = 125;
return true;
} }
bool testAT(unsigned long timeout = 10000L) { bool testAT(unsigned long timeout = 10000L) {
@@ -238,52 +309,100 @@ public:
void maintain() {} void maintain() {}
bool factoryDefault() { bool factoryDefault() {
commandMode(); if (!commandMode()) return false; // Return immediately
sendAT(GF("RE")); sendAT(GF("RE"));
bool ret_val = waitResponse() == 1; bool ret_val = waitResponse() == 1;
writeChanges(); ret_val &= writeChanges();
exitCommand(); exitCommand();
return ret_val; return ret_val;
} }
String getModemInfo() {
String modemInf = "";
if (!commandMode()) return modemInf; // Try up to 10 times for the init
sendAT(GF("HS")); // Get the "Hardware Series"
modemInf += readResponse();
exitCommand();
return modemInf;
}
bool hasSSL() { bool hasSSL() {
if (beeType == S6B) return false; if (beeType == XBEE_S6B_WIFI) return false;
else return true; else return true;
} }
XBeeType getBeeType() {
return beeType;
}
String getBeeName() {
switch (beeType){
case XBEE_S6B_WIFI: return "Digi XBee® Wi-Fi";
case XBEE_LTE1_VZN: return "Digi XBee® Cellular LTE Cat 1";
case XBEE_3G: return "Digi XBee® Cellular 3G";
case XBEE3_LTE1_ATT: return "Digi XBee3™ Cellular LTE CAT 1";
case XBEE3_LTEM_ATT: return "Digi XBee3™ Cellular LTE-M";
case XBEE3_LTENB: return "Digi XBee3™ Cellular NB-IoT";
}
}
/* /*
* Power functions * Power functions
*/ */
bool restart() { bool restart() {
commandMode(); if (!commandMode()) return false; // Return immediately
sendAT(GF("AM1")); // Digi suggests putting into airplane mode before restarting
// This allows the sockets and connections to close cleanly
writeChanges();
if (waitResponse() != 1) goto fail;
sendAT(GF("FR")); sendAT(GF("FR"));
if (waitResponse() != 1) { if (waitResponse() != 1) goto fail;
return false;
}
delay (2000); // Actually resets about 2 seconds later delay (2000); // Actually resets about 2 seconds later
// Wait until reboot complete and responds to command mode call again
for (unsigned long start = millis(); millis() - start < 60000L; ) { for (unsigned long start = millis(); millis() - start < 60000L; ) {
if (commandMode()) { if (commandMode(1)) {
sendAT(GF("AM0")); // Turn off airplane mode
writeChanges();
exitCommand(); exitCommand();
return true; delay(250); // wait a litle before trying again
} }
} }
exitCommand(); return true;
return false;;
fail:
exitCommand();
return false;
} }
void setupPinSleep() { void setupPinSleep(bool maintainAssociation = false) {
commandMode(); if (!commandMode()) return; // Return immediately
sendAT(GF("SM"),1); sendAT(GF("SM"),1); // Pin sleep
waitResponse(); waitResponse();
if (beeType == S6B) { if (beeType == XBEE_S6B_WIFI && !maintainAssociation) {
sendAT(GF("SO"),200); sendAT(GF("SO"),200); // For lowest power, dissassociated deep sleep
waitResponse();
}
else if (!maintainAssociation){
sendAT(GF("SO"),1); // For lowest power, dissassociated deep sleep
// Not supported by all modules, will return "ERROR"
waitResponse(); waitResponse();
} }
writeChanges(); writeChanges();
exitCommand(); exitCommand();
} }
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/* /*
* SIM card functions * SIM card functions
*/ */
@@ -293,23 +412,17 @@ public:
} }
String getSimCCID() { String getSimCCID() {
commandMode(); if (!commandMode()) return ""; // Return immediately
sendAT(GF("S#")); sendAT(GF("S#"));
// wait for the response String res = readResponse();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand(); exitCommand();
return res; return res;
} }
String getIMEI() { String getIMEI() {
commandMode(); if (!commandMode()) return ""; // Return immediately
sendAT(GF("IM")); sendAT(GF("IM"));
// wait for the response String res = readResponse();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand(); exitCommand();
return res; return res;
} }
@@ -318,38 +431,10 @@ public:
return SIM_READY; // unsupported return SIM_READY; // unsupported
} }
RegStatus getRegistrationStatus() {
commandMode();
sendAT(GF("AI"));
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
if(res == GF("0"))
return REG_OK_HOME;
else if(res == GF("13") || res == GF("2A"))
return REG_UNREGISTERED;
else if(res == GF("FF") || res == GF("22") || res == GF("23") ||
res == GF("40") || res == GF("41") || res == GF("42"))
return REG_SEARCHING;
else if(res == GF("24") || res == GF("25") || res == GF("27"))
return REG_DENIED;
else return REG_UNKNOWN;
}
String getOperator() { String getOperator() {
commandMode(); if (!commandMode()) return ""; // Return immediately
sendAT(GF("MN")); sendAT(GF("MN"));
// wait for the response String res = readResponse();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand(); exitCommand();
return res; return res;
} }
@@ -358,26 +443,95 @@ public:
* Generic network functions * Generic network functions
*/ */
int getSignalQuality() { RegStatus getRegistrationStatus() {
commandMode(); if (!commandMode()) return REG_UNKNOWN; // Return immediately
if (beeType == S6B) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
else sendAT(GF("DB")); // ask for the cell strength in dBm sendAT(GF("AI"));
// wait for the response String res = readResponse();
unsigned long startMillis = millis(); char buf[3] = {0,}; // Set up buffer for response
while (!stream.available() && millis() - startMillis < 1000) {}; res.toCharArray(buf, 3);
char buf[2] = {0}; // Set up buffer for response int intRes = strtol(buf, 0, 16);
buf[0] = streamRead(); RegStatus stat = REG_UNKNOWN;
buf[1] = streamRead();
// DBG(buf[0], buf[1], "\n"); switch (beeType){
case XBEE_S6B_WIFI: {
if(intRes == 0x00) // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets
stat = REG_OK;
else if(intRes == 0x01) // 0x01 Wi-Fi transceiver initialization in progress.
stat = REG_SEARCHING;
else if(intRes == 0x02) // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point.
stat = REG_SEARCHING;
else if(intRes == 0x13) { // 0x13 Disconnecting from access point.
restart(); // Restart the device; the S6B tends to get stuck "disconnecting"
stat = REG_UNREGISTERED;
}
else if(intRes == 0x23) // 0x23 SSID not configured.
stat = REG_UNREGISTERED;
else if(intRes == 0x24) // 0x24 Encryption key invalid (either NULL or invalid length for WEP).
stat = REG_DENIED;
else if(intRes == 0x27) // 0x27 SSID was found, but join failed.
stat = REG_DENIED;
else if(intRes == 0x40) // 0x40 Waiting for WPA or WPA2 Authentication.
stat = REG_SEARCHING;
else if(intRes == 0x41) // 0x41 Device joined a network and is waiting for IP configuration to complete
stat = REG_SEARCHING;
else if(intRes == 0x42) // 0x42 Device is joined, IP is configured, and listening sockets are being set up.
stat = REG_SEARCHING;
else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID.
stat = REG_SEARCHING;
else stat = REG_UNKNOWN;
break;
}
default: {
if(intRes == 0x00) // 0x00 Connected to the Internet.
stat = REG_OK;
else if(intRes == 0x22) // 0x22 Registering to cellular network.
stat = REG_SEARCHING;
else if(intRes == 0x23) // 0x23 Connecting to the Internet.
stat = REG_SEARCHING;
else if(intRes == 0x24) // 0x24 The cellular component is missing, corrupt, or otherwise in error.
stat = REG_UNKNOWN;
else if(intRes == 0x25) // 0x25 Cellular network registration denied.
stat = REG_DENIED;
else if(intRes == 0x2A) { // 0x2A Airplane mode.
sendAT(GF("AM0")); // Turn off airplane mode
waitResponse();
writeChanges();
stat = REG_UNKNOWN;
}
else if(intRes == 0x2F) { // 0x2F Bypass mode active.
sendAT(GF("AP0")); // Set back to transparent mode
waitResponse();
writeChanges();
stat = REG_UNKNOWN;
}
else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID.
stat = REG_SEARCHING;
else stat = REG_UNKNOWN;
break;
}
}
exitCommand(); exitCommand();
int intr = strtol(buf, 0, 16); return stat;
if (beeType == S6B) return -93 + intr; // the maximum sensitivity is -93dBm }
else return -1*intr; // need to convert to negative number
int getSignalQuality() {
if (!commandMode()) return 0; // Return immediately
if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
else sendAT(GF("DB")); // ask for the cell strength in dBm
String res = readResponse(); // it works better if we read in as a string
exitCommand();
char buf[3] = {0,}; // Set up buffer for response
res.toCharArray(buf, 3);
int intRes = strtol(buf, 0, 16);
if (beeType == XBEE_S6B_WIFI) return -93 + intRes; // the maximum sensitivity is -93dBm
else return -1*intRes; // need to convert to negative number
} }
bool isNetworkConnected() { bool isNetworkConnected() {
RegStatus s = getRegistrationStatus(); RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING); return (s == REG_OK);
} }
bool waitForNetwork(unsigned long timeout = 60000L) { bool waitForNetwork(unsigned long timeout = 60000L) {
@@ -385,30 +539,41 @@ public:
if (isNetworkConnected()) { if (isNetworkConnected()) {
return true; return true;
} }
delay(250); // delay(250); // Enough delay going in and out of command mode
} }
return false; return false;
} }
String getLocalIP() {
if (!commandMode()) return ""; // Return immediately
sendAT(GF("MY"));
String IPaddr; IPaddr.reserve(16);
// wait for the response - this response can be very slow
IPaddr = readResponse(30000);
exitCommand();
IPaddr.trim();
return IPaddr;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/* /*
* WiFi functions * WiFi functions
*/ */
bool networkConnect(const char* ssid, const char* pwd) { bool networkConnect(const char* ssid, const char* pwd) {
commandMode(); if (!commandMode()) return false; // return immediately
sendAT(GF("EE"), 2); // Set security to WPA2 sendAT(GF("EE"), 2); // Set security to WPA2
waitResponse(); if (waitResponse() != 1) goto fail;
sendAT(GF("ID"), ssid); sendAT(GF("ID"), ssid);
if (waitResponse() != 1) { if (waitResponse() != 1) goto fail;
goto fail;
}
sendAT(GF("PK"), pwd); sendAT(GF("PK"), pwd);
if (waitResponse() != 1) { if (waitResponse() != 1) goto fail;
goto fail;
}
writeChanges(); writeChanges();
exitCommand(); exitCommand();
@@ -421,29 +586,19 @@ fail:
} }
bool networkDisconnect() { bool networkDisconnect() {
return false; // Doesn't support disconnecting if (!commandMode()) return false; // return immediately
} sendAT(GF("NR0")); // Do a network reset in order to disconnect
int res = (1 == waitResponse(5000));
String getLocalIP() { writeChanges();
commandMode(); exitCommand();
sendAT(GF("MY")); return res;
String IPaddr; IPaddr.reserve(16);
// wait for the response
unsigned long startMillis = millis();
while (stream.available() < 8 && millis() - startMillis < 30000) {};
IPaddr = streamReadUntil('\r'); // read result
return IPaddr;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
} }
/* /*
* GPRS functions * GPRS functions
*/ */
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
commandMode(); if (!commandMode()) return false; // Return immediately
sendAT(GF("AN"), apn); // Set the APN sendAT(GF("AN"), apn); // Set the APN
waitResponse(); waitResponse();
writeChanges(); writeChanges();
@@ -451,8 +606,20 @@ fail:
return true; return true;
} }
bool gprsDisconnect() { // TODO bool gprsDisconnect() {
return false; if (!commandMode()) return false; // return immediately
sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode
int res = (1 == waitResponse(5000));
writeChanges();
sendAT(GF("AM0")); // Airplane mode off
waitResponse(5000);
writeChanges();
exitCommand();
return res;
}
bool isGprsConnected() {
return isNetworkConnected();
} }
/* /*
@@ -462,35 +629,69 @@ fail:
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS(const String& number, const String& text) { bool sendSMS(const String& number, const String& text) {
commandMode(); if (!commandMode()) return false; // Return immediately
sendAT(GF("IP"), 2); // Put in text messaging mode sendAT(GF("IP"), 2); // Put in text messaging mode
waitResponse(); if (waitResponse() !=1) goto fail;
sendAT(GF("PH"), number); // Set the phone number sendAT(GF("PH"), number); // Set the phone number
waitResponse(); if (waitResponse() !=1) goto fail;
sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriabe return) sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return)
waitResponse(); if (waitResponse() !=1) goto fail;
writeChanges(); if (!writeChanges()) goto fail;
exitCommand(); exitCommand();
stream.print(text); streamWrite(text);
stream.write((char)0x0D); // close off with the carriage return stream.write((char)0x0D); // close off with the carriage return
return true; return true;
fail:
exitCommand();
return false;
} }
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
protected:
private: private:
int modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) {
sendAT(GF("LA"), host);
String strIP; strIP.reserve(16); String strIP; strIP.reserve(16);
// wait for the response
unsigned long startMillis = millis(); unsigned long startMillis = millis();
while (stream.available() < 8 && millis() - startMillis < 30000) {}; bool gotIP = false;
strIP = streamReadUntil('\r'); // read result // XBee's require a numeric IP address for connection, but do provide the
IPAddress ip = TinyGsmIpFromString(strIP); // functionality to look up the IP address from a fully qualified domain name
return modemConnect(ip, port, mux, ssl); while (!gotIP && millis() - startMillis < 45000L) // the lookup can take a while
{
sendAT(GF("LA"), host);
while (stream.available() < 4) {}; // wait for any response
strIP = stream.readStringUntil('\r'); // read result
strIP.trim();
DBG("<<< ", strIP);
if (!strIP.endsWith(GF("ERROR"))) gotIP = true;
delay(100); // short wait before trying again
}
if (gotIP) { // No reason to continue if we don't know the IP address
IPAddress ip = TinyGsmIpFromString(strIP);
return modemConnect(ip, port, mux, ssl);
}
else return false;
} }
int modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) { bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) {
bool success = true;
String host; host.reserve(16); String host; host.reserve(16);
host += ip[0]; host += ip[0];
host += "."; host += ".";
@@ -500,17 +701,17 @@ private:
host += "."; host += ".";
host += ip[3]; host += ip[3];
if (ssl) { if (ssl) {
sendAT(GF("IP"), 4); // Put in TCP mode sendAT(GF("IP"), 4); // Put in SSL over TCP communication mode
waitResponse(); success &= (1 == waitResponse());
} else { } else {
sendAT(GF("IP"), 1); // Put in TCP mode sendAT(GF("IP"), 1); // Put in TCP mode
waitResponse(); success &= (1 == waitResponse());
} }
sendAT(GF("DL"), host); // Set the "Destination Address Low" sendAT(GF("DL"), host); // Set the "Destination Address Low"
waitResponse(); success &= (1 == waitResponse());
sendAT(GF("DE"), String(port, HEX)); // Set the destination port sendAT(GF("DE"), String(port, HEX)); // Set the destination port
int rsp = waitResponse(); success &= (1 == waitResponse());
return rsp; return success;
} }
int modemSend(const void* buff, size_t len, uint8_t mux = 0) { int modemSend(const void* buff, size_t len, uint8_t mux = 0) {
@@ -520,7 +721,7 @@ private:
} }
bool modemGetConnected(uint8_t mux = 0) { bool modemGetConnected(uint8_t mux = 0) {
commandMode(); if (!commandMode()) return false;
sendAT(GF("AI")); sendAT(GF("AI"));
int res = waitResponse(GF("0")); int res = waitResponse(GF("0"));
exitCommand(); exitCommand();
@@ -542,32 +743,33 @@ public:
streamWrite(tail...); streamWrite(tail...);
} }
int streamRead() { return stream.read(); }
String streamReadUntil(char c) {
TINY_GSM_YIELD();
String return_string = stream.readStringUntil(c);
return_string.trim();
// DBG(return_string, c);
return return_string;
}
void streamClear(void) { void streamClear(void) {
while (stream.available()) { streamRead(); } TINY_GSM_YIELD();
while (stream.available()) { stream.read(); }
} }
bool commandMode(void) { bool commandMode(int retries = 2) {
delay(guardTime); // cannot send anything for 1 second before entering command mode int triesMade = 0;
streamWrite(GF("+++")); // enter command mode bool success = false;
// DBG("\r\n+++\r\n"); streamClear(); // Empty everything in the buffer before starting
return 1 == waitResponse(guardTime*2); while (!success and triesMade < retries) {
// Cannot send anything for 1 "guard time" before entering command mode
// Default guard time is 1s, but the init fxn decreases it to 250 ms
delay(guardTime);
streamWrite(GF("+++")); // enter command mode
DBG("\r\n+++");
success = (1 == waitResponse(guardTime*2));
triesMade ++;
}
return success;
} }
void writeChanges(void) { bool writeChanges(void) {
sendAT(GF("WR")); // Write changes to flash sendAT(GF("WR")); // Write changes to flash
waitResponse(); if (1 != waitResponse()) return false;
sendAT(GF("AC")); // Apply changes sendAT(GF("AC")); // Apply changes
waitResponse(); if (1 != waitResponse()) return false;
return true;
} }
void exitCommand(void) { void exitCommand(void) {
@@ -575,12 +777,22 @@ public:
waitResponse(); waitResponse();
} }
String readResponse(uint32_t timeout = 1000) {
TINY_GSM_YIELD();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < timeout) {};
String res = stream.readStringUntil('\r'); // lines end with carriage returns
res.trim();
DBG("<<< ", res);
return res;
}
template<typename... Args> template<typename... Args>
void sendAT(Args... cmd) { void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL); streamWrite("AT", cmd..., GSM_NL);
stream.flush(); stream.flush();
TINY_GSM_YIELD(); TINY_GSM_YIELD();
//DBG("### AT:", cmd...); DBG("### AT:", cmd...);
} }
// TODO: Optimize this! // TODO: Optimize this!
@@ -588,19 +800,19 @@ public:
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{ {
/*String r1s(r1); r1s.trim(); String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim(); String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim(); String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim(); String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim(); String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);
data.reserve(64); data.reserve(16); // Should never be getting much here for the XBee
int index = 0; int index = 0;
unsigned long startMillis = millis(); unsigned long startMillis = millis();
do { do {
TINY_GSM_YIELD(); TINY_GSM_YIELD();
while (stream.available() > 0) { while (stream.available() > 0) {
int a = streamRead(); int a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a; data += (char)a;
if (r1 && data.endsWith(r1)) { if (r1 && data.endsWith(r1)) {
@@ -625,7 +837,7 @@ finish:
if (!index) { if (!index) {
data.trim(); data.trim();
data.replace(GSM_NL GSM_NL, GSM_NL); data.replace(GSM_NL GSM_NL, GSM_NL);
data.replace(GSM_NL, "\r\n" " "); data.replace(GSM_NL, "\r\n ");
if (data.length()) { if (data.length()) {
DBG("### Unhandled:", data, "\r\n"); DBG("### Unhandled:", data, "\r\n");
} else { } else {
@@ -636,7 +848,7 @@ finish:
data.replace(GSM_NL GSM_NL, GSM_NL); data.replace(GSM_NL GSM_NL, GSM_NL);
data.replace(GSM_NL, "\r\n "); data.replace(GSM_NL, "\r\n ");
if (data.length()) { if (data.length()) {
// DBG("<<< ", data); DBG("<<< ", data);
} }
} }
return index; return index;