diff --git a/README.md b/README.md index f7fc0db..eb7a825 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,20 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope ## Features -Feature \ Modem | SIM8xx | Ublox | A6/A7/A20 | M590 | ESP8266 | XBee +Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | M590 | ESP8266 | XBee --- | --- | --- | --- | --- | --- | --- **Data connections** TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ UDP | ◌ | ◌ | | | | ◌ SSL/TLS (HTTPS) | ✔¹ | ✔ | 🅧 | 🅧 | ✔¹ | ✔¹ **USSD** -Sending USSD requests | ✔ | | ✔ | ✔ | 🅧 | -Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | 🅧 | +Sending USSD requests | ✔ | | ✔ | ✔ | 🅧 | +Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | 🅧 | **SMS** -Sending | ✔ | | ✔ | ✔ | 🅧 | ✔ -Sending Unicode | ✔ | | ◌ | 🅧 | 🅧 | -Reading | | | | | 🅧 | -Incoming message event | | | | ? | 🅧 | +Sending | ✔ | ✔ | ✔ | ✔ | 🅧 | ✔ +Sending Unicode | ✔ | | ◌ | 🅧 | 🅧 | +Reading | | | | | 🅧 | +Incoming message event | | | | ? | 🅧 | **Calls** Dial, hangup | ✔ | | ✔ | 🅧 | 🅧 | 🅧 Receiving calls | ✔ | | ✔ | 🅧 | 🅧 | 🅧 @@ -70,7 +70,7 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | - ESP8266 (AT commands interface, similar to GSM modems) - Digi XBee WiFi and Cellular (using XBee command mode) - Neoway M590 -- U-blox SARA U2 (U201, U260, U270) +- u-blox Cellular Modems (LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx) - Quectel BG96 ***(alpha)*** ### Supported boards/modules @@ -141,7 +141,7 @@ For additional functions, please refer to [this example sketch](examples/AllFunc Use this sketch to diagnose your SIM card and GPRS connection: File -> Examples -> TynyGSM -> tools -> [Diagnostics](https://github.com/vshymanskyy/TinyGSM/blob/master/tools/Diagnostics/Diagnostics.ino) - + ### Ensure stable data & power connection Most modules require up to 2A and specific voltage - according to the module documentation. @@ -158,7 +158,7 @@ Try selecting **57600**, **38400**, or even lower - the one that works best for In some cases **9600** is unstable, but using **38400** helps, etc. Be sure to set correct TX/RX pins in the sketch. Please note that not every Arduino pin can serve as TX or RX pin. **Read more about SoftSerial options and configuration [here](https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html) and [here](https://www.arduino.cc/en/Reference/SoftwareSerial).** - + ### ESP32 HardwareSerial When using ESP32 `HardwareSerial`, you may need to specify additional parameters to the `.begin()` call. @@ -180,7 +180,7 @@ This may result in problems such as: * etc. To return module to **Factory Defaults**, use this sketch: - File -> Examples -> TynyGSM -> tools -> [FactoryReset](https://github.com/vshymanskyy/TinyGSM/blob/master/tools/FactoryReset/FactoryReset.ino) + File -> Examples -> TinyGSM -> tools -> [FactoryReset](https://github.com/vshymanskyy/TinyGSM/blob/master/tools/FactoryReset/FactoryReset.ino) ### Goouuu Tech IOT-GA6 vs AI-Thinker A6 confusion diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index 8c73bce..c352cbb 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -34,9 +34,9 @@ #elif defined(TINY_GSM_MODEM_UBLOX) #define TINY_GSM_MODEM_HAS_GPRS #include - typedef TinyGsmU201 TinyGsm; - typedef TinyGsmU201::GsmClient TinyGsmClient; - typedef TinyGsmU201::GsmClientSecure TinyGsmClientSecure; + typedef TinyGsmUBLOX TinyGsm; + typedef TinyGsmUBLOX::GsmClient TinyGsmClient; + typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_BG96) #define TINY_GSM_MODEM_HAS_GPRS @@ -47,24 +47,29 @@ #elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7) #define TINY_GSM_MODEM_HAS_GPRS #include - typedef TinyGsm::GsmClient TinyGsmClient; + typedef TinyGsmA6 TinyGsm; + typedef TinyGsmA6::GsmClient TinyGsmClient; #elif defined(TINY_GSM_MODEM_M590) #define TINY_GSM_MODEM_HAS_GPRS #include - typedef TinyGsm::GsmClient TinyGsmClient; + typedef TinyGsmM590 TinyGsm; + typedef TinyGsmM590::GsmClient TinyGsmClient; #elif defined(TINY_GSM_MODEM_ESP8266) #define TINY_GSM_MODEM_HAS_WIFI #include - typedef TinyGsm::GsmClient TinyGsmClient; - typedef TinyGsm::GsmClientSecure TinyGsmClientSecure; + typedef TinyGsmESP8266 TinyGsm; + typedef TinyGsmESP8266::GsmClient TinyGsmClient; + typedef TinyGsmESP8266::GsmClientSecure TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_XBEE) #define TINY_GSM_MODEM_HAS_GPRS #define TINY_GSM_MODEM_HAS_WIFI #include - typedef TinyGsm::GsmClient TinyGsmClient; + typedef TinyGsmXBee TinyGsm; + typedef TinyGsmXBee::GsmClient TinyGsmClient; + typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure; #else #error "Please define GSM modem model" diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index a53890b..ef6414c 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -38,25 +38,37 @@ enum RegStatus { REG_UNKNOWN = 4, }; +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmA6 Class +//============================================================================// +//============================================================================// -class TinyGsm +class TinyGsmA6 { +//============================================================================// +//============================================================================// +// The Internal A6 Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client { - friend class TinyGsm; + friend class TinyGsmA6; typedef TinyGsmFifo RxFifo; public: GsmClient() {} - GsmClient(TinyGsm& modem) { + GsmClient(TinyGsmA6& modem) { init(&modem); } - bool init(TinyGsm* modem) { + bool init(TinyGsmA6* modem) { this->at = modem; this->mux = -1; sock_connected = false; @@ -162,15 +174,33 @@ public: String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; private: - TinyGsm* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; + TinyGsmA6* at; + uint8_t mux; + bool sock_connected; + RxFifo rx; }; +//============================================================================// +//============================================================================// +// The A6 does not have a secure client! +//============================================================================// +//============================================================================// + + + +//============================================================================// +//============================================================================// +// The A6 Modem Functions +//============================================================================// +//============================================================================// + public: - TinyGsm(Stream& stream) +#ifdef GSM_DEFAULT_STREAM + TinyGsmA6(Stream& stream = GSM_DEFAULT_STREAM) +#else + TinyGsmA6(Stream& stream) +#endif : stream(stream) { memset(sockets, 0, sizeof(sockets)); @@ -209,8 +239,8 @@ public: for (unsigned long start = millis(); millis() - start < timeout; ) { sendAT(GF("")); if (waitResponse(200) == 1) { - delay(100); - return true; + delay(100); + return true; } delay(100); } @@ -307,10 +337,10 @@ public: int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK")); waitResponse(); switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; } } return SIM_ERROR; @@ -370,6 +400,10 @@ public: return false; } + /* + * WiFi functions + */ + /* * GPRS functions */ @@ -650,9 +684,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -737,6 +775,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h index f65da65..94e1643 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -16,7 +16,7 @@ #define TINY_GSM_RX_BUFFER 64 #endif -#define TINY_GSM_MUX_COUNT 5 +#define TINY_GSM_MUX_COUNT 12 #include @@ -39,10 +39,22 @@ enum RegStatus { REG_UNKNOWN = 4, }; +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmBG96 Class +//============================================================================// +//============================================================================// class TinyGsmBG96 { +//============================================================================// +//============================================================================// +// The Internal BG96 Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client @@ -173,6 +185,13 @@ private: RxFifo rx; }; +//============================================================================// +//============================================================================// +// The BG96 Secure Client +//============================================================================// +//============================================================================// + + class GsmClientSecure : public GsmClient { public: @@ -192,9 +211,19 @@ public: } }; +//============================================================================// +//============================================================================// +// The BG96 Modem Functions +//============================================================================// +//============================================================================// + public: +#ifdef GSM_DEFAULT_STREAM + TinyGsmBG96(Stream& stream = GSM_DEFAULT_STREAM) +#else TinyGsmBG96(Stream& stream) +#endif : stream(stream) { memset(sockets, 0, sizeof(sockets)); @@ -344,10 +373,10 @@ public: int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); waitResponse(); switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; } } return SIM_ERROR; @@ -404,6 +433,10 @@ public: return false; } + /* + * WiFi functions + */ + /* * GPRS functions */ @@ -660,9 +693,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -743,6 +780,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientESP8266.h b/src/TinyGsmClientESP8266.h index 44ed149..8777677 100644 --- a/src/TinyGsmClientESP8266.h +++ b/src/TinyGsmClientESP8266.h @@ -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 unsigned TINY_GSM_TCP_KEEP_ALIVE = 120; -class TinyGsm +// 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 Internal Client Class + //============================================================================// + //============================================================================// + public: class GsmClient : public Client { - friend class TinyGsm; + friend class TinyGsmESP8266; typedef TinyGsmFifo RxFifo; public: GsmClient() {} - GsmClient(TinyGsm& modem, uint8_t mux = 1) { + GsmClient(TinyGsmESP8266& modem, uint8_t mux = 1) { init(&modem, mux); } - bool init(TinyGsm* modem, uint8_t mux = 1) { + bool init(TinyGsmESP8266* modem, uint8_t mux = 1) { this->at = modem; this->mux = mux; sock_connected = false; @@ -144,18 +171,25 @@ public: String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; private: - TinyGsm* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; + TinyGsmESP8266* at; + uint8_t mux; + bool sock_connected; + RxFifo rx; }; +//============================================================================// +//============================================================================// +// The Secure ESP8266 Client Class +//============================================================================// +//============================================================================// + + class GsmClientSecure : public GsmClient { public: GsmClientSecure() {} - GsmClientSecure(TinyGsm& modem, uint8_t mux = 1) + GsmClientSecure(TinyGsmESP8266& modem, uint8_t mux = 1) : GsmClient(modem, mux) {} @@ -169,9 +203,20 @@ public: } }; + +//============================================================================// +//============================================================================// +// The ESP8266 Modem Functions +//============================================================================// +//============================================================================// + public: - TinyGsm(Stream& stream) +#ifdef GSM_DEFAULT_STREAM + TinyGsmESP8266(Stream& stream = GSM_DEFAULT_STREAM) +#else + TinyGsmESP8266(Stream& stream) +#endif : stream(stream) { memset(sockets, 0, sizeof(sockets)); @@ -192,6 +237,14 @@ public: if (waitResponse() != 1) { 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; } @@ -203,8 +256,8 @@ public: for (unsigned long start = millis(); millis() - start < timeout; ) { sendAT(GF("")); if (waitResponse(200) == 1) { - delay(100); - return true; + delay(100); + return true; } delay(100); } @@ -255,6 +308,23 @@ public: 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 + */ + + 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; + } /* * Generic network functions @@ -275,21 +345,9 @@ public: return res2; } - bool isNetworkConnected() { - sendAT(GF("+CIPSTATUS")); - int res1 = waitResponse(3000, GF("STATUS:")); - int res2 = 0; - if (res1 == 1) { - res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); - } - // 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 isNetworkConnected() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_IP || s == REG_OK_TCP); } bool waitForNetwork(unsigned long timeout = 60000L) { @@ -298,7 +356,7 @@ public: int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); if (res1 == 2) { 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(); return true; } @@ -312,17 +370,6 @@ public: * 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; @@ -353,6 +400,28 @@ public: return TinyGsmIpFromString(getLocalIP()); } + /* + * 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: bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { @@ -384,21 +453,8 @@ protected: } bool modemGetConnected(uint8_t mux) { - // TODO: re-check this - sendAT(GF("+CIPSTATUS="), mux); - int res1 = waitResponse(3000, GF("STATUS:")); - int res2; - if (res1 == 1) { - res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); - } - // 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; + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_IP || s == REG_OK_TCP); } public: @@ -416,9 +472,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -505,6 +565,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientM590.h b/src/TinyGsmClientM590.h index 21539ae..80252e7 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -39,24 +39,37 @@ enum RegStatus { }; -class TinyGsm +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmM590 Class +//============================================================================// +//============================================================================// + +class TinyGsmM590 { +//============================================================================// +//============================================================================// +// The M590 Internal Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client { - friend class TinyGsm; + friend class TinyGsmM590; typedef TinyGsmFifo RxFifo; public: GsmClient() {} - GsmClient(TinyGsm& modem, uint8_t mux = 1) { + GsmClient(TinyGsmM590& modem, uint8_t mux = 1) { init(&modem, mux); } - bool init(TinyGsm* modem, uint8_t mux = 1) { + bool init(TinyGsmM590* modem, uint8_t mux = 1) { this->at = modem; this->mux = mux; sock_connected = false; @@ -159,15 +172,33 @@ public: String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; private: - TinyGsm* at; + TinyGsmM590* at; uint8_t mux; bool sock_connected; RxFifo rx; }; +//============================================================================// +//============================================================================// +// The M590 Has no Secure client! +//============================================================================// +//============================================================================// + + + +//============================================================================// +//============================================================================// +// The M590 Modem Functions +//============================================================================// +//============================================================================// + public: - TinyGsm(Stream& stream) +#ifdef GSM_DEFAULT_STREAM + TinyGsmM590(Stream& stream = GSM_DEFAULT_STREAM) +#else + TinyGsmM590(Stream& stream) +#endif : stream(stream) { memset(sockets, 0, sizeof(sockets)); @@ -205,8 +236,8 @@ public: for (unsigned long start = millis(); millis() - start < timeout; ) { sendAT(GF("")); if (waitResponse(200) == 1) { - delay(100); - return true; + delay(100); + return true; } delay(100); } @@ -318,10 +349,10 @@ public: int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK")); waitResponse(); switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; } } return SIM_ERROR; @@ -378,6 +409,10 @@ public: return false; } + /* + * WiFi functions + */ + /* * GPRS functions */ @@ -408,12 +443,12 @@ public: } return false; -set_dns: - sendAT(GF("+DNSSERVER=1,8.8.8.8")); - waitResponse(); - - sendAT(GF("+DNSSERVER=2,8.8.4.4")); - waitResponse(); +// set_dns: // TODO +// sendAT(GF("+DNSSERVER=1,8.8.8.8")); +// waitResponse(); +// +// sendAT(GF("+DNSSERVER=2,8.8.4.4")); +// waitResponse(); return true; } @@ -595,9 +630,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -683,6 +722,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientSIM800.h b/src/TinyGsmClientSIM800.h index a841a66..03b367c 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -39,15 +39,28 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum DateTime { +enum TinyGSMDateTimeFormat { DATE_FULL = 0, DATE_TIME = 1, DATE_DATE = 2 }; +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmSim800 Class +//============================================================================// +//============================================================================// + class TinyGsmSim800 { +//============================================================================// +//============================================================================// +// The Sim800 Internal Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client @@ -179,14 +192,21 @@ public: private: TinyGsmSim800* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; + uint8_t mux; + uint16_t sock_available; + uint32_t prev_check; + bool sock_connected; + bool got_data; + RxFifo rx; }; +//============================================================================// +//============================================================================// +// The SIM800 Secure Client +//============================================================================// +//============================================================================// + + class GsmClientSecure : public GsmClient { public: @@ -206,9 +226,19 @@ public: } }; +//============================================================================// +//============================================================================// +// The SIM800 Modem Functions +//============================================================================// +//============================================================================// + public: +#ifdef GSM_DEFAULT_STREAM + TinyGsmSim800(Stream& stream = GSM_DEFAULT_STREAM) +#else TinyGsmSim800(Stream& stream) +#endif : stream(stream) { memset(sockets, 0, sizeof(sockets)); @@ -244,8 +274,8 @@ public: for (unsigned long start = millis(); millis() - start < timeout; ) { sendAT(GF("")); if (waitResponse(200) == 1) { - delay(100); - return true; + delay(100); + return true; } delay(100); } @@ -396,10 +426,10 @@ public: int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); waitResponse(); switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; } } return SIM_ERROR; @@ -456,6 +486,10 @@ public: return false; } + /* + * WiFi functions + */ + /* * GPRS functions */ @@ -734,12 +768,12 @@ public: /* * Time functions */ - String getGSMDateTime(DateTime format) { + String getGSMDateTime(TinyGSMDateTimeFormat format) { sendAT(GF("+CCLK?")); if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { return ""; } - + String res; switch(format) { @@ -892,9 +926,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -975,6 +1013,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientSIM808.h b/src/TinyGsmClientSIM808.h index 2b9c49a..b49ab85 100644 --- a/src/TinyGsmClientSIM808.h +++ b/src/TinyGsmClientSIM808.h @@ -11,6 +11,13 @@ #include + +//============================================================================// +//============================================================================// +// Declaration and Definitio of the TinyGsmSim808 Class +//============================================================================// +//============================================================================// + class TinyGsmSim808: public TinyGsmSim800 { diff --git a/src/TinyGsmClientUBLOX.h b/src/TinyGsmClientUBLOX.h index 9ea3c63..0117155 100644 --- a/src/TinyGsmClientUBLOX.h +++ b/src/TinyGsmClientUBLOX.h @@ -1,13 +1,13 @@ /** - * @file TinyGsmClientU201.h + * @file TinyGsmClientUBLOX.h * @author Volodymyr Shymanskyy * @license LGPL-3.0 * @copyright Copyright (c) 2016 Volodymyr Shymanskyy * @date Nov 2016 */ -#ifndef TinyGsmClientU201_h -#define TinyGsmClientU201_h +#ifndef TinyGsmClientUBLOX_h +#define TinyGsmClientUBLOX_h //#define TINY_GSM_DEBUG Serial @@ -39,31 +39,42 @@ enum RegStatus { REG_UNKNOWN = 4, }; +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmUBLOX Class +//============================================================================// +//============================================================================// -class TinyGsmU201 +class TinyGsmUBLOX { +//============================================================================// +//============================================================================// +// The UBLOX Internal Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client { - friend class TinyGsmU201; + friend class TinyGsmUBLOX; typedef TinyGsmFifo RxFifo; public: GsmClient() {} - GsmClient(TinyGsmU201& modem, uint8_t mux = 1) { + GsmClient(TinyGsmUBLOX& modem, uint8_t mux = 1) { init(&modem, mux); } - bool init(TinyGsmU201* modem, uint8_t mux = 1) { + bool init(TinyGsmUBLOX* modem, uint8_t mux = 1) { this->at = modem; this->mux = mux; sock_available = 0; sock_connected = false; got_data = false; - return true; } @@ -109,7 +120,7 @@ public: virtual int available() { TINY_GSM_YIELD(); - if (!rx.size()) { + if (!rx.size() && sock_connected) { at->maintain(); } return rx.size() + sock_available; @@ -164,7 +175,7 @@ public: String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; private: - TinyGsmU201* at; + TinyGsmUBLOX* at; uint8_t mux; uint16_t sock_available; bool sock_connected; @@ -172,12 +183,19 @@ private: RxFifo rx; }; +//============================================================================// +//============================================================================// +// The Secure UBLOX Client Class +//============================================================================// +//============================================================================// + + class GsmClientSecure : public GsmClient { public: GsmClientSecure() {} - GsmClientSecure(TinyGsmU201& modem, uint8_t mux = 1) + GsmClientSecure(TinyGsmUBLOX& modem, uint8_t mux = 1) : GsmClient(modem, mux) {} @@ -192,12 +210,18 @@ public: } }; +//============================================================================// +//============================================================================// +// The UBLOX Modem Functions +//============================================================================// +//============================================================================// + public: #ifdef GSM_DEFAULT_STREAM - TinyGsmU201(Stream& stream = GSM_DEFAULT_STREAM) + TinyGsmUBLOX(Stream& stream = GSM_DEFAULT_STREAM) #else - TinyGsmU201(Stream& stream) + TinyGsmUBLOX(Stream& stream) #endif : stream(stream) { @@ -305,6 +329,8 @@ public: return true; } + bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* * SIM card functions */ @@ -346,10 +372,10 @@ public: int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); waitResponse(); switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; } } return SIM_ERROR; @@ -406,6 +432,10 @@ public: return false; } + /* + * WiFi functions + */ + /* * GPRS functions */ @@ -507,7 +537,20 @@ public: String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool sendSMS(const String& number, const String& text) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sendSMS(const String& number, const String& text) { + sendAT(GF("+CSCS=\"GSM\"")); // Set GSM default alphabet + waitResponse(); + sendAT(GF("+CMGF=1")); // Set preferred message format to text mode + waitResponse(); + sendAT(GF("+CMGS=\""), number, GF("\"")); // set the phone number + if (waitResponse(GF(">")) != 1) { + return false; + } + stream.print(text); // Actually send the message + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; @@ -649,9 +692,13 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { //TODO: timeout - while (true) { - while (!stream.available()) { TINY_GSM_YIELD(); } + bool streamSkipUntil(char c) { + const unsigned long timeout = 1000L; + unsigned long startMillis = millis(); + while (millis() - startMillis < timeout) { + while (millis() - startMillis < timeout && !stream.available()) { + TINY_GSM_YIELD(); + } if (stream.read() == c) return true; } @@ -727,6 +774,7 @@ finish: } data = ""; } + //DBG('<', index, '>'); return index; } diff --git a/src/TinyGsmClientXBee.h b/src/TinyGsmClientXBee.h index 87dbcda..67413a8 100644 --- a/src/TinyGsmClientXBee.h +++ b/src/TinyGsmClientXBee.h @@ -11,9 +11,6 @@ //#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 @@ -29,38 +26,54 @@ enum SimStatus { SIM_LOCKED = 2, }; -enum XBeeType { - S6B = 0, - LTEC1 = 1, -}; - enum RegStatus { - REG_UNREGISTERED = 0, + REG_OK = 0, + REG_UNREGISTERED = 1, REG_SEARCHING = 2, REG_DENIED = 3, - REG_OK_HOME = 1, - REG_OK_ROAMING = 5, 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 +}; + +//============================================================================// +//============================================================================// +// Declaration of the TinyGsmXBee Class +//============================================================================// +//============================================================================// -class TinyGsm +class TinyGsmXBee { +//============================================================================// +//============================================================================// +// The XBee Internal Client Class +//============================================================================// +//============================================================================// + + public: class GsmClient : public Client { - friend class TinyGsm; + friend class TinyGsmXBee; public: GsmClient() {} - GsmClient(TinyGsm& modem, uint8_t mux = 0) { + GsmClient(TinyGsmXBee& modem, uint8_t mux = 0) { init(&modem, mux); } - bool init(TinyGsm* modem, uint8_t mux = 0) { + bool init(TinyGsmXBee* modem, uint8_t mux = 0) { this->at = modem; this->mux = mux; sock_connected = false; @@ -73,19 +86,25 @@ public: public: virtual int connect(const char *host, uint16_t port) { at->streamClear(); // Empty anything remaining in the buffer; - at->commandMode(); - sock_connected = at->modemConnect(host, port, mux, false); - at->writeChanges(); - at->exitCommand(); + bool sock_connected = false; + if (at->commandMode()) { // Don't try if we didn't successfully get into command mode + sock_connected = at->modemConnect(host, port, mux, false); + at->writeChanges(); + at->exitCommand(); + } + at->streamClear(); // Empty anything remaining in the buffer; return sock_connected; } virtual int connect(IPAddress ip, uint16_t port) { at->streamClear(); // Empty anything remaining in the buffer; - at->commandMode(); - sock_connected = at->modemConnect(ip, port, mux, false); - at->writeChanges(); - at->exitCommand(); + bool sock_connected = false; + if (at->commandMode()) { // Don't try if we didn't successfully get into command mode + sock_connected = at->modemConnect(ip, port, mux, false); + at->writeChanges(); + at->exitCommand(); + } + at->streamClear(); // Empty anything remaining in the buffer; return sock_connected; } @@ -100,7 +119,7 @@ public: at->exitCommand(); at->modemSend("", 1, mux); 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->writeChanges(); at->exitCommand(); @@ -110,7 +129,6 @@ public: virtual size_t write(const uint8_t *buf, size_t size) { TINY_GSM_YIELD(); - //at->maintain(); return at->modemSend(buf, size, mux); } @@ -125,7 +143,7 @@ public: virtual int read(uint8_t *buf, size_t size) { TINY_GSM_YIELD(); - return at->stream.readBytes(buf, size); + return at->stream.readBytes((char*)buf, size); } virtual int read() { @@ -151,47 +169,69 @@ public: String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; private: - TinyGsm* at; + TinyGsmXBee* at; uint8_t mux; bool sock_connected; }; +//============================================================================// +//============================================================================// +// The Secure XBee Client Class +//============================================================================// +//============================================================================// + + class GsmClientSecure : public GsmClient { public: GsmClientSecure() {} - GsmClientSecure(TinyGsm& modem, uint8_t mux = 1) + GsmClientSecure(TinyGsmXBee& modem, uint8_t mux = 1) : GsmClient(modem, mux) {} public: virtual int connect(const char *host, uint16_t port) { at->streamClear(); // Empty anything remaining in the buffer; - at->commandMode(); - sock_connected = at->modemConnect(host, port, mux, true); - at->writeChanges(); - at->exitCommand(); + bool sock_connected = false; + if (at->commandMode()) { // Don't try if we didn't successfully get into command mode + sock_connected = at->modemConnect(host, port, mux, true); + at->writeChanges(); + at->exitCommand(); + } + at->streamClear(); // Empty anything remaining in the buffer; return sock_connected; } virtual int connect(IPAddress ip, uint16_t port) { at->streamClear(); // Empty anything remaining in the buffer; - at->commandMode(); - sock_connected = at->modemConnect(ip, port, mux, true); - at->writeChanges(); - at->exitCommand(); + bool sock_connected = false; + if (at->commandMode()) { // Don't try if we didn't successfully get into command mode + sock_connected = at->modemConnect(ip, port, mux, true); + at->writeChanges(); + at->exitCommand(); + } + at->streamClear(); // Empty anything remaining in the buffer; return sock_connected; } }; + +//============================================================================// +//============================================================================// +// The XBee Modem Functions +//============================================================================// +//============================================================================// + public: - TinyGsm(Stream& stream) +#ifdef GSM_DEFAULT_STREAM + TinyGsmXBee(Stream& stream = GSM_DEFAULT_STREAM) +#else + TinyGsmXBee(Stream& stream) +#endif : stream(stream) - { - memset(sockets, 0, sizeof(sockets)); - } + {} /* * Basic functions @@ -201,23 +241,53 @@ public: } bool init() { - guardTime = 1100; - commandMode(); + guardTime = 1100; // Start with a default guard time of 1 second + + if (!commandMode(10)) return false; // Try up to 10 times for the init + 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 + 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(); 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(); - if (res == "601") beeType = S6B; - else beeType = LTEC1; - guardTime = 125; - return true; } bool testAT(unsigned long timeout = 10000L) { @@ -238,52 +308,100 @@ public: void maintain() {} bool factoryDefault() { - commandMode(); + if (!commandMode()) return false; // Return immediately sendAT(GF("RE")); bool ret_val = waitResponse() == 1; - writeChanges(); + ret_val &= writeChanges(); exitCommand(); 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() { - if (beeType == S6B) return false; + if (beeType == XBEE_S6B_WIFI) return false; 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 */ 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")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) goto fail; + 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; ) { - if (commandMode()) { + if (commandMode(1)) { + sendAT(GF("AM0")); // Turn off airplane mode + writeChanges(); exitCommand(); - return true; + delay(250); // wait a litle before trying again } } - exitCommand(); - return false;; + return true; + + + fail: + exitCommand(); + return false; } - void setupPinSleep() { - commandMode(); - sendAT(GF("SM"),1); + void setupPinSleep(bool maintainAssociation = false) { + if (!commandMode()) return; // Return immediately + sendAT(GF("SM"),1); // Pin sleep waitResponse(); - if (beeType == S6B) { - sendAT(GF("SO"),200); + if (beeType == XBEE_S6B_WIFI && !maintainAssociation) { + 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(); } writeChanges(); 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 */ @@ -293,23 +411,17 @@ public: } String getSimCCID() { - commandMode(); + if (!commandMode()) return ""; // Return immediately sendAT(GF("S#")); - // 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 + String res = readResponse(); exitCommand(); return res; } String getIMEI() { - commandMode(); + if (!commandMode()) return ""; // Return immediately sendAT(GF("IM")); - // 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 + String res = readResponse(); exitCommand(); return res; } @@ -319,37 +431,82 @@ public: } 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; + if (!commandMode()) return REG_UNKNOWN; // Return immediately - 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; + sendAT(GF("AI")); + String res = readResponse(); + char buf[3] = {0,}; // Set up buffer for response + res.toCharArray(buf, 3); + int intRes = strtol(buf, 0, 16); + RegStatus stat = REG_UNKNOWN; + + 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; + } + } - else return REG_UNKNOWN; + exitCommand(); + return stat; } String getOperator() { - commandMode(); + if (!commandMode()) return ""; // Return immediately sendAT(GF("MN")); - // 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 + String res = readResponse(); exitCommand(); return res; } @@ -359,25 +516,21 @@ public: */ int getSignalQuality() { - commandMode(); - if (beeType == S6B) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity + 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 - // wait for the response - unsigned long startMillis = millis(); - while (!stream.available() && millis() - startMillis < 1000) {}; - char buf[2] = {0}; // Set up buffer for response - buf[0] = streamRead(); - buf[1] = streamRead(); - // DBG(buf[0], buf[1], "\n"); + String res = readResponse(); // it works better if we read in as a string exitCommand(); - int intr = strtol(buf, 0, 16); - if (beeType == S6B) return -93 + intr; // the maximum sensitivity is -93dBm - else return -1*intr; // need to convert to negative number + 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() { RegStatus s = getRegistrationStatus(); - return (s == REG_OK_HOME || s == REG_OK_ROAMING); + return (s == REG_OK); } bool waitForNetwork(unsigned long timeout = 60000L) { @@ -385,7 +538,7 @@ public: if (isNetworkConnected()) { return true; } - delay(250); + // delay(250); // Enough delay going in and out of command mode } return false; } @@ -395,20 +548,16 @@ public: */ bool networkConnect(const char* ssid, const char* pwd) { - commandMode(); + if (!commandMode()) return false; // return immediately sendAT(GF("EE"), 2); // Set security to WPA2 - waitResponse(); + if (waitResponse() != 1) goto fail; sendAT(GF("ID"), ssid); - if (waitResponse() != 1) { - goto fail; - } + if (waitResponse() != 1) goto fail; sendAT(GF("PK"), pwd); - if (waitResponse() != 1) { - goto fail; - } + if (waitResponse() != 1) goto fail; writeChanges(); exitCommand(); @@ -421,17 +570,22 @@ fail: } 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)); + writeChanges(); + exitCommand(); + return res; } String getLocalIP() { - commandMode(); + if (!commandMode()) return ""; // Return immediately sendAT(GF("MY")); 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 + // wait for the response - this response can be very slow + IPaddr = readResponse(30000); + exitCommand(); + IPaddr.trim(); return IPaddr; } @@ -443,7 +597,7 @@ fail: * GPRS functions */ 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 waitResponse(); writeChanges(); @@ -451,8 +605,20 @@ fail: return true; } - bool gprsDisconnect() { // TODO - return false; + bool gprsDisconnect() { + 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 +628,67 @@ fail: String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; bool sendSMS(const String& number, const String& text) { - commandMode(); + if (!commandMode()) return false; // Return immediately + sendAT(GF("IP"), 2); // Put in text messaging mode - waitResponse(); + if (waitResponse() !=1) goto fail; sendAT(GF("PH"), number); // Set the phone number - waitResponse(); - sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriabe return) - waitResponse(); - writeChanges(); + if (waitResponse() !=1) goto fail; + sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return) + if (waitResponse() !=1) goto fail; + if (!writeChanges()) goto fail; + exitCommand(); - stream.print(text); + streamWrite(text); stream.write((char)0x0D); // close off with the carriage return return true; + + fail: + exitCommand(); + return false; } + /* + * Location functions + */ -private: + String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; - int modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { - sendAT(GF("LA"), host); + /* + * Battery functions + */ + + uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; + + int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; + +protected: + + bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { String strIP; strIP.reserve(16); - // wait for the response unsigned long startMillis = millis(); - while (stream.available() < 8 && millis() - startMillis < 30000) {}; - strIP = streamReadUntil('\r'); // read result - IPAddress ip = TinyGsmIpFromString(strIP); - return modemConnect(ip, port, mux, ssl); + bool gotIP = false; + // XBee's require a numeric IP address for connection, but do provide the + // functionality to look up the IP address from a fully qualified domain name + 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); host += ip[0]; host += "."; @@ -500,17 +698,17 @@ private: host += "."; host += ip[3]; if (ssl) { - sendAT(GF("IP"), 4); // Put in TCP mode - waitResponse(); + sendAT(GF("IP"), 4); // Put in SSL over TCP communication mode + success &= (1 == waitResponse()); } else { sendAT(GF("IP"), 1); // Put in TCP mode - waitResponse(); + success &= (1 == waitResponse()); } sendAT(GF("DL"), host); // Set the "Destination Address Low" - waitResponse(); + success &= (1 == waitResponse()); sendAT(GF("DE"), String(port, HEX)); // Set the destination port - int rsp = waitResponse(); - return rsp; + success &= (1 == waitResponse()); + return success; } int modemSend(const void* buff, size_t len, uint8_t mux = 0) { @@ -520,7 +718,7 @@ private: } bool modemGetConnected(uint8_t mux = 0) { - commandMode(); + if (!commandMode()) return false; sendAT(GF("AI")); int res = waitResponse(GF("0")); exitCommand(); @@ -542,32 +740,33 @@ public: 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) { - while (stream.available()) { streamRead(); } - } - - bool commandMode(void) { - delay(guardTime); // cannot send anything for 1 second before entering command mode - streamWrite(GF("+++")); // enter command mode - // DBG("\r\n+++\r\n"); - return 1 == waitResponse(guardTime*2); + TINY_GSM_YIELD(); + while (stream.available()) { stream.read(); } + } + + bool commandMode(int retries = 2) { + int triesMade = 0; + bool success = false; + streamClear(); // Empty everything in the buffer before starting + 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("+++"); + success = (1 == waitResponse(guardTime*2)); + triesMade ++; + } + return success; } - void writeChanges(void) { + bool writeChanges(void) { sendAT(GF("WR")); // Write changes to flash - waitResponse(); + if (1 != waitResponse()) return false; sendAT(GF("AC")); // Apply changes - waitResponse(); + if (1 != waitResponse()) return false; + return true; } void exitCommand(void) { @@ -575,6 +774,16 @@ public: 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 void sendAT(Args... cmd) { streamWrite("AT", cmd..., GSM_NL); @@ -594,13 +803,13 @@ public: String r4s(r4); r4s.trim(); String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); + data.reserve(16); // Should never be getting much here for the XBee int index = 0; unsigned long startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { - int a = streamRead(); + int a = stream.read(); if (a <= 0) continue; // Skip 0x00 bytes, just in case data += (char)a; if (r1 && data.endsWith(r1)) { @@ -625,7 +834,7 @@ finish: if (!index) { data.trim(); data.replace(GSM_NL GSM_NL, GSM_NL); - data.replace(GSM_NL, "\r\n" " "); + data.replace(GSM_NL, "\r\n "); if (data.length()) { DBG("### Unhandled:", data, "\r\n"); } else {