diff --git a/README.md b/README.md index 890a6d9a..2075126e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you like **TinyGSM** - give it a star, or fork it and contribute! [![GitHub forks](https://img.shields.io/github/forks/vshymanskyy/TinyGSM.svg?style=social&label=Fork)](https://github.com/vshymanskyy/TinyGSM/network) You can also join our chat: -[![Gitter](https://img.shields.io/gitter/room/vshymanskyy/TinyGSM.svg)](https://gitter.im/tinygsm) +[![Gitter](https://img.shields.io/gitter/room/vshymanskyy/TinyGSM.svg)](https://app.gitter.im/#/room/#tinygsm_Lobby:gitter.im) - [Supported modems](#supported-modems) - [Supported boards/modules](#supported-boardsmodules) @@ -29,7 +29,7 @@ You can also join our chat: - [How does it work?](#how-does-it-work) - [API Reference](#api-reference) - [Troubleshooting](#troubleshooting) - - [Ensure stable data & power connection](#ensure-stable-data--power-connection) + - [Ensure stable data \& power connection](#ensure-stable-data--power-connection) - [Baud rates](#baud-rates) - [Broken initial configuration](#broken-initial-configuration) - [Failed connection or no data received](#failed-connection-or-no-data-received) @@ -70,34 +70,32 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope - SIMCom LTE Modules (SIM7100E, SIM7500E, SIM7500A, SIM7600C, SIM7600E) - SIMCom SIM7000E/A/G CAT-M1/NB-IoT Module - SIMCom SIM7070/SIM7080/SIM7090 CAT-M1/NB-IoT Module +- SIMCom A7672X CAT-M1 Module - AI-Thinker A6, A6C, A7, A20 - ESP8266/ESP32 (AT commands interface, similar to GSM modems) - Digi XBee WiFi and Cellular (using XBee command mode) - Neoway M590 - u-blox 2G, 3G, 4G, and LTE Cat1 Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx) -- u-blox LTE-M/NB-IoT Modems (SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) +- u-blox LTE-M/NB-IoT Modems (SARA-R4xx, SARA-N4xx, SARA-R5xx, _but NOT SARA-N2xx_) - Sequans Monarch LTE Cat M1/NB1 (VZM20Q) - Quectel BG96 +- Quectel BG95 - Quectel M95 - Quectel MC60 ***(alpha)*** ### Supported boards/modules +- EnviroDIY LTE Bee, WiFi Bee - Arduino MKR GSM 1400 -- GPRSbee +- Sodaq GPRSbee, uBee - Microduino GSM -- Adafruit FONA (Mini Cellular GSM Breakout) -- Adafruit FONA 800/808 Shield +- Adafruit FONA Mini Cellular GSM Breakout, 800/808 Shield, FONA 3G - Industruino GSM +- Dragino NB-IoT Bee +- Digi XBee S6B, XBee LTE Cat 1, XBee3 LTE Cat 1, XBee3 CatM +- Nimbelink Skywire/Airgain NL-SW-LTE-QBG96, NL-SW-LTE-QBG95 - RAK WisLTE ***(alpha)*** - ... 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, UG95 -- [ ] SIMCom SIM7020 -- [ ] Telit GL865 -- [ ] ZTE MG2639 -- [ ] Hi-Link HLK-RM04 - Watch this repo for new updates! And of course, contributions are welcome ;) ## Features @@ -110,6 +108,7 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - ESP8266 - 5 - Neoway M590 - 2 - Quectel BG96 - 12 + - Quectel BG95 - 12 - Quectel M95 - 6 - Quectel MC60/MC60E - 6 - Sequans Monarch - 6 @@ -118,6 +117,7 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - SIM7000 - 8 possible without SSL, only 2 with - SIM 7070/7080/7090 - 12 - SIM 7500/7600/7800 - 10 + - SIM A7672X - 10 - u-blox 2G/3G - 7 - u-blox SARA R4/N4 - 7 - Digi XBee - _only 1 connection supported!_ @@ -125,10 +125,10 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - Not yet supported on any module, though it may be some day - SSL/TLS (HTTPS) - Supported on: - - SIM800, SIM7000, u-Blox, XBee _cellular_, ESP8266, and Sequans Monarch + - SIM800, SIM7000, SIM 7500/7600/7800 , A7672X, u-Blox, XBee _cellular_, ESP8266, Sequans Monarch and Quectel BG95 and BG96 - Note: **only some device models or firmware revisions have this feature** (SIM8xx R14.18, A7, etc.) - Not yet supported on: - - Quectel modems, SIM 5360/5320/7100, SIM 7500/7600/7800 + - SIM 5360/5320/7100 - Not possible on: - SIM900, A6/A7, Neoway M590, XBee _WiFi_ - Like TCP, most modules support simultaneous connections @@ -151,14 +151,14 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - Not yet supported on: - SIM7000, SIM5360/5320/7100, SIM7500/7800, VZM20Q (Monarch) - Not possible on: - - XBee (any type), u-blox SARA R4/N4, Neoway M590, ESP8266 (obviously) + - XBee (any type), u-blox SARA R4/R5/N4, Neoway M590, ESP8266 (obviously) - Functions: - Dial, hangup - DTMF sending **Location** - GPS/GNSS - - SIM808, SIM7000, SIM7500/7600/7800, BG96, u-blox + - SIM808, SIM7000, SIM7500/7600/7800, BG96, BG95, u-blox - NOTE: u-blox chips do _NOT_ have embedded GPS - this functionality only works if a secondary GPS is connected to primary cellular chip over I2C - GSM location service - SIM800, SIM7000, Quectel, u-blox @@ -176,6 +176,10 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - [V1pr](https://github.com/V1pr) - Quectel M95 - [replicadeltd](https://github.com/replicadeltd) +- UBLOX SARA-R5 + - [Sebastian Bergner](https://github.com/sebastianbergner) +- SIMCOM A7672X + - [Giovanni de Rosso Unruh](https://github.com/giovannirosso) - Other Contributors: - https://github.com/vshymanskyy/TinyGSM/graphs/contributors diff --git a/src/TinyGsmClientSIM7600.h b/src/TinyGsmClientSIM7600.h index 966f57e2..2cfd5e34 100644 --- a/src/TinyGsmClientSIM7600.h +++ b/src/TinyGsmClientSIM7600.h @@ -14,30 +14,42 @@ #define TINY_GSM_MUX_COUNT 10 #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "SIMCom" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_SIM7500) +#define MODEM_MODEL "SIM7500"; +#elif defined(TINY_GSM_MODEM_SIM7800) +#define MODEM_MODEL "SIM7800"; +#else +#define MODEM_MODEL "SIM7600"; +#endif -#include -#include "TinyGsmBattery.tpp" -#include "TinyGsmCalling.tpp" -#include "TinyGsmGPRS.tpp" -#include "TinyGsmGPS.tpp" -#include "TinyGsmGSMLocation.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" -#include "TinyGsmTemperature.tpp" +#include "TinyGsmSSL.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmGPS.tpp" #include "TinyGsmTime.tpp" #include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" +#include "TinyGsmTemperature.tpp" -#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; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum SIM7600RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -58,6 +70,7 @@ enum class SSLVersion: int8_t { class TinyGsmSim7600 : public TinyGsmModem, public TinyGsmGPRS, public TinyGsmTCP, + public TinyGsmSSL, public TinyGsmSMS, public TinyGsmGSMLocation, public TinyGsmGPS, @@ -69,6 +82,7 @@ class TinyGsmSim7600 : public TinyGsmModem, friend class TinyGsmModem; friend class TinyGsmGPRS; friend class TinyGsmTCP; + friend class TinyGsmSSL; friend class TinyGsmSMS; friend class TinyGsmGPS; friend class TinyGsmGSMLocation; @@ -142,7 +156,7 @@ class TinyGsmSim7600 : public TinyGsmModem, if (at->waitResponse(GF(">")) != 1) { return 0; } at->stream.write(reinterpret_cast(buff), len); at->stream.flush(); - if (at->waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { return 0; } + if (at->waitResponse(GF(AT_NL "+CIPSEND:")) != 1) { return 0; } at->streamSkipUntil(','); // Skip mux at->streamSkipUntil(','); // Skip requested bytes to send // TODO(?): make sure requested and confirmed bytes match @@ -224,23 +238,32 @@ class TinyGsmSim7600 : public TinyGsmModem, : GsmClientSim7600(modem, mux) {} protected: - String cacert; - String clientcert; - String clientkey; + String certificates[TINY_GSM_MUX_COUNT]; + String clientCertificates[TINY_GSM_MUX_COUNT]; + String clientPrivateKeys[TINY_GSM_MUX_COUNT]; bool certValidation = true; SSLVersion sslVersion = SSLVersion::ALL_SSL; public: - void setCACert(String certificateString) { - cacert = std::move(certificateString); + bool addCertificate(const char* certificateName, const char* cert, + const uint16_t len) { + return at->addCertificate(certificateName, cert, len); + } + + bool deleteCertificate(const char* certificateName) { + return at->deleteCertificate(certificateName); + } + + bool setCertificate(const char* certificateName) { + return at->setCertificate(certificateName, mux); } - void setCertificate(String certificateString) { - clientcert = std::move(certificateString); + bool setClientCertificate(const char* certificateName) { + return at->setClientCertificate(certificateName, mux); } - void setPrivateKey(String certificateString) { - clientkey = std::move(certificateString); + bool setClientKey(const char* certificateName) { + return at->setClientPrivateKey(certificateName, mux); } void setCertValidation(bool validation = true) { @@ -307,7 +330,7 @@ class TinyGsmSim7600 : public TinyGsmModem, size_t result = 0; if (!at->sockets[mux]) return 0; at->sendAT(GF("+CCHRECV?")); - if (at->waitResponse(GF(GSM_NL "+CCHRECV: ")) != 1) { + if (at->waitResponse(GF(AT_NL "+CCHRECV: ")) != 1) { at->sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (at->waitResponse(GF("+CIPRXGET:")) == 1) { @@ -322,7 +345,6 @@ class TinyGsmSim7600 : public TinyGsmModem, } return result; } - int startMillis = millis(); at->streamSkipUntil(','); // Skip mode 4 if (mux) { at->streamSkipUntil(','); // Skip mode 4 @@ -337,10 +359,9 @@ class TinyGsmSim7600 : public TinyGsmModem, stop(15000L); TINY_GSM_YIELD(); rx.clear(); - if (certValidation && cacert.isEmpty()) {return -1;} + if (certValidation && at->certificates[mux].length() == 0) {return -1;} sock_connected = at->modemConnect(host, port, mux, sslVersion, - timeout_s, cacert, clientcert, - clientkey); + timeout_s); return sock_connected; } @@ -348,21 +369,16 @@ class TinyGsmSim7600 : public TinyGsmModem, at->sendAT(GF("+CCHCLOSE="), mux); at->waitResponse(5000L); - if (!cacert.isEmpty()) { - at->sendAT(GF("+CCERTDELE=\"cacert"), static_cast(mux), ".pem\""); - at->waitResponse(); + if (certificates[mux].length() != 0) { + deleteCertificate(certificates[mux].c_str()); } - if (!clientcert.isEmpty()) { - at->sendAT(GF("+CCERTDELE=\"clientcert"), static_cast(mux), - ".pem\""); - at->waitResponse(); + if (!clientCertificates[mux].length() != 0) { + deleteCertificate(clientCertificates[mux].c_str()); } - if (!clientkey.isEmpty()) { - at->sendAT(GF("+CCERTDELE=\"clientkey"), static_cast(mux), - ".pem\""); - at->waitResponse(); + if (!clientPrivateKeys[mux].length() != 0) { + deleteCertificate(clientPrivateKeys[mux].c_str()); } GsmClientSim7600::stop(maxWaitMs); } @@ -381,7 +397,7 @@ class TinyGsmSim7600 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7600")); @@ -409,7 +425,7 @@ class TinyGsmSim7600 : public TinyGsmModem, SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim - if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -419,21 +435,6 @@ class TinyGsmSim7600 : public TinyGsmModem, } } - String getModemNameImpl() { - String name = "SIMCom SIM7600"; - - sendAT(GF("+CGMM")); - String res2; - if (waitResponse(1000L, res2) != 1) { return name; } - res2.replace(GSM_NL "OK" GSM_NL, ""); - res2.replace("_", " "); - res2.trim(); - - name = res2; - DBG("### Modem:", name); - return name; - } - bool factoryDefaultImpl() { // these commands aren't supported return false; } @@ -442,11 +443,11 @@ class TinyGsmSim7600 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } sendAT(GF("+CRESET")); if (waitResponse(10000L) != 1) { return false; } - delay(5000L); // TODO(?): Test this delay! + delay(16000L); return init(pin); } @@ -475,20 +476,21 @@ class TinyGsmSim7600 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CGREG"); + SIM7600RegStatus getRegistrationStatus() { + return (SIM7600RegStatus)getRegistrationStatusXREG("CGREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + SIM7600RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } public: String getNetworkModes() { + // Get the help string, not the setting value sendAT(GF("+CNMP=?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; @@ -496,7 +498,7 @@ class TinyGsmSim7600 : public TinyGsmModem, int16_t getNetworkMode() { sendAT(GF("+CNMP?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return false; } int16_t mode = streamGetIntBefore('\n'); waitResponse(); return mode; @@ -507,23 +509,57 @@ class TinyGsmSim7600 : public TinyGsmModem, return waitResponse() == 1; } + bool getNetworkSystemMode(bool& n, int16_t& stat) { + // n: whether to automatically report the system mode info + // stat: the current service. 0 if it not connected + sendAT(GF("+CNSMOD?")); + if (waitResponse(GF(AT_NL "+CNSMOD:")) != 1) { return false; } + n = streamGetIntBefore(',') != 0; + stat = streamGetIntBefore('\n'); + waitResponse(); + return true; + } + String getLocalIPImpl() { sendAT(GF("+IPADDR")); // Inquire Socket PDP address // sendAT(GF("+CGPADDR=1")); // Show PDP address String res; if (waitResponse(10000L, res) != 1) { return ""; } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); + cleanResponseString(res); res.trim(); return res; } + /* + * Secure socket layer (SSL) functions + */ + public: + bool addCertificate(const char* certificateName, const char* cert, + const uint16_t len) { + sendAT(GF("+CCERTDOWN=\""), certificateName, GF("\","), len); + if (!waitResponse(GF(">"))) { return false; } + stream.write(cert, len); + stream.flush(); + return waitResponse() == 1; + } + + bool deleteCertificate(const char* certificateName) { + sendAT(GF("+CCERTDELE=\""), certificateName, GF("\"")); + return waitResponse() == 1; + } + + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // Make sure we're not connected first // Define the PDP context @@ -532,7 +568,7 @@ class TinyGsmSim7600 : public TinyGsmModem, // Set the external authentication if (user && strlen(user) > 0) { - sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); + sendAT(GF("+CGAUTH=1,0,\""), pwd, GF("\",\""), user, '"'); waitResponse(); } @@ -580,8 +616,7 @@ class TinyGsmSim7600 : public TinyGsmModem, // We to ignore any immediate response and wait for the // URC to show it's really connected. sendAT(GF("+NETOPEN")); - if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { return false; } - + if (waitResponse(75000L, GF(AT_NL "+NETOPEN: 0")) != 1) { return false; } // Set the module to require manual reading of rx buffer data. sendAT(GF("+CCHSET=0,1")); @@ -598,17 +633,18 @@ class TinyGsmSim7600 : public TinyGsmModem, // Note: On the LTE models, this single command closes all sockets and the // service sendAT(GF("+NETCLOSE")); - waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")); + waitResponse(60000L, GF(AT_NL "+NETCLOSE: 0")); + // We assume this works, so we can do ssh disconnect too // stop the SSH client sendAT(GF("+CCHSTOP")); - return (waitResponse(60000L, GF(GSM_NL "+CCHSTOP: 0")) != 1); + return (waitResponse(60000L, GF(AT_NL "+CCHSTOP: 0")) != 1); } bool isGprsConnectedImpl() { sendAT(GF("+NETOPEN?")); // May return +NETOPEN: 1, 0. We just confirm that the first number is 1 - if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); sendAT(GF("+IPADDR")); // Inquire Socket PDP address @@ -618,6 +654,15 @@ class TinyGsmSim7600 : public TinyGsmModem, return true; } + String getProviderImpl() { + sendAT(GF("+CSPN?")); + if (waitResponse(GF("+CSPN:")) != 1) { return ""; } + streamSkipUntil('"'); /* Skip mode and format */ + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + /* * SIM card functions */ @@ -625,7 +670,7 @@ class TinyGsmSim7600 : public TinyGsmModem, // Gets the CCID of a sim card via AT+CCID String getSimCCIDImpl() { sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -642,16 +687,20 @@ class TinyGsmSim7600 : public TinyGsmModem, } /* - * Messaging functions + * Audio functions */ - protected: - // Follows all messaging functions per template + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp /* * GSM Location functions */ - protected: - // Can return a GSM-based location from CLBS as per the template + // Follows all GSM-based location functions as inherited from + // TinyGsmGSMLocation.tpp /* * GPS/GNSS/GLONASS location functions @@ -673,7 +722,7 @@ class TinyGsmSim7600 : public TinyGsmModem, // get the RAW GPS output String getGPSrawImpl() { sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+CGNSSINFO:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -686,7 +735,7 @@ class TinyGsmSim7600 : public TinyGsmModem, int* year = 0, int* month = 0, int* day = 0, int* hour = 0, int* minute = 0, int* second = 0) { sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CGNSSINFO:")) != 1) { return false; } uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix // TODO(?) Can 1 be returned @@ -712,11 +761,11 @@ class TinyGsmSim7600 : public TinyGsmModem, streamSkipUntil(','); // GLONASS satellite valid numbers streamSkipUntil(','); // BEIDOU satellite valid numbers ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm - north = stream.read(); // N/S Indicator, N=north or S=south - streamSkipUntil(','); + north = stream.readStringUntil(',').charAt( + 0); // N/S Indicator, N=north or S=south ilon = streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm - east = stream.read(); // E/W Indicator, E=east or W=west - streamSkipUntil(','); + east = stream.readStringUntil(',').charAt( + 0); // E/W Indicator, E=east or W=west // Date. Output format is ddmmyy iday = streamGetIntLength(2); // Two digit day @@ -732,31 +781,29 @@ class TinyGsmSim7600 : public TinyGsmModem, ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots. streamSkipUntil(','); // Course Over Ground. Degrees. - streamSkipUntil(','); // After set, will report GPS every x seconds iaccuracy = streamGetFloatBefore(','); // Position Dilution Of Precision streamSkipUntil(','); // Horizontal Dilution Of Precision - streamSkipUntil(','); // Vertical Dilution Of Precision - streamSkipUntil('\n'); // TODO(?) is one more field reported?? + streamSkipUntil('\n'); // Vertical Dilution Of Precision // Set pointers - if (lat != NULL) + if (lat != nullptr) *lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) * (north == 'N' ? 1 : -1); - if (lon != NULL) + if (lon != nullptr) *lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) * (east == 'E' ? 1 : -1); - if (speed != NULL) *speed = ispeed; - if (alt != NULL) *alt = ialt; - if (vsat != NULL) *vsat = ivsat; - if (usat != NULL) *usat = iusat; - if (accuracy != NULL) *accuracy = iaccuracy; + if (speed != nullptr) *speed = ispeed; + if (alt != nullptr) *alt = ialt; + if (vsat != nullptr) *vsat = ivsat; + if (usat != nullptr) *usat = iusat; + if (accuracy != nullptr) *accuracy = iaccuracy; if (iyear < 2000) iyear += 2000; - if (year != NULL) *year = iyear; - if (month != NULL) *month = imonth; - if (day != NULL) *day = iday; - if (hour != NULL) *hour = ihour; - if (minute != NULL) *minute = imin; - if (second != NULL) *second = static_cast(secondWithSS); + if (year != nullptr) *year = iyear; + if (month != nullptr) *month = imonth; + if (day != nullptr) *day = iday; + if (hour != nullptr) *hour = ihour; + if (minute != nullptr) *minute = imin; + if (second != nullptr) *second = static_cast(secondWithSS); waitResponse(); return true; @@ -766,7 +813,6 @@ class TinyGsmSim7600 : public TinyGsmModem, return false; } - /** * CGNSSMODE: , * This command is used to configure GPS, GLONASS, BEIDOU and QZSS support @@ -777,14 +823,14 @@ class TinyGsmSim7600 : public TinyGsmModem, String res; sendAT(GF("+CGNSSMODE="), mode, ",", dpo); if (waitResponse(10000L, res) != 1) { return ""; } - res.replace(GSM_NL, ""); + res.replace(AT_NL, ""); res.trim(); return res; } uint8_t getGNSSModeImpl() { sendAT(GF("+CGNSSMODE?")); - if (waitResponse(GF(GSM_NL "+CGNSSMODE:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CGNSSMODE:")) != 1) { return 0; } return stream.readStringUntil(',').toInt(); } @@ -792,22 +838,26 @@ class TinyGsmSim7600 : public TinyGsmModem, /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Follows all clock functions as inherited from TinyGsmTime.tpp /* * NTP server functions */ - // Can sync with server using CNTP as per template + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported /* * Battery functions */ protected: // returns volts, multiply by 1000 to get mV - uint16_t getBattVoltageImpl() { + int16_t getBattVoltageImpl() { sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return 0; } // get voltage in VOLTS float voltage = streamGetFloatBefore('\n'); @@ -820,10 +870,10 @@ class TinyGsmSim7600 : public TinyGsmModem, int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + bool getBattStatsImpl(int8_t& chargeState, int8_t& percent, + int16_t& milliVolts) { chargeState = 0; percent = 0; milliVolts = getBattVoltage(); @@ -837,7 +887,7 @@ class TinyGsmSim7600 : public TinyGsmModem, // get temperature in degree celsius uint16_t getTemperatureImpl() { sendAT(GF("+CPMUTEMP")); - if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CPMUTEMP:")) != 1) { return 0; } // return temperature in C uint16_t res = streamGetIntBefore('\n'); // Wait for final OK @@ -850,9 +900,7 @@ class TinyGsmSim7600 : public TinyGsmModem, */ protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, - SSLVersion sslVersion, int timeout_s = 15, - String cacert = "", String clientcert = "", - String clientkey = "") { + SSLVersion sslVersion, int timeout_s = 15) { if (sslVersion != SSLVersion::NO_SSL) { uint8_t authmode = 0; // List the certs available @@ -862,56 +910,32 @@ class TinyGsmSim7600 : public TinyGsmModem, static_cast(sslVersion)); if (waitResponse(5000L) != 1) return false; - if (!cacert.isEmpty()) { - sendAT(GF("+CCERTDOWN=\"cacert"), static_cast(mux), ".pem\",", - cacert.length()); - - if (waitResponse(GF(">"))) { - stream.write(reinterpret_cast(cacert.c_str()), - cacert.length()); - waitResponse(5000L); - - sendAT(GF("+CSSLCFG=\"cacert\","), mux, ",\"cacert", // set the root CA - static_cast(mux), ".pem\""); + if (certificates[mux].length() != 0) { + sendAT(GF("+CSSLCFG=\"cacert\","), mux, ",\"", + certificates[mux].c_str(), "\""); // set root CA if (waitResponse(5000L) != 1) return false; - } + authmode = 1; } - if (!clientcert.isEmpty()) { - sendAT(GF("+CCERTDOWN=\"clientcert"), - static_cast(mux), ".pem\",", clientcert.length()); - - if (waitResponse(GF(">"))) { - stream.write(reinterpret_cast(clientcert.c_str()), - clientcert.length()); - waitResponse(5000L); - } - - sendAT(GF("+CSSLCFG=\"clientcert\","), mux, ",\"clientcert", - static_cast(mux), ".pem\""); // set the client certificate + if (clientCertificates[mux].length() != 0) { + sendAT(GF("+CSSLCFG=\"clientcert\","), mux, ",\"", + clientCertificates[mux].c_str(), "\""); // set clientcertificate if (waitResponse(5000L) != 1) return false; } - if (!clientkey.isEmpty()) { - sendAT(GF("+CCERTDOWN=\"clientkey"), static_cast(mux), ".pem\",", - clientkey.length()); - - if (waitResponse(GF(">"))) { - stream.write(reinterpret_cast(clientkey.c_str()), - clientkey.length()); - waitResponse(5000L); - } - sendAT(GF("+CSSLCFG=\"clientkey\","), mux, ",\"clientkey", - static_cast(mux), ".pem\""); // set the client key + if (clientPrivateKeys[mux].length() != 0) { + sendAT(GF("+CSSLCFG=\"clientkey\","), mux, ",\"", + clientPrivateKeys[mux].c_str(), "\""); // set the clientkey if (waitResponse(5000L) != 1) return false; } - if (!cacert.isEmpty()) { - authmode = 1; - if (!clientcert.isEmpty() && !clientkey.isEmpty()) { - authmode = 2; - } - } else if (!clientcert.isEmpty() && !clientkey.isEmpty()) { + if (certificates[mux].length() != 0 && + clientCertificates[mux].length() != 0 && + clientPrivateKeys[mux].length() != 0) { + authmode = 2; + } else if (certificates[mux].length() == 0 && + clientCertificates[mux].length() != 0 && + clientPrivateKeys[mux].length() != 0) { authmode = 3; } @@ -935,7 +959,7 @@ class TinyGsmSim7600 : public TinyGsmModem, sendAT(GF("+CCHOPEN="), mux, ',', GF("\""), host, GF("\","), port); // The reply is OK followed by +CIPOPEN: , where // is the mux number and should be 0 if there's no error - if (waitResponse(timeout_ms, GF(GSM_NL "+CCHOPEN:")) != 1) { + if (waitResponse(timeout_ms, GF(AT_NL "+CCHOPEN:")) != 1) { return false; } @@ -944,7 +968,7 @@ class TinyGsmSim7600 : public TinyGsmModem, host, GF("\","), port); // The reply is OK followed by +CIPOPEN: , where // is the mux number and should be 0 if there's no error - if (waitResponse(timeout_ms, GF(GSM_NL "+CIPOPEN:")) != 1) { + if (waitResponse(timeout_ms, GF(AT_NL "+CIPOPEN:")) != 1) { return false; } } @@ -963,6 +987,7 @@ class TinyGsmSim7600 : public TinyGsmModem, size_t modemRead(size_t size, uint8_t mux) { return sockets[mux]->modemRead(size, mux); } + size_t modemGetAvailable(uint8_t mux) { return sockets[mux]->modemGetAvailable(mux); } @@ -971,13 +996,12 @@ class TinyGsmSim7600 : public TinyGsmModem, // Read the status of all sockets at once sendAT(GF("+CIPOPEN?")); if (waitResponse(GF("+CIPOPEN:")) != 1) { - // return false; // TODO: Why does this not read correctly? + return false; } for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { // +CIPOPEN:, String state = stream.readStringUntil('\n'); if (state.indexOf(',') > 0) { sockets[muxNo]->sock_connected = true; } - waitResponse(GF("+CIPOPEN:")); } waitResponse(); // Should be an OK at the end if (!sockets[mux]) return false; @@ -988,125 +1012,49 @@ class TinyGsmSim7600 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - 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); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(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)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - 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 "+CIPRXGET:"))) { - int8_t mode = streamGetIntBefore(','); - if (mode == 1) { - int8_t mux = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - } - data = ""; - // DBG("### Got Data:", mux); - } else { - data += mode; - } - } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } - } - data = ""; - // DBG("### Got Data:", len, "on", mux); - } else if (data.endsWith(GF("+IPCLOSE:"))) { - int8_t mux = streamGetIntBefore(','); - streamSkipUntil('\n'); // Skip the reason code - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->sock_connected = false; - } - data = ""; - DBG("### Closed: ", mux); - } else if (data.endsWith(GF("+CIPEVENT:"))) { - // Need to close all open sockets and release the network library. - // User will then need to reconnect. - DBG("### Network error!"); - if (!isGprsConnected()) { gprsDisconnect(); } - data = ""; + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) { + int8_t mode = streamGetIntBefore(','); + if (mode == 1) { + int8_t mux = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; } + data = ""; + // DBG("### Got Data:", mux); + return true; + } else { + data += mode; + return false; + } + } else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } + } + data = ""; + // DBG("### Got Data:", len, "on", mux); + return true; + } else if (data.endsWith(GF("+IPCLOSE:"))) { + int8_t mux = streamGetIntBefore(','); + streamSkipUntil('\n'); // Skip the reason code + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + DBG("### Closed: ", mux); + return true; + } else if (data.endsWith(GF("+CIPEVENT:"))) { + // Need to close all open sockets and release the network library. + // User will then need to reconnect. + DBG("### Network error!"); + if (!isGprsConnected()) { gprsDisconnect(); } + data = ""; + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -1114,7 +1062,6 @@ class TinyGsmSim7600 : public TinyGsmModem, protected: GsmClientSim7600* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTSIM7600_H_ diff --git a/src/TinyGsmSSL.tpp b/src/TinyGsmSSL.tpp index f33a579f..e9d8327e 100644 --- a/src/TinyGsmSSL.tpp +++ b/src/TinyGsmSSL.tpp @@ -13,18 +13,54 @@ #define TINY_GSM_MODEM_HAS_SSL - -template +template class TinyGsmSSL { + /* =========================================== */ + /* =========================================== */ + /* + * Define the interface + */ public: /* - * SSL functions + * Secure socket layer (SSL) functions */ bool addCertificate(const char* filename) { return thisModem().addCertificateImpl(filename); } - bool deleteCertificate() { - return thisModem().deleteCertificateImpl(); + bool addCertificate(const String& filename) { + return addCertificate(filename.c_str()); + } + bool addCertificate(const char* certificateName, const char* cert, + const uint16_t len) { + return thisModem().addCertificateImpl(certificateName, cert, len); + } + bool addCertificate(const String& certificateName, const String& cert, + const uint16_t len) { + return addCertificate(certificateName.c_str(), cert.c_str(), len); + } + + bool deleteCertificate(const char* filename) { + return thisModem().deleteCertificateImpl(filename); + } + + bool setCertificate(const String& certificateName, const uint8_t mux = 0) { + if (mux >= muxCount) return false; + certificates[mux] = certificateName; + return true; + } + + bool setClientCertificate(const String& certificateName, + const uint8_t mux = 0) { + if (mux >= muxCount) return false; + clientCertificates[mux] = certificateName; + return true; + } + + bool setClientPrivateKey(const String& certificateName, + const uint8_t mux = 0) { + if (mux >= muxCount) return false; + clientPrivateKeys[mux] = certificateName; + return true; } /* @@ -37,35 +73,27 @@ class TinyGsmSSL { inline modemType& thisModem() { return static_cast(*this); } + ~TinyGsmSSL() {} + /* =========================================== */ + /* =========================================== */ /* - * Inner Secure Client + * Define the default function implementations */ - /* - public: - class GsmClientSecure : public GsmClient { - public: - GsmClientSecureSim800() {} - - explicit GsmClientSecureSim800(TinyGsmSim800& modem, uint8_t mux = 0) - : GsmClientSim800(modem, mux) {} - - public: - int connect(const char* host, uint16_t port, int timeout_s) overide { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } - };*/ /* - * SSL functions + * Secure socket layer (SSL) functions */ protected: bool addCertificateImpl(const char* filename) TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool deleteCertificateImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool addCertificateImpl(const char* certificateName, const char* cert, + const uint16_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool + deleteCertificateImpl(const char* filename) TINY_GSM_ATTR_NOT_IMPLEMENTED; + + String certificates[muxCount]; + String clientCertificates[muxCount]; + String clientPrivateKeys[muxCount]; }; #endif // SRC_TINYGSMSSL_H_