diff --git a/README.md b/README.md index eb7a825..822ff16 100644 --- a/README.md +++ b/README.md @@ -35,29 +35,29 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope ## Features -Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | M590 | ESP8266 | XBee ---- | --- | --- | --- | --- | --- | --- +Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | M590 | ESP8266 | XBee | Quectel M95 | +--- | --- | --- | --- | --- | --- | --- | ----------- | **Data connections** -TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ -UDP | ◌ | ◌ | | | | ◌ -SSL/TLS (HTTPS) | ✔¹ | ✔ | 🅧 | 🅧 | ✔¹ | ✔¹ +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 | ✔ | | ✔ | 🅧 | 🅧 | 🅧 -Incoming event (RING) | ◌ | | ◌ | 🅧 | 🅧 | 🅧 -DTMF sending | ✔ | | ✔ | 🅧 | 🅧 | 🅧 -DTMF decoding | ◌ | | 🅧 | 🅧 | 🅧 | 🅧 +Dial, hangup | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? | +Receiving calls | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? | +Incoming event (RING) | ◌ | | ◌ | 🅧 | 🅧 | 🅧 | ? | +DTMF sending | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? | +DTMF decoding | ◌ | | 🅧 | 🅧 | 🅧 | 🅧 | ? | **Location** -GSM location service | ✔ | ✔ | 🅧 | 🅧 | 🅧 | ✔ -GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | 🅧 | 🅧 +GSM location service | ✔ | ✔ | 🅧 | 🅧 | 🅧 | ✔ | 🅧 | +GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | 🅧 | 🅧 | 🅧 | ✔ - implemented  ◌ - planned  🅧 - not available on this modem ¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) @@ -72,6 +72,8 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | - Neoway M590 - u-blox Cellular Modems (LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx) - Quectel BG96 ***(alpha)*** +- Quectel M95 ***(alpha)*** +- Quectel MC60 ***(alpha)*** ### Supported boards/modules - Arduino MKR GSM 1400 @@ -84,8 +86,8 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | - ... other modules, based on supported modems. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration). More modems may be supported later: -- [ ] Quectel M10, M35, M95, UG95, EC21 - [ ] Sequans Monarch LTE Cat M1/NB1 +- [ ] Quectel M10, UG95 - [ ] SIMCom SIM5320, SIM5360, SIM5216, SIM7xxx - [ ] Telit GL865 - [ ] ZTE MG2639 diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index 573e279..fde9f02 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -18,6 +18,8 @@ // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 // #define TINY_GSM_MODEM_M590 +// #define TINY_GSM_MODEM_MC60 +// #define TINY_GSM_MODEM_MC60E // Set serial for debug console (to the Serial Monitor, speed 115200) #define SerialMon Serial @@ -34,6 +36,22 @@ //#define DUMP_AT_COMMANDS #define TINY_GSM_DEBUG SerialMon +#define GSM_AUTOBAUD_MIN 9600 +#define GSM_AUTOBAUD_MAX 38400 + +/* + * Test enabled + */ +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_CALL true +#define TINY_GSM_USE_SMS true +#define TINY_GSM_USE_USSD true +// powerdown modem after tests +#define TINY_GSM_POWERDOWN false + +// set GSM PIN, if any +#define GSM_PIN "" + // Set phone numbers, if you want to test SMS and Calls //#define SMS_TARGET "+380xxxxxxxxx" //#define CALL_TARGET "+380xxxxxxxxx" @@ -64,7 +82,7 @@ void setup() { delay(3000); // Set GSM module baud rate - TinyGsmAutoBaud(SerialAT); + TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); } void loop() { @@ -73,6 +91,10 @@ void loop() { // To skip it, call init() instead of restart() DBG("Initializing modem..."); if (!modem.restart()) { + DBG("Failed to restart modem, delayin 10s and retring"); + delay(3000); + // restart autobaud in case GSM just rebooted + TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); delay(10000); return; } @@ -80,8 +102,10 @@ void loop() { String modemInfo = modem.getModemInfo(); DBG("Modem:", modemInfo); - // Unlock your SIM card with a PIN - //modem.simUnlock("1234"); + // Unlock your SIM card with a PIN if needed + if ( GSM_PIN && modem.getSimStatus() != 3 ) { + modem.simUnlock(GSM_PIN); + } DBG("Waiting for network..."); if (!modem.waitForNetwork()) { @@ -93,11 +117,13 @@ void loop() { DBG("Network connected"); } +#if TINY_GSM_USE_GPRS DBG("Connecting to", apn); if (!modem.gprsConnect(apn, user, pass)) { delay(10000); return; } +#endif bool res = modem.isGprsConnected(); DBG("GPRS status:", res ? "connected" : "not connected"); @@ -110,9 +136,10 @@ void loop() { String cop = modem.getOperator(); DBG("Operator:", cop); - + IPAddress local = modem.localIP(); DBG("Local IP:", local); +#endif int csq = modem.getSignalQuality(); DBG("Signal quality:", csq); @@ -140,15 +167,16 @@ void loop() { String ussd_phone_num = modem.sendUSSD("*161#"); DBG("Phone number (USSD):", ussd_phone_num); +#endif -#if defined(TINY_GSM_MODEM_SIM808) +#if defined(TINY_GSM_MODEM_HAS_GPS) modem.enableGPS(); String gps_raw = modem.getGPSraw(); modem.disableGPS(); DBG("GPS raw data:", gps_raw); #endif -#if defined(SMS_TARGET) +#if TINY_GSM_USE_SMS && defined(SMS_TARGET) res = modem.sendSMS(SMS_TARGET, String("Hello from ") + imei); DBG("SMS:", res ? "OK" : "fail"); @@ -157,7 +185,7 @@ void loop() { DBG("UTF16 SMS:", res ? "OK" : "fail"); #endif -#if defined(CALL_TARGET) +#if TINY_GSM_USE_CALL && defined(CALL_TARGET) DBG("Calling:", CALL_TARGET); // This is NOT supported on M590 @@ -182,21 +210,24 @@ void loop() { } #endif +#if TINY_GSM_USE_GPRS modem.gprsDisconnect(); if (!modem.isGprsConnected()) { DBG("GPRS disconnected"); } else { DBG("GPRS disconnect: Failed."); } +#endif +#if TINY_GSM_POWERDOWN // Try to power-off (modem may decide to restart automatically) // To turn off modem completely, please use Reset/Enable pins modem.poweroff(); DBG("Poweroff."); +#endif // Do nothing forevermore while (true) { modem.maintain(); } } - diff --git a/extras/doc/Digi XBee 3G Global Users Guide - 90001541.pdf b/extras/doc/Digi XBee 3G Global Users Guide - 90001541.pdf new file mode 100644 index 0000000..7cb00e1 Binary files /dev/null and b/extras/doc/Digi XBee 3G Global Users Guide - 90001541.pdf differ diff --git a/extras/doc/Digi XBee3 LTEC1 Users Guide - 90002253.pdf b/extras/doc/Digi XBee3 LTEC1 Users Guide - 90002253.pdf new file mode 100644 index 0000000..67a395a Binary files /dev/null and b/extras/doc/Digi XBee3 LTEC1 Users Guide - 90002253.pdf differ diff --git a/extras/doc/ESP8266 - AT Command Examples 1.3.pdf b/extras/doc/ESP8266 - AT Command Examples 1.3.pdf new file mode 100644 index 0000000..9d0087b Binary files /dev/null and b/extras/doc/ESP8266 - AT Command Examples 1.3.pdf differ diff --git a/extras/doc/ESP8266 - AT Instruction Set v2.0.0.pdf b/extras/doc/ESP8266 - AT Instruction Set v2.0.0.pdf new file mode 100644 index 0000000..ab75959 Binary files /dev/null and b/extras/doc/ESP8266 - AT Instruction Set v2.0.0.pdf differ diff --git a/extras/doc/Gemalto ELS31 AT Commands.pdf b/extras/doc/Gemalto ELS31 AT Commands.pdf new file mode 100644 index 0000000..75f970a Binary files /dev/null and b/extras/doc/Gemalto ELS31 AT Commands.pdf differ diff --git a/extras/doc/Quectel_GSM_SSL_TCP_Application_Note.pdf b/extras/doc/Quectel_GSM_SSL_TCP_Application_Note.pdf new file mode 100644 index 0000000..7f9934e Binary files /dev/null and b/extras/doc/Quectel_GSM_SSL_TCP_Application_Note.pdf differ diff --git a/extras/doc/Quectel_GSM_TCPIP_Recommended_Process_V1.2.pdf b/extras/doc/Quectel_GSM_TCPIP_Recommended_Process_V1.2.pdf new file mode 100644 index 0000000..ef45e09 Binary files /dev/null and b/extras/doc/Quectel_GSM_TCPIP_Recommended_Process_V1.2.pdf differ diff --git a/extras/doc/Quectel_M35_at_command.pdf b/extras/doc/Quectel_M35_AT_Commands_Manual_V1.0.pdf similarity index 100% rename from extras/doc/Quectel_M35_at_command.pdf rename to extras/doc/Quectel_M35_AT_Commands_Manual_V1.0.pdf diff --git a/extras/doc/Quectel_M95_GSM_Specification_V3.1.pdf b/extras/doc/Quectel_M95_GSM_Specification_V3.1.pdf new file mode 100644 index 0000000..b73c228 Binary files /dev/null and b/extras/doc/Quectel_M95_GSM_Specification_V3.1.pdf differ diff --git a/extras/doc/Quectel_M95_at_commands_manual_v1.3.pdf b/extras/doc/Quectel_M95_at_commands_manual_v1.3.pdf new file mode 100644 index 0000000..201fd3e Binary files /dev/null and b/extras/doc/Quectel_M95_at_commands_manual_v1.3.pdf differ diff --git a/extras/doc/Quectel_MC60_AT_Commands_Manual_V1.1.pdf b/extras/doc/Quectel_MC60_AT_Commands_Manual_V1.1.pdf new file mode 100644 index 0000000..392783d Binary files /dev/null and b/extras/doc/Quectel_MC60_AT_Commands_Manual_V1.1.pdf differ diff --git a/extras/doc/SIM800 Hardware Design V1.08.pdf b/extras/doc/SIM800 Hardware Design V1.08.pdf new file mode 100644 index 0000000..fed3779 Binary files /dev/null and b/extras/doc/SIM800 Hardware Design V1.08.pdf differ diff --git a/extras/doc/SIM800 Series AT Command Manual V1.09.pdf b/extras/doc/SIM800 Series AT Command Manual V1.09.pdf new file mode 100644 index 0000000..aabadf4 Binary files /dev/null and b/extras/doc/SIM800 Series AT Command Manual V1.09.pdf differ diff --git a/extras/doc/SIM800 Series AT Command Manual V1.10.pdf b/extras/doc/SIM800 Series AT Command Manual V1.10.pdf new file mode 100644 index 0000000..0ec8983 Binary files /dev/null and b/extras/doc/SIM800 Series AT Command Manual V1.10.pdf differ diff --git a/extras/doc/SIM800 Series Software Upgrade Application Note V1.00.pdf b/extras/doc/SIM800 Series Software Upgrade Application Note V1.00.pdf new file mode 100644 index 0000000..e3b3460 Binary files /dev/null and b/extras/doc/SIM800 Series Software Upgrade Application Note V1.00.pdf differ diff --git a/extras/doc/SIM800+Series Bluetooth Application Note V1.04.pdf b/extras/doc/SIM800+Series Bluetooth Application Note V1.04.pdf new file mode 100644 index 0000000..b0ed6c6 Binary files /dev/null and b/extras/doc/SIM800+Series Bluetooth Application Note V1.04.pdf differ diff --git a/extras/doc/SIM800+Series Email Application Note V1.00.pdf b/extras/doc/SIM800+Series Email Application Note V1.00.pdf new file mode 100644 index 0000000..3664f5e Binary files /dev/null and b/extras/doc/SIM800+Series Email Application Note V1.00.pdf differ diff --git a/extras/doc/Sequans Monarch AT Commands.pdf b/extras/doc/Sequans Monarch AT Commands.pdf new file mode 100644 index 0000000..88bb4f1 Binary files /dev/null and b/extras/doc/Sequans Monarch AT Commands.pdf differ diff --git a/extras/doc/Telit 3G Modules AT Commands Reference Guide r11-1.pdf b/extras/doc/Telit 3G Modules AT Commands Reference Guide r11-1.pdf new file mode 100644 index 0000000..5651699 Binary files /dev/null and b/extras/doc/Telit 3G Modules AT Commands Reference Guide r11-1.pdf differ diff --git a/extras/doc/Telit CE910 AT Commands Reference Guide r5.pdf b/extras/doc/Telit CE910 AT Commands Reference Guide r5.pdf new file mode 100644 index 0000000..d17b57a Binary files /dev/null and b/extras/doc/Telit CE910 AT Commands Reference Guide r5.pdf differ diff --git a/extras/doc/Telit HE910 AT Command Reference Guide r0.pdf b/extras/doc/Telit HE910 AT Command Reference Guide r0.pdf new file mode 100644 index 0000000..012865e Binary files /dev/null and b/extras/doc/Telit HE910 AT Command Reference Guide r0.pdf differ diff --git a/extras/doc/Telit_LE866_AT_Commands_Reference_Guide_r5.pdf b/extras/doc/Telit LE866 AT Commands Reference Guide r5.pdf similarity index 100% rename from extras/doc/Telit_LE866_AT_Commands_Reference_Guide_r5.pdf rename to extras/doc/Telit LE866 AT Commands Reference Guide r5.pdf diff --git a/extras/doc/Telit LE910 AT Commands Reference Guide r14.1.pdf b/extras/doc/Telit LE910 AT Commands Reference Guide r14.1.pdf new file mode 100644 index 0000000..be776b6 Binary files /dev/null and b/extras/doc/Telit LE910 AT Commands Reference Guide r14.1.pdf differ diff --git a/extras/doc/Telit ME910C1 AT Commands Reference Guide r2.pdf b/extras/doc/Telit ME910C1 AT Commands Reference Guide r2.pdf new file mode 100644 index 0000000..3cfe781 Binary files /dev/null and b/extras/doc/Telit ME910C1 AT Commands Reference Guide r2.pdf differ diff --git a/extras/doc/Telit Modules Software User Guide 2G3G4G r20.pdf b/extras/doc/Telit Modules Software User Guide 2G3G4G r20.pdf new file mode 100644 index 0000000..e8787fe Binary files /dev/null and b/extras/doc/Telit Modules Software User Guide 2G3G4G r20.pdf differ diff --git a/extras/doc/u-blox-CEL_ATCommands_(UBX-13002752).pdf b/extras/doc/u-blox Cellular ATCommands R54 (UBX-13002752).pdf similarity index 100% rename from extras/doc/u-blox-CEL_ATCommands_(UBX-13002752).pdf rename to extras/doc/u-blox Cellular ATCommands R54 (UBX-13002752).pdf diff --git a/extras/doc/u-blox Cellular ATCommands R57 (UBX-13002752).pdf b/extras/doc/u-blox Cellular ATCommands R57 (UBX-13002752).pdf new file mode 100644 index 0000000..56a58d1 Binary files /dev/null and b/extras/doc/u-blox Cellular ATCommands R57 (UBX-13002752).pdf differ diff --git a/extras/doc/u-blox SARA-G3 DataSheet (UBX-13000993).pdf b/extras/doc/u-blox SARA-G3 DataSheet (UBX-13000993).pdf new file mode 100644 index 0000000..5281ed9 Binary files /dev/null and b/extras/doc/u-blox SARA-G3 DataSheet (UBX-13000993).pdf differ diff --git a/extras/doc/UBlox SARA-R4 AT Commands Manual (UBX-17003787).pdf b/extras/doc/u-blox SARA-R4 AT Commands Manual (UBX-17003787).pdf similarity index 100% rename from extras/doc/UBlox SARA-R4 AT Commands Manual (UBX-17003787).pdf rename to extras/doc/u-blox SARA-R4 AT Commands Manual (UBX-17003787).pdf diff --git a/library.json b/library.json index 29ac518..3fbc503 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TinyGSM", - "version": "0.3.5", + "version": "0.3.6", "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", "authors": diff --git a/library.properties b/library.properties index 7762874..9a8e1a1 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=TinyGSM -version=0.3.5 +version=0.3.6 author=Volodymyr Shymanskyy maintainer=Volodymyr Shymanskyy sentence=A small Arduino library for GPRS modules, that just works. -paragraph=Includes examples for Blynk, MQTT, File Download, and Web Client. Supports GSM modules with AT command interface: SIM800, SIM900, A6, A7, M590, ESP8266, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968 +paragraph=Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM and wifi modules with AT command interfaces. category=Communication url=https://github.com/vshymanskyy/TinyGSM architectures=* diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index c352cbb..3f477f1 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -13,7 +13,7 @@ #define TINY_GSM_MODEM_HAS_SSL #endif -#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7) +#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7) || defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E) #define TINY_GSM_MODEM_HAS_GPS #endif @@ -38,6 +38,12 @@ typedef TinyGsmUBLOX::GsmClient TinyGsmClient; typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure; +#elif defined(TINY_GSM_MODEM_M95) + #define TINY_GSM_MODEM_HAS_GPRS + #include + typedef TinyGsmM95 TinyGsm; + typedef TinyGsmM95::GsmClient TinyGsmClient; + #elif defined(TINY_GSM_MODEM_BG96) #define TINY_GSM_MODEM_HAS_GPRS #include @@ -56,6 +62,11 @@ typedef TinyGsmM590 TinyGsm; typedef TinyGsmM590::GsmClient TinyGsmClient; +#elif defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E) + #include + typedef TinyGsmMC60 TinyGsm; + typedef TinyGsmMC60::GsmClient TinyGsmClient; + #elif defined(TINY_GSM_MODEM_ESP8266) #define TINY_GSM_MODEM_HAS_WIFI #include diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index e1e4b86..9d486e3 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -39,7 +39,7 @@ enum RegStatus { }; -class TinyGsmA6 +class TinyGsmA6 : public TinyGsmModem { public: @@ -178,7 +178,7 @@ private: public: TinyGsmA6(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -186,11 +186,8 @@ public: /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { if (!testAT()) { return false; } @@ -208,6 +205,15 @@ public: return true; } + String getModemName() { + #if defined(TINY_GSM_MODEM_A6) + return "AI-Thinker A6"; + #elif defined(TINY_GSM_MODEM_A7) + return "AI-Thinker A7"; + #endif + return "AI-Thinker A6"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -251,6 +257,14 @@ public: return false; } + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + /* * Power functions */ @@ -367,22 +381,6 @@ public: return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - bool waitForNetwork(unsigned long timeout = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout; ) { - if (isNetworkConnected()) { - return true; - } - delay(250); - } - return false; - } - - /* - * WiFi functions - */ - bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE; - bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - /* * GPRS functions */ @@ -441,6 +439,10 @@ public: return (res == 1); } + /* + * IP Address functions + */ + String getLocalIP() { sendAT(GF("+CIFSR")); String res; @@ -453,10 +455,6 @@ public: return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ @@ -606,6 +604,10 @@ public: return res; } + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux) { @@ -650,30 +652,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - 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; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h index c2fc95e..0cc6bcf 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -40,7 +40,7 @@ enum RegStatus { }; -class TinyGsmBG96 +class TinyGsmBG96 : public TinyGsmModem { public: @@ -115,7 +115,7 @@ public: virtual int available() { TINY_GSM_YIELD(); - if (!rx.size()) { + if (!rx.size() && sock_connected) { at->maintain(); } return rx.size() + sock_available; @@ -125,7 +125,7 @@ public: TINY_GSM_YIELD(); at->maintain(); size_t cnt = 0; - while (cnt < size) { + while (cnt < size && sock_connected) { size_t chunk = TinyGsmMin(size-cnt, rx.size()); if (chunk > 0) { rx.get(buf, chunk); @@ -179,30 +179,30 @@ private: }; -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - -public: - virtual int connect(const char *host, uint16_t port) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true); - return sock_connected; - } -}; +// class GsmClientSecure : public GsmClient +// { +// public: +// GsmClientSecure() {} +// +// GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1) +// : GsmClient(modem, mux) +// {} +// +// public: +// virtual int connect(const char *host, uint16_t port) { +// stop(); +// TINY_GSM_YIELD(); +// rx.clear(); +// sock_connected = at->modemConnect(host, port, mux, true); +// return sock_connected; +// } +// }; public: TinyGsmBG96(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -210,11 +210,8 @@ public: /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { if (!testAT()) { return false; } @@ -226,6 +223,10 @@ public: return true; } + String getModemName() { + return "Quectel BG96"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -280,6 +281,14 @@ public: return false; // TODO: For now } + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + /* * Power functions */ @@ -401,44 +410,30 @@ public: return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - bool waitForNetwork(unsigned long timeout = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout; ) { - if (isNetworkConnected()) { - return true; - } - delay(250); - } - return false; - } - - /* - * WiFi functions - */ - bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE; - bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - /* * GPRS functions */ bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { gprsDisconnect(); + //Configure the TCPIP Context sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); if (waitResponse() != 1) { return false; } + //Activate GPRS/CSD Context sendAT(GF("+QIACT=1")); if (waitResponse(150000L) != 1) { return false; } + //Attach to Packet Domain service - is this necessary? sendAT(GF("+CGATT=1")); if (waitResponse(60000L) != 1) { return false; } - return true; } @@ -463,12 +458,13 @@ public: return localIP() != 0; } + /* + * IP Address functions + */ + String getLocalIP() { - sendAT(GF("+CGPADDR=1")); - if (waitResponse(10000L, GF(GSM_NL "+CGPADDR:")) != 1) { - return ""; - } - streamSkipUntil(','); + sendAT(GF("+QILOCIP")); + stream.readStringUntil('\n'); String res = stream.readStringUntil('\n'); if (waitResponse() != 1) { return ""; @@ -476,10 +472,6 @@ public: return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ @@ -535,6 +527,8 @@ public: bool sendSMS_UTF16(const String& number, const void* text, size_t len) { sendAT(GF("+CMGF=1")); waitResponse(); + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); sendAT(GF("+CSMP=17,167,0,8")); waitResponse(); @@ -567,14 +561,42 @@ public: /* * Battery functions */ - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_IMPLEMENTED; - int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED; + // Use: float vBatt = modem.getBattVoltage() / 1000.0; + uint16_t getBattVoltage() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return 0; + } + streamSkipUntil(','); // Skip + streamSkipUntil(','); // Skip + + uint16_t res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + int getBattPercent() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return false; + } + stream.readStringUntil(','); + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + /* + * Client related functions + */ protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { int rsp; + // (1-16), (0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE", + // "/",,,(0-2 0=buffer) sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0")); rsp = waitResponse(); @@ -660,30 +682,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - 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; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientESP8266.h b/src/TinyGsmClientESP8266.h index 0561208..1c6daf3 100644 --- a/src/TinyGsmClientESP8266.h +++ b/src/TinyGsmClientESP8266.h @@ -39,7 +39,7 @@ enum RegStatus { -class TinyGsmESP8266 +class TinyGsmESP8266 : public TinyGsmModem { public: @@ -191,10 +191,11 @@ public: } }; + public: TinyGsmESP8266(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -202,11 +203,8 @@ public: /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { if (!testAT()) { return false; } @@ -225,6 +223,10 @@ public: return true; } + String getModemName() { + return "ESP8266"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -266,6 +268,14 @@ public: return true; } + bool hasWifi() { + return true; + } + + bool hasGPRS() { + return false; + } + /* * Power functions */ @@ -362,6 +372,10 @@ public: return retVal; } + /* + * IP Address functions + */ + String getLocalIP() { sendAT(GF("+CIPSTA_CUR??")); int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:")); @@ -373,33 +387,9 @@ public: return res2; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* - * GPRS functions + * Client related functions */ - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) TINY_GSM_ATTR_NOT_AVAILABLE; - bool gprsDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - - /* - * 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: @@ -438,30 +428,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - 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; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientM590.h b/src/TinyGsmClientM590.h index 55e837b..13e190a 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -39,7 +39,7 @@ enum RegStatus { }; -class TinyGsmM590 +class TinyGsmM590 : public TinyGsmModem { public: @@ -175,7 +175,7 @@ private: public: TinyGsmM590(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -183,11 +183,8 @@ public: /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { if (!testAT()) { return false; } @@ -204,6 +201,10 @@ public: return true; } + String getModemName() { + return "Neoway M590"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -255,6 +256,14 @@ public: return false; } + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + /* * Power functions */ @@ -375,22 +384,6 @@ public: return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - bool waitForNetwork(unsigned long timeout = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout; ) { - if (isNetworkConnected()) { - return true; - } - delay(250); - } - return false; - } - - /* - * WiFi functions - */ - bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE; - bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - /* * GPRS functions */ @@ -447,6 +440,10 @@ public: return res == 1; } + /* + * IP Address functions + */ + String getLocalIP() { sendAT(GF("+XIIC?")); if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { @@ -459,10 +456,6 @@ public: return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ @@ -538,6 +531,10 @@ public: int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux) { @@ -595,30 +592,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - 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; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientM95.h b/src/TinyGsmClientM95.h new file mode 100644 index 0000000..29c4c21 --- /dev/null +++ b/src/TinyGsmClientM95.h @@ -0,0 +1,843 @@ +/** + * @file TinyGsmClientM95.h + * @author Volodymyr Shymanskyy, Pacman Pereira, and Replicade Ltd. + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, (c)2017 Replicade Ltd. + * @date Nov 2016 + */ + +#ifndef TinyGsmClientM95_h +#define TinyGsmClientM95_h + +//#define TINY_GSM_DEBUG Serial +//#define TINY_GSM_USE_HEX + +#if !defined(TINY_GSM_RX_BUFFER) + #define TINY_GSM_RX_BUFFER 64 +#endif + +#define TINY_GSM_MUX_COUNT 6 + +#include + +#define GSM_NL "\r\n" +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; + +enum SimStatus { + SIM_ERROR = 0, + SIM_READY = 1, + SIM_LOCKED = 2, +}; + +enum RegStatus { + REG_UNREGISTERED = 0, + REG_SEARCHING = 2, + REG_DENIED = 3, + REG_OK_HOME = 1, + REG_OK_ROAMING = 5, + REG_UNKNOWN = 4, +}; + + +class TinyGsmM95 : public TinyGsmModem +{ + +public: + +class GsmClient : public Client +{ + friend class TinyGsmM95; + typedef TinyGsmFifo RxFifo; + +public: + GsmClient() {} + + GsmClient(TinyGsmM95& modem, uint8_t mux = 1) { + init(&modem, mux); + } + + bool init(TinyGsmM95* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + sock_connected = false; + got_data = false; + + at->sockets[mux] = this; + + return true; + } + +public: + virtual int connect(const char *host, uint16_t port) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux); + return sock_connected; + } + + virtual int connect(IPAddress ip, uint16_t port) { + String host; host.reserve(16); + host += ip[0]; + host += "."; + host += ip[1]; + host += "."; + host += ip[2]; + host += "."; + host += ip[3]; + return connect(host.c_str(), port); + } + + virtual void stop() { + TINY_GSM_YIELD(); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); + rx.clear(); + } + + virtual size_t write(const uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + return at->modemSend(buf, size, mux); + } + + virtual size_t write(uint8_t c) { + return write(&c, 1); + } + + virtual size_t write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); + } + + virtual int available() { + TINY_GSM_YIELD(); + if (!rx.size() && sock_connected) { + at->maintain(); + } + return rx.size() + sock_available; + } + + virtual int read(uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + size_t cnt = 0; + while (cnt < size && sock_connected) { + size_t chunk = TinyGsmMin(size-cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } + // TODO: Read directly into user buffer? + at->maintain(); + if (sock_available > 0) { + at->modemRead(rx.free(), mux); + } else { + break; + } + } + return cnt; + } + + virtual int read() { + uint8_t c; + if (read(&c, 1) == 1) { + return c; + } + return -1; + } + + virtual int peek() { return -1; } //TODO + virtual void flush() { at->stream.flush(); } + + virtual uint8_t connected() { + if (available()) { + return true; + } + return sock_connected; + } + virtual operator bool() { return connected(); } + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + +private: + TinyGsmM95* at; + uint8_t mux; + uint16_t sock_available; + bool sock_connected; + bool got_data; + RxFifo rx; +}; + + +// class GsmClientSecure : public GsmClient +// { +// public: +// GsmClientSecure() {} +// +// GsmClientSecure(TinyGsmm95& modem, uint8_t mux = 1) +// : GsmClient(modem, mux) +// {} +// +// public: +// virtual int connect(const char *host, uint16_t port) { +// stop(); +// TINY_GSM_YIELD(); +// rx.clear(); +// sock_connected = at->modemConnect(host, port, mux, true); +// return sock_connected; +// } +// }; + + +public: + + TinyGsmM95(Stream& stream) + : TinyGsmModem(stream), stream(stream) + { + memset(sockets, 0, sizeof(sockets)); + } + + /* + * Basic functions + */ + + bool init(const char* pin = NULL) { + if (!testAT()) { + return false; + } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off + if (waitResponse() != 1) { + return false; + } +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); + waitResponse(); +#endif + + getSimStatus(); + return true; + } + + String getModemName() { + return "Quectel M95"; + } + + void setBaud(unsigned long baud) { + sendAT(GF("+IPR="), baud); + } + + bool testAT(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("")); + if (waitResponse(200) == 1) { + delay(100); + return true; + } + delay(100); + } + return false; + } + + void maintain() { + for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { + GsmClient* sock = sockets[mux]; + if (sock && sock->got_data) { + sock->got_data = false; + sock->sock_available = modemGetAvailable(mux); + } + } + while (stream.available()) { + waitResponse(10, NULL, NULL); + } + } + + bool factoryDefault() { + sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write + waitResponse(); + sendAT(GF("+IPR=0")); // Auto-baud + waitResponse(); + sendAT(GF("&W")); // Write configuration + return waitResponse() == 1; + } + + String getModemInfo() { + sendAT(GF("I")); + String res; + if (waitResponse(1000L, res) != 1) { + return ""; + } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, " "); + res.trim(); + return res; + } + + bool hasSSL() { + return false; // TODO: For now + } + + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + + /* + * Power functions + */ + + bool restart() { + if (!testAT()) { + return false; + } + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) == 3) { + return false; + } + sendAT(GF("+CFUN=1")); + if (waitResponse(10000L, GF("Call Ready"), GF("OK"), GF("FAIL")) == 3) { + return false; + } + return init(); + } + + bool poweroff() { + sendAT(GF("+QPOWD")); + return waitResponse(GF("POWERED DOWN")) == 1; // TODO + } + + bool radioOff() { + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return true; + } + + /* + * SIM card functions + */ + + bool simUnlock(const char *pin) { + sendAT(GF("+CPIN=\""), pin, GF("\"")); + return waitResponse() == 1; + } + + String getSimCCID() { + sendAT(GF("+ICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + String getIMEI() { + sendAT(GF("+GSN")); + if (waitResponse(GF(GSM_NL)) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + SimStatus getSimStatus(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("+CPIN?")); + if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { + delay(1000); + continue; + } + 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; + } + } + 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() { + sendAT(GF("+COPS?")); + if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { + return ""; + } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + + /* + * Generic network functions + */ + + int getSignalQuality() { + sendAT(GF("+CSQ")); + if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { + return 99; + } + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + bool isNetworkConnected() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + void setHostFormat( bool useDottedQuad ) { + if ( useDottedQuad ) { + sendAT(GF("+QIDNSIP=0")); + } else { + sendAT(GF("+QIDNSIP=1")); + } + waitResponse(); + } + + /* + * GPRS functions + */ + bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + gprsDisconnect(); + + // select foreground context 0 = VIRTUAL_UART_1 + sendAT(GF("+QIFGCNT=0")); + if (waitResponse() != 1) { + return false; + } + + //Select GPRS (=1) as the Bearer + sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); + if (waitResponse() != 1) { + return false; + } + + //Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); + if (waitResponse() != 1) { + return false; + } + + //Activate GPRS/CSD Context + sendAT(GF("+QIACT")); + if (waitResponse(10000) != 1) { + return false; + } + + //Enable multiple TCP/IP connections + sendAT(GF("+QIMUX=1")); + if (waitResponse() != 1) { + return false; + } + + //Request an IP header for received data ("IPD(data length):") + sendAT(GF("+QIHEAD=1")); + if (waitResponse() != 1) { + return false; + } + + //Set Method to Handle Received TCP/IP Data - Retrieve Data by Command + sendAT(GF("+QINDI=1")); + if (waitResponse() != 1) { + return false; + } + + return true; + } + + bool gprsDisconnect() { + sendAT(GF("+QIDEACT")); + return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; + } + + bool isGprsConnected() { + sendAT(GF("+CGATT?")); + if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { + return false; + } + int res = stream.readStringUntil('\n').toInt(); + waitResponse(); + if (res != 1) + return false; + + return localIP() != 0; + } + + /* + * IP Address functions + */ + + String getLocalIP() { + sendAT(GF("+QILOCIP")); + stream.readStringUntil('\n'); + String res = stream.readStringUntil('\n'); + res.trim(); + return res; + } + + /* + * Messaging functions + */ + + String sendUSSD(const String& code) { + sendAT(GF("+CMGF=1")); + waitResponse(); + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + sendAT(GF("+CUSD=1,\""), code, GF("\"")); + if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { + return ""; + } + stream.readStringUntil('"'); + String hex = stream.readStringUntil('"'); + stream.readStringUntil(','); + int dcs = stream.readStringUntil('\n').toInt(); + + if (waitResponse() != 1) { + return ""; + } + + if (dcs == 15) { + return TinyGsmDecodeHex8bit(hex); + } else if (dcs == 72) { + return TinyGsmDecodeHex16bit(hex); + } else { + return hex; + } + } + + bool sendSMS(const String& number, const String& text) { + sendAT(GF("+CMGF=1")); + waitResponse(); + //Set GSM 7 bit default alphabet (3GPP TS 23.038) + sendAT(GF("+CSCS=\"GSM\"")); + waitResponse(); + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + stream.print(text); + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + bool sendSMS_UTF16(const String& number, const void* text, size_t len) { + sendAT(GF("+CMGF=1")); + waitResponse(); + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + sendAT(GF("+CSMP=17,167,0,8")); + waitResponse(); + + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + + uint16_t* t = (uint16_t*)text; + for (size_t i=0; i> 8; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + c = t[i] & 0xFF; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + } + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + /** Delete all SMS */ + bool deleteAllSMS() { + sendAT(GF("+QMGDA=6")); + if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) { + return true; + } + return false; + } + + /* + * 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; + + /* + * Location functions + */ + + String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + + /* + * Battery functions + */ + + // Use: float vBatt = modem.getBattVoltage() / 1000.0; + uint16_t getBattVoltage() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return 0; + } + streamSkipUntil(','); // Skip + streamSkipUntil(','); // Skip + + uint16_t res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + int getBattPercent() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return false; + } + stream.readStringUntil(','); + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + /* + * Client related functions + */ + +protected: + + bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { + sendAT(GF("+QIOPEN="), GF("\"TCP"), GF("\",\""), host, GF("\","), port); + int rsp = waitResponse(75000L, + GF("CONNECT OK" GSM_NL), + GF("CONNECT FAIL" GSM_NL), + GF("ALREADY CONNECT" GSM_NL)); + if ( rsp != 1 ) { + return false; + } + return (1 == rsp); + } + + int modemSend(const void* buff, size_t len, uint8_t mux) { + sendAT(GF("+QISEND="), mux, ',', len); + if (waitResponse(GF(">")) != 1) { + return 0; + } + stream.write((uint8_t*)buff, len); + stream.flush(); + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { + return 0; + } + + bool allAcknowledged = false; + // bool failed = false; + while ( !allAcknowledged ) { + sendAT( GF("+QISACK")); + if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { + return -1; + } else { + streamSkipUntil(','); /** Skip total */ + streamSkipUntil(','); /** Skip acknowledged data size */ + if ( stream.readStringUntil('\n').toInt() == 0 ) { + allAcknowledged = true; + } + } + } + waitResponse(5000L); + + // streamSkipUntil(','); // Skip mux + // return stream.readStringUntil('\n').toInt(); + return 1; + } + + size_t modemRead(size_t size, uint8_t mux) { + sendAT(GF("+QIRD="), mux, ',', size); + if (waitResponse(GF("+QIRD:")) != 1) { + return 0; + } + size_t len = stream.readStringUntil('\n').toInt(); + + for (size_t i=0; irx.put(c); + } + waitResponse(); + DBG("### READ:", mux, ",", len); + return len; + } + + size_t modemGetAvailable(uint8_t mux) { + sendAT(GF("+QIRD="), mux, GF(",0")); + size_t result = 0; + if (waitResponse(GF("+QIRD:")) == 1) { + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read + result = stream.readStringUntil('\n').toInt(); + DBG("### STILL:", mux, "has", result); + waitResponse(); + } + if (!result) { + sockets[mux]->sock_connected = modemGetConnected(mux); + } + return result; + } + + bool modemGetConnected(uint8_t mux) { + sendAT(GF("+QISTATE=1,"), mux); + //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + + if (waitResponse(GF("+QISTATE:"))) + return false; + + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state + + waitResponse(); + + // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing + return 2 == res; + } + +public: + + /* + Utilities + */ + + template + void sendAT(Args... cmd) { + streamWrite("AT", cmd..., GSM_NL); + stream.flush(); + TINY_GSM_YIELD(); + //DBG("### AT:", cmd...); + } + + // TODO: Optimize this! + uint8_t waitResponse(uint32_t timeout, String& data, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + /*String r1s(r1); r1s.trim(); + String r2s(r2); r2s.trim(); + String r3s(r3); r3s.trim(); + String r4s(r4); r4s.trim(); + String r5s(r5); r5s.trim(); + DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ + data.reserve(64); + int index = 0; + unsigned long startMillis = millis(); + do { + TINY_GSM_YIELD(); + while (stream.available() > 0) { + int a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += (char)a; + if (r1 && data.endsWith(r1)) { + index = 1; + goto finish; + } else if (r2 && data.endsWith(r2)) { + index = 2; + goto finish; + } else if (r3 && data.endsWith(r3)) { + index = 3; + goto finish; + } else if (r4 && data.endsWith(r4)) { + index = 4; + goto finish; + } else if (r5 && data.endsWith(r5)) { + index = 5; + goto finish; + } else if (data.endsWith(GF(GSM_NL "+QIRD:"))) { + streamSkipUntil(','); // Skip the context + streamSkipUntil(','); // Skip the role + int mux = stream.readStringUntil('\n').toInt(); + DBG("### Got Data:", mux); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + } + } else if (data.endsWith(GF("CLOSED" GSM_NL))) { + int nl = data.lastIndexOf(GSM_NL, data.length()-8); + int coma = data.indexOf(',', nl+2); + int mux = data.substring(nl+2, coma).toInt(); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + DBG("### Closed: ", mux); + } + } + } while (millis() - startMillis < timeout); +finish: + if (!index) { + data.trim(); + if (data.length()) { + DBG("### Unhandled:", data); + } + data = ""; + } + //DBG('<', index, '>'); + return index; + } + + uint8_t waitResponse(uint32_t timeout, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + String data; + return waitResponse(timeout, data, r1, r2, r3, r4, r5); + } + + uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + return waitResponse(1000, r1, r2, r3, r4, r5); + } + +public: + Stream& stream; + +protected: + GsmClient* sockets[TINY_GSM_MUX_COUNT]; +}; + +#endif diff --git a/src/TinyGsmClientMC60.h b/src/TinyGsmClientMC60.h new file mode 100644 index 0000000..db4379d --- /dev/null +++ b/src/TinyGsmClientMC60.h @@ -0,0 +1,874 @@ +/** + * @file TinyGsmClientMC60.h + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Nov 2016 + * + * @MC60 support added by Tamas Dajka 2017.10.15 - with fixes by Sara Damiano + * + */ + +#ifndef TinyGsmClientMC60_h +#define TinyGsmClientMC60_h + +//#define TINY_GSM_DEBUG Serial +//#define TINY_GSM_USE_HEX + +#if !defined(TINY_GSM_RX_BUFFER) + #define TINY_GSM_RX_BUFFER 64 +#endif + +#define TINY_GSM_MUX_COUNT 6 + +#include + +#define GSM_NL "\r\n" +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; + +enum SimStatus { + SIM_ERROR = 0, + SIM_READY = 1, + SIM_LOCKED = 2, + SIM_ANTITHEFT_LOCKED = 3, +}; + +enum RegStatus { + REG_UNREGISTERED = 0, + REG_SEARCHING = 2, + REG_DENIED = 3, + REG_OK_HOME = 1, + REG_OK_ROAMING = 5, + REG_UNKNOWN = 4, +}; + + +class TinyGsmMC60 : public TinyGsmModem +{ + +public: + +class GsmClient : public Client +{ + friend class TinyGsmMC60; + typedef TinyGsmFifo RxFifo; + +public: + GsmClient() {} + + GsmClient(TinyGsmMC60& modem, uint8_t mux = 1) { + init(&modem, mux); + } + + bool init(TinyGsmMC60* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + sock_connected = false; + got_data = false; + + at->sockets[mux] = this; + + return true; + } + +public: + virtual int connect(const char *host, uint16_t port) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux); + return sock_connected; + } + + virtual int connect(IPAddress ip, uint16_t port) { + String host; host.reserve(16); + host += ip[0]; + host += "."; + host += ip[1]; + host += "."; + host += ip[2]; + host += "."; + host += ip[3]; + return connect(host.c_str(), port); + } + + virtual void stop() { + TINY_GSM_YIELD(); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); + rx.clear(); + } + + virtual size_t write(const uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + return at->modemSend(buf, size, mux); + } + + virtual size_t write(uint8_t c) { + return write(&c, 1); + } + + virtual size_t write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); + } + + virtual int available() { + TINY_GSM_YIELD(); + if (!rx.size() && sock_connected) { + at->maintain(); + } + return rx.size() + sock_available; + } + + virtual int read(uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + size_t cnt = 0; + while (cnt < size && sock_connected) { + size_t chunk = TinyGsmMin(size-cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } + // TODO: Read directly into user buffer? + at->maintain(); + if (sock_available > 0) { + at->modemRead(rx.free(), mux); + } else { + break; + } + } + return cnt; + } + + virtual int read() { + uint8_t c; + if (read(&c, 1) == 1) { + return c; + } + return -1; + } + + virtual int peek() { return -1; } //TODO + virtual void flush() { at->stream.flush(); } + + virtual uint8_t connected() { + if (available()) { + return true; + } + return sock_connected; + } + virtual operator bool() { return connected(); } + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + +private: + TinyGsmMC60* at; + uint8_t mux; + uint16_t sock_available; + bool sock_connected; + bool got_data; + RxFifo rx; +}; + + +// class GsmClientSecure : public GsmClient +// { +// public: +// GsmClientSecure() {} +// +// GsmClientSecure(TinyGsmMC60& modem, uint8_t mux = 1) +// : GsmClient(modem, mux) +// {} +// +// public: +// virtual int connect(const char *host, uint16_t port) { +// stop(); +// TINY_GSM_YIELD(); +// rx.clear(); +// sock_connected = at->modemConnect(host, port, mux, true); +// return sock_connected; +// } +// }; + + +public: + + TinyGsmMC60(Stream& stream) + : TinyGsmModem(stream), stream(stream) + { + memset(sockets, 0, sizeof(sockets)); + } + + /* + * Basic functions + */ + + bool init(const char* pin = NULL) { + if (!testAT()) { + return false; + } + sendAT(GF("&FZ")); // Factory + Reset + waitResponse(); + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { + return false; + } + getSimStatus(); + return true; + } + + String getModemName() { + #if defined(TINY_GSM_MODEM_MC60) + return "Quectel MC60"; + #elif defined(TINY_GSM_MODEM_MC60E) + return "Quectel MC60E"; + #endif + return "Quectel MC60"; + } + + void setBaud(unsigned long baud) { + sendAT(GF("+IPR="), baud); + } + + bool testAT(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("")); + if (waitResponse(200) == 1) { + delay(100); + return true; + } + delay(100); + } + return false; + } + + void maintain() { + for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { + GsmClient* sock = sockets[mux]; + if (sock && sock->got_data) { + sock->got_data = false; + sock->sock_available = modemGetAvailable(mux); + } + } + while (stream.available()) { + waitResponse(10, NULL, NULL); + } + } + + bool factoryDefault() { + sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write + waitResponse(); + sendAT(GF("+IPR=0")); // Auto-baud + waitResponse(); + sendAT(GF("&W")); // Write configuration + return waitResponse() == 1; + } + + String getModemInfo() { + sendAT(GF("I")); + String res; + if (waitResponse(1000L, res) != 1) { + return ""; + } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, " "); + res.trim(); + return res; + } + + /* + * under development + */ + // bool hasSSL() { + // sendAT(GF("+QIPSSL=?")); + // if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { + // return false; + // } + // return waitResponse() == 1; + // } + + bool hasSSL() { + return false; // TODO: For now + } + + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + + /* + * Power functions + */ + + bool restart() { + if (!testAT()) { + return false; + } + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + sendAT(GF("+CFUN=1,1")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return init(); + } + + bool poweroff() { + sendAT(GF("+QPOWD=1")); + return waitResponse(GF("NORMAL POWER DOWN")) == 1; + } + + bool radioOff() { + if (!testAT()) { + return false; + } + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return true; + } + + /* + * SIM card functions + */ + + bool simUnlock(const char *pin) { + sendAT(GF("+CPIN=\""), pin, GF("\"")); + return waitResponse() == 1; + } + + String getSimCCID() { + sendAT(GF("+ICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + String getIMEI() { + sendAT(GF("+GSN")); + if (waitResponse(GF(GSM_NL)) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + SimStatus getSimStatus(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("+CPIN?")); + if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { + delay(1000); + continue; + } + int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"), GF("PH_SIM PIN"), GF("PH_SIM PUK")); + waitResponse(); + switch (status) { + case 2: + case 3: return SIM_LOCKED; + case 5: + case 6: return SIM_ANTITHEFT_LOCKED; + case 1: return SIM_READY; + default: 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() { + sendAT(GF("+COPS?")); + if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { + return ""; + } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + + /* + * Generic network functions + */ + + int getSignalQuality() { + sendAT(GF("+CSQ")); + if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { + return 99; + } + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + bool isNetworkConnected() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + /* + * GPRS functions + */ + bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + gprsDisconnect(); + + // select foreground context 0 = VIRTUAL_UART_1 + sendAT(GF("+QIFGCNT=0")); + if (waitResponse() != 1) { + return false; + } + + //Select GPRS (=1) as the Bearer + sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); + if (waitResponse() != 1) { + return false; + } + + //Define PDP context - is this necessary? + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); + waitResponse(); + + // Activate PDP context - is this necessary? + sendAT(GF("+CGACT=1,1")); + waitResponse(60000L); + + //Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); + if (waitResponse() != 1) { + return false; + } + + //Activate GPRS/CSD Context + sendAT(GF("+QIACT")); + if (waitResponse(60000L) != 1) { + return false; + } + + //Enable multiple TCP/IP connections + sendAT(GF("+QIMUX=1")); + if (waitResponse() != 1) { + return false; + } + + //Request an IP header for received data ("IPD(data length):") + sendAT(GF("+QIHEAD=1")); + if (waitResponse() != 1) { + return false; + } + + //Set Method to Handle Received TCP/IP Data - Retrieve Data by Command + sendAT(GF("+QINDI=1")); + if (waitResponse() != 1) { + return false; + } + + // Check that we have a local IP address + if (localIP() != 0) { + return true; + } + + return false; + } + + bool gprsDisconnect() { + sendAT(GF("+QIDEACT")); + return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; + } + + bool isGprsConnected() { + sendAT(GF("+CGATT?")); + if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { + return false; + } + int res = stream.readStringUntil('\n').toInt(); + waitResponse(); + if (res != 1) + return false; + + return localIP() != 0; + } + + /* + * IP Address functions + */ + + String getLocalIP() { + sendAT(GF("+QILOCIP")); + stream.readStringUntil('\n'); + String res = stream.readStringUntil('\n'); + res.trim(); + return res; + } + + /* + * Messaging functions + */ + + String sendUSSD(const String& code) { + sendAT(GF("+CMGF=1")); + waitResponse(); + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + sendAT(GF("+CUSD=1,\""), code, GF("\"")); + if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { + return ""; + } + stream.readStringUntil('"'); + String hex = stream.readStringUntil('"'); + stream.readStringUntil(','); + int dcs = stream.readStringUntil('\n').toInt(); + + if (waitResponse() != 1) { + return ""; + } + + if (dcs == 15) { + return TinyGsmDecodeHex8bit(hex); + } else if (dcs == 72) { + return TinyGsmDecodeHex16bit(hex); + } else { + return hex; + } + } + + bool sendSMS(const String& number, const String& text) { + sendAT(GF("+CMGF=1")); + waitResponse(); + //Set GSM 7 bit default alphabet (3GPP TS 23.038) + sendAT(GF("+CSCS=\"GSM\"")); + waitResponse(); + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + stream.print(text); + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + bool sendSMS_UTF16(const String& number, const void* text, size_t len) { + sendAT(GF("+CMGF=1")); + waitResponse(); + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + sendAT(GF("+CSMP=17,167,0,8")); + waitResponse(); + + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + + uint16_t* t = (uint16_t*)text; + for (size_t i=0; i> 8; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + c = t[i] & 0xFF; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + } + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + /** Delete all SMS */ + bool deleteAllSMS() { + sendAT(GF("+QMGDA=6")); + if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) { + return true; + } + return false; + } + + /* + * Location functions + */ + + String getGsmLocation() { + sendAT(GF("+CIPGSMLOC=1,1")); + if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + /* + * Battery functions + */ + + // Use: float vBatt = modem.getBattVoltage() / 1000.0; + uint16_t getBattVoltage() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return 0; + } + streamSkipUntil(','); // Skip + streamSkipUntil(','); // Skip + + uint16_t res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + int getBattPercent() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return false; + } + stream.readStringUntil(','); + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + /* + * Client related functions + */ + +protected: + + bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { + sendAT(GF("+QIOPEN="), GF("\"TCP"), GF("\",\""), host, GF("\","), port); + int rsp = waitResponse(75000L, + GF("CONNECT OK" GSM_NL), + GF("CONNECT FAIL" GSM_NL), + GF("ALREADY CONNECT" GSM_NL)); + if ( rsp != 1 ) { + return false; + } + return (1 == rsp); + } + + int modemSend(const void* buff, size_t len, uint8_t mux) { + sendAT(GF("+QISEND="), mux, ',', len); + if (waitResponse(GF(">")) != 1) { + return 0; + } + stream.write((uint8_t*)buff, len); + stream.flush(); + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { + return 0; + } + + bool allAcknowledged = false; + // bool failed = false; + while ( !allAcknowledged ) { + sendAT( GF("+QISACK")); + if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { + return -1; + } else { + streamSkipUntil(','); /** Skip total */ + streamSkipUntil(','); /** Skip acknowledged data size */ + if ( stream.readStringUntil('\n').toInt() == 0 ) { + allAcknowledged = true; + } + } + } + waitResponse(5000L); + + // streamSkipUntil(','); // Skip mux + // return stream.readStringUntil('\n').toInt(); + return 1; + } + + size_t modemRead(size_t size, uint8_t mux) { + sendAT(GF("+QIRD="), mux, ',', size); + if (waitResponse(GF("+QIRD:")) != 1) { + return 0; + } + size_t len = stream.readStringUntil('\n').toInt(); + + for (size_t i=0; irx.put(c); + } + waitResponse(); + DBG("### READ:", mux, ",", len); + return len; + } + + size_t modemGetAvailable(uint8_t mux) { + sendAT(GF("+QIRD="), mux, GF(",0")); + size_t result = 0; + if (waitResponse(GF("+QIRD:")) == 1) { + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read + result = stream.readStringUntil('\n').toInt(); + DBG("### STILL:", mux, "has", result); + waitResponse(); + } + if (!result) { + sockets[mux]->sock_connected = modemGetConnected(mux); + } + return result; + } + + bool modemGetConnected(uint8_t mux) { + sendAT(GF("+QISTATE=1,"), mux); + //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + + if (waitResponse(GF("+QISTATE:"))) + return false; + + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state + + waitResponse(); + + // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing + return 2 == res; + } + +public: + + /* + Utilities + */ + + template + void sendAT(Args... cmd) { + streamWrite("AT", cmd..., GSM_NL); + stream.flush(); + TINY_GSM_YIELD(); + //DBG("### AT:", cmd...); + } + + // TODO: Optimize this! + uint8_t waitResponse(uint32_t timeout, String& data, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) + { + /*String r1s(r1); r1s.trim(); + String r2s(r2); r2s.trim(); + String r3s(r3); r3s.trim(); + String r4s(r4); r4s.trim(); + String r5s(r5); r5s.trim(); + String r6s(r6); r6s.trim(); + DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s, ",", r6s);*/ + data.reserve(64); + int index = 0; + unsigned long startMillis = millis(); + do { + TINY_GSM_YIELD(); + while (stream.available() > 0) { + int a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += (char)a; + if (r1 && data.endsWith(r1)) { + index = 1; + goto finish; + } else if (r2 && data.endsWith(r2)) { + index = 2; + goto finish; + } else if (r3 && data.endsWith(r3)) { + index = 3; + goto finish; + } else if (r4 && data.endsWith(r4)) { + index = 4; + goto finish; + } else if (r5 && data.endsWith(r5)) { + index = 5; + goto finish; + } else if (r6 && data.endsWith(r6)) { + index = 6; + goto finish; + } else if (data.endsWith(GF(GSM_NL "+QIRD:"))) { + streamSkipUntil(','); // Skip the context + streamSkipUntil(','); // Skip the role + int mux = stream.readStringUntil('\n').toInt(); + DBG("### Got Data:", mux); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + } + } else if (data.endsWith(GF("CLOSED" GSM_NL))) { + int nl = data.lastIndexOf(GSM_NL, data.length()-8); + int coma = data.indexOf(',', nl+2); + int mux = data.substring(nl+2, coma).toInt(); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + DBG("### Closed: ", mux); + } + } + } while (millis() - startMillis < timeout); +finish: + if (!index) { + data.trim(); + if (data.length()) { + DBG("### Unhandled:", data); + } + data = ""; + } + //DBG('<', index, '>'); + return index; + } + + uint8_t waitResponse(uint32_t timeout, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) + { + String data; + return waitResponse(timeout, data, r1, r2, r3, r4, r5, r6); + } + + uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) + { + return waitResponse(1000, r1, r2, r3, r4, r5, r6); + } + +public: + Stream& stream; + +protected: + GsmClient* sockets[TINY_GSM_MUX_COUNT]; +}; + +#endif diff --git a/src/TinyGsmClientSIM800.h b/src/TinyGsmClientSIM800.h index 4da8aa9..10bba51 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -45,8 +45,7 @@ enum TinyGSMDateTimeFormat { DATE_DATE = 2 }; - -class TinyGsmSim800 +class TinyGsmSim800 : public TinyGsmModem { public: @@ -217,7 +216,7 @@ public: public: TinyGsmSim800(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -225,11 +224,8 @@ public: /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { if (!testAT()) { return false; } @@ -243,6 +239,19 @@ public: return true; } + String getModemName() { + #if defined(TINY_GSM_MODEM_SIM800) + return "SIMCom SIM800"; + #elif defined(TINY_GSM_MODEM_SIM808) + return "SIMCom SIM808"; + #elif defined(TINY_GSM_MODEM_SIM868) + return "SIMCom SIM868"; + #elif defined(TINY_GSM_MODEM_SIM900) + return "SIMCom SIM900"; + #endif + return "SIMCom SIM800"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -312,6 +321,14 @@ public: #endif } + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + /* * Power functions */ @@ -455,22 +472,6 @@ public: return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - bool waitForNetwork(unsigned long timeout = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout; ) { - if (isNetworkConnected()) { - return true; - } - delay(250); - } - return false; - } - - /* - * WiFi functions - */ - bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE; - bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - /* * GPRS functions */ @@ -591,6 +592,10 @@ public: return true; } + /* + * IP Address functions + */ + String getLocalIP() { sendAT(GF("+CIFSR;E0")); String res; @@ -603,9 +608,6 @@ public: return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } /* * Phone Call functions @@ -800,6 +802,10 @@ public: return res; } + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { @@ -894,30 +900,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - bool streamSkipUntil(const char c, const unsigned long timeout = 3000L) { - unsigned long startMillis = millis(); - while (millis() - startMillis < timeout) { - while (millis() - startMillis < timeout && !stream.available()) { - TINY_GSM_YIELD(); - } - if (stream.read() == c) - return true; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientSIM808.h b/src/TinyGsmClientSIM808.h index 94d7428..7f3417e 100644 --- a/src/TinyGsmClientSIM808.h +++ b/src/TinyGsmClientSIM808.h @@ -27,7 +27,7 @@ public: // enable GPS bool enableGPS() { - uint16_t state; + // uint16_t state; sendAT(GF("+CGNSPWR=1")); if (waitResponse() != 1) { @@ -38,7 +38,7 @@ public: } bool disableGPS() { - uint16_t state; + // uint16_t state; sendAT(GF("+CGNSPWR=0")); if (waitResponse() != 1) { @@ -65,7 +65,7 @@ public: // works only with ans SIM808 V2 bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) { //String buffer = ""; - char chr_buffer[12]; + // char chr_buffer[12]; bool fix = false; sendAT(GF("+CGNSINF")); diff --git a/src/TinyGsmClientUBLOX.h b/src/TinyGsmClientUBLOX.h index 38fad37..a624d4b 100644 --- a/src/TinyGsmClientUBLOX.h +++ b/src/TinyGsmClientUBLOX.h @@ -40,7 +40,7 @@ enum RegStatus { }; -class TinyGsmUBLOX +class TinyGsmUBLOX : public TinyGsmModem { public: @@ -123,7 +123,7 @@ public: TINY_GSM_YIELD(); at->maintain(); size_t cnt = 0; - while (cnt < size) { + while (cnt < size && sock_connected) { size_t chunk = TinyGsmMin(size-cnt, rx.size()); if (chunk > 0) { rx.get(buf, chunk); @@ -201,7 +201,7 @@ public: public: TinyGsmUBLOX(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -209,9 +209,6 @@ public: /* * Basic functions */ - bool begin(const char* pin = NULL) { - return init(pin); - } bool init(const char* pin = NULL) { if (!testAT()) { @@ -228,6 +225,10 @@ public: return (getSimStatus() == SIM_READY); } + String getModemName() { + return "u-blox Cellular Modem"; + } + void setBaud(unsigned long baud) { sendAT(GF("+IPR="), baud); } @@ -280,6 +281,14 @@ public: return true; } + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + /* * Power functions */ @@ -400,22 +409,6 @@ public: return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - bool waitForNetwork(unsigned long timeout = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout; ) { - if (isNetworkConnected()) { - return true; - } - delay(250); - } - return false; - } - - /* - * WiFi functions - */ - bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE; - bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE; - /* * GPRS functions */ @@ -481,6 +474,10 @@ public: return localIP() != 0; } + /* + * IP Address functions + */ + String getLocalIP() { sendAT(GF("+UPSND=0,0")); if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { @@ -495,10 +492,6 @@ public: return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ @@ -566,6 +559,10 @@ public: return res; } + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) { @@ -659,30 +656,9 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } - - 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; - } - return false; - } + /* + Utilities + */ template void sendAT(Args... cmd) { diff --git a/src/TinyGsmClientXBee.h b/src/TinyGsmClientXBee.h index 8c0f1b0..bb55486 100644 --- a/src/TinyGsmClientXBee.h +++ b/src/TinyGsmClientXBee.h @@ -45,7 +45,7 @@ enum XBeeType { }; -class TinyGsmXBee +class TinyGsmXBee : public TinyGsmModem { public: @@ -207,17 +207,14 @@ public: public: TinyGsmXBee(Stream& stream) - : stream(stream) + : TinyGsmModem(stream), stream(stream) {} /* * Basic functions */ - bool begin() { - return init(); - } - bool init() { + bool init(const char* pin = NULL) { guardTime = 1100; // Start with a default guard time of 1 second if (!commandMode(10)) return false; // Try up to 10 times for the init @@ -242,6 +239,10 @@ public: return ret_val; } + String getModemName() { + return getBeeName(); + } + void setBaud(unsigned long baud) { if (!commandMode()) return; switch(baud) @@ -309,6 +310,16 @@ public: else return true; } + bool hasWifi() { + if (beeType == XBEE_S6B_WIFI) return true; + else return false; + } + + bool hasGPRS() { + if (beeType == XBEE_S6B_WIFI) return false; + else return true; + } + XBeeType getBeeType() { return beeType; } @@ -321,6 +332,7 @@ public: 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"; + default: return "Digi XBee®"; } } @@ -555,6 +567,10 @@ fail: return res; } + /* + * IP Address functions + */ + String getLocalIP() { if (!commandMode()) return ""; // Return immediately sendAT(GF("MY")); @@ -566,10 +582,6 @@ fail: return IPaddr; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * GPRS functions */ @@ -639,6 +651,10 @@ fail: int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) { @@ -704,63 +720,15 @@ protected: public: - /* Utilities */ - - template - void streamWrite(T last) { - stream.print(last); - } - - template - void streamWrite(T head, Args... tail) { - stream.print(head); - streamWrite(tail...); - } + /* + Utilities + */ void streamClear(void) { 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; - } - - bool writeChanges(void) { - sendAT(GF("WR")); // Write changes to flash - if (1 != waitResponse()) return false; - sendAT(GF("AC")); // Apply changes - if (1 != waitResponse()) return false; - return true; - } - - void exitCommand(void) { - sendAT(GF("CN")); // Exit command mode - 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); @@ -825,6 +793,7 @@ finish: // DBG("<<< ", data); } } + //DBG('<', index, '>'); return index; } @@ -842,6 +811,45 @@ finish: return waitResponse(1000, r1, r2, r3, r4, r5); } + 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; + } + + bool writeChanges(void) { + sendAT(GF("WR")); // Write changes to flash + if (1 != waitResponse()) return false; + sendAT(GF("AC")); // Apply changes + if (1 != waitResponse()) return false; + return true; + } + + void exitCommand(void) { + sendAT(GF("CN")); // Exit command mode + 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; + } + public: Stream& stream; diff --git a/src/TinyGsmCommon.h b/src/TinyGsmCommon.h index b803651..2cd7827 100644 --- a/src/TinyGsmCommon.h +++ b/src/TinyGsmCommon.h @@ -69,6 +69,7 @@ namespace { } } #else + #define DBG_PLAIN(...) #define DBG(...) #endif @@ -194,4 +195,135 @@ String TinyGsmDecodeHex16bit(String &instr) { return result; } + + + +class TinyGsmModem +{ + +public: + + TinyGsmModem(Stream& stream) + : stream(stream) + {} + + /* + * Basic functions + */ + + // Prepare the modem for further functionality + virtual bool init(const char* pin = NULL) = 0; + // Begin is redundant with init + virtual bool begin(const char* pin = NULL) { + return init(pin); + } + // Returns a string with the chip name + virtual String getModemName() = 0; + // Sets the serial communication baud rate + virtual void setBaud(unsigned long baud) = 0; + // Checks that the modem is responding to standard AT commands + virtual bool testAT(unsigned long timeout = 10000L) = 0; + // Holds open communication with the modem waiting for data to come in + virtual void maintain() = 0; + // Resets all modem chip settings to factor defaults + virtual bool factoryDefault() = 0; + // Returns the response to a get info request. The format varies by modem. + virtual String getModemInfo() = 0; + // Answers whether types of communication are available on this modem + virtual bool hasSSL() = 0; + virtual bool hasWifi() = 0; + virtual bool hasGPRS() = 0; + + /* + * Power functions + */ + + virtual bool restart() = 0; + + /* + * SIM card functions - only apply to cellular modems + */ + + virtual bool simUnlock(const char *pin) { return false; } + virtual String getSimCCID() { return ""; } + virtual String getIMEI() { return ""; } + virtual String getOperator() { return ""; } + + /* + * Generic network functions + */ + + virtual int getSignalQuality() = 0; + // NOTE: this returns whether the modem is registered on the cellular or WiFi + // network NOT whether GPRS or other internet connections are available + virtual bool isNetworkConnected() = 0; + virtual bool waitForNetwork(unsigned long timeout = 60000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + if (isNetworkConnected()) { + return true; + } + delay(250); + } + return false; + } + + /* + * WiFi functions - only apply to WiFi modems + */ + + virtual bool networkConnect(const char* ssid, const char* pwd) { return false; } + virtual bool networkDisconnect() { return false; } + + /* + * GPRS functions - only apply to cellular modems + */ + + virtual bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + return false; + } + virtual bool gprsDisconnect() { return false; } + + /* + * IP Address functions + */ + + virtual String getLocalIP() = 0; + virtual IPAddress localIP() { + return TinyGsmIpFromString(getLocalIP()); + } + + /* + Utilities + */ + + template + void streamWrite(T last) { + stream.print(last); + } + + template + void streamWrite(T head, Args... tail) { + stream.print(head); + streamWrite(tail...); + } + + bool streamSkipUntil(const 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; + } + return false; + } + +public: + Stream& stream; +}; + + + + #endif diff --git a/tools/test_build/test_build.ino b/tools/test_build/test_build.ino index 245e38a..0086c2a 100644 --- a/tools/test_build/test_build.ino +++ b/tools/test_build/test_build.ino @@ -3,6 +3,7 @@ * DO NOT USE THIS - this is just a compilation test! * **************************************************************/ +#define TINY_GSM_MODEM_SIM800 #include @@ -77,4 +78,3 @@ void loop() { modem.networkDisconnect(); #endif } -