diff --git a/src/TinyGsmClientA7608.h b/src/TinyGsmClientA7608.h index e7ccb7d4..1eda2e02 100644 --- a/src/TinyGsmClientA7608.h +++ b/src/TinyGsmClientA7608.h @@ -1,9 +1,10 @@ /** - * @file TinyGsmClientA7608.h - * @author Volodymyr Shymanskyy - * @license LGPL-3.0 - * @copyright Copyright (c) 2016 Volodymyr Shymanskyy - * @date Nov 2016 + * @file TinyGsmClientA7608.h + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2023 Shenzhen Xin Yuan Electronic Technology Co., Ltd + * @date 2024-12-05 + * */ #ifndef SRC_TINYGSMCLIENTA7608_H_ @@ -15,73 +16,18 @@ #define TINY_GSM_MUX_COUNT 10 #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE -#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 "TinyGsmTime.tpp" -#include "TinyGsmNTP.tpp" +#include "TinyGsmClientA76xx.h" #include "TinyGsmMqttA76xx.h" #include "TinyGsmHttpsA76xx.h" +#include "TinyGsmTCP.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 { - REG_NO_RESULT = -1, - REG_UNREGISTERED = 0, - REG_SEARCHING = 2, - REG_DENIED = 3, - REG_OK_HOME = 1, - REG_OK_ROAMING = 5, - REG_UNKNOWN = 4, - REG_SMS_ONLY = 6, - REG_EMERGENCY = 11 -}; - -enum NetworkMode { - MODEM_NETWORK_AUTO = 2, - MODEM_NETWORK_GSM = 13, - MODEM_NETWORK_WCDMA = 14, - MODEM_NETWORK_LTE = 38, -}; - -class TinyGsmA7608 : public TinyGsmModem, - public TinyGsmGPRS, - public TinyGsmTCP, - public TinyGsmSMS, - public TinyGsmGSMLocation, - public TinyGsmGPS, - public TinyGsmTime, - public TinyGsmNTP, - public TinyGsmBattery, - public TinyGsmTemperature, - public TinyGsmCalling, - public TinyGsmMqttA76xx, - public TinyGsmHttpsA76xx - -{ - friend class TinyGsmModem; - friend class TinyGsmGPRS; +class TinyGsmA7608 : public TinyGsmA76xx, + public TinyGsmTCP, + public TinyGsmMqttA76xx, + public TinyGsmHttpsA76xx + { + friend class TinyGsmA76xx; friend class TinyGsmTCP; - friend class TinyGsmSMS; - friend class TinyGsmGPS; - friend class TinyGsmGSMLocation; - friend class TinyGsmTime; - friend class TinyGsmNTP; - friend class TinyGsmBattery; - friend class TinyGsmTemperature; - friend class TinyGsmCalling; friend class TinyGsmMqttA76xx; friend class TinyGsmHttpsA76xx; @@ -146,34 +92,14 @@ class TinyGsmA7608 : public TinyGsmModem, /* * Inner Secure Client */ - - /*TODO(?)) - class GsmClientSecureSIM7600 : public GsmClientA7608 - { - public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmA7608& modem, uint8_t mux = 0) - : public GsmClient(modem, mux) - {} - - public: - int connect(const char* host, uint16_t port, int timeout_s) override { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } - TINY_GSM_CLIENT_CONNECT_OVERRIDES - }; - */ + // NOTE: Use modem TINYGSMA7608SSL for a secure client! /* * Constructor */ public: - explicit TinyGsmA7608(Stream& stream) : stream(stream) { + explicit TinyGsmA7608(Stream& stream) + : TinyGsmA76xx(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -219,206 +145,16 @@ class TinyGsmA7608 : public TinyGsmModem, } } - String getModemNameImpl() { - String name = "UNKOWN"; - String res; - - sendAT(GF("E0")); // Echo Off - waitResponse(); - - sendAT("I"); - if (waitResponse(10000L, res) != 1) { - DBG("MODEM STRING NO FOUND!"); - return name; - } - int modelIndex = res.indexOf("Model:") + 6; - int nextLineIndex = res.indexOf('\n', modelIndex); - if (nextLineIndex != -1) { - String modelString = res.substring(modelIndex, nextLineIndex); - modelString.trim(); - if(modelString.startsWith("A7608")){ - name = modelString; - DBG("### Modem:", name); - } - } else { - DBG("Model string not found."); - } - return name; - } - - bool factoryDefaultImpl() { - sendAT("&F"); //Set all current parameters to manufacturer defaults - if (waitResponse() != 1) { return false; } - return true; - } - /* * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { - if (!testAT()) { return false; } - sendAT(GF("+CRESET")); - if (waitResponse(10000L) != 1) { return false; } - delay(5000L); // TODO(?): Test this delay! - return init(pin); - } - - bool powerOffImpl() { - sendAT(GF("+CPOF")); - return waitResponse() == 1; - } - - bool radioOffImpl() { - if (!setPhoneFunctionality(4)) { return false; } - delay(3000); - return true; - } - - bool sleepEnableImpl(bool enable = true) { - sendAT(GF("+CSCLK="), enable); - return waitResponse() == 1; - } - - bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) { - sendAT(GF("+CFUN="), fun, reset ? ",1" : ""); - return waitResponse(10000L) == 1; - } + // Follows the A76xx template /* * Generic network functions */ - public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CGREG"); - } - protected: - bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); - return (s == REG_OK_HOME || s == REG_OK_ROAMING); - } - - public: - String getNetworkModes() { - int16_t mode = getNetworkMode(); - switch (mode) - { - case MODEM_NETWORK_AUTO: - return "AUTO"; - case MODEM_NETWORK_GSM: - return "GSM"; - case MODEM_NETWORK_WCDMA: - return "WCDMA"; - case MODEM_NETWORK_LTE: - return "LTE"; - default: - break; - } - return "UNKNOWN"; - } - - int16_t getNetworkMode() { - sendAT(GF("+CNMP?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return -1; } - int16_t mode = streamGetIntBefore('\n'); - waitResponse(); - return mode; - } - - bool setNetworkMode(NetworkMode mode) { - switch (mode) - { - case MODEM_NETWORK_AUTO: - case MODEM_NETWORK_GSM: - case MODEM_NETWORK_WCDMA: - case MODEM_NETWORK_LTE: - break; - default: - return false; - } - sendAT(GF("+CNMP="), mode); - return waitResponse() == 1; - } - - String getLocalIPImpl() { - String res; - sendAT(GF("+IPADDR")); // Inquire Socket PDP address - if (waitResponse(GF("+IPADDR: ")) != 1) { return ""; } - waitResponse(1000,res); - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); - res.trim(); - return res; - } - - bool enableNetwork(){ - sendAT(GF("+NETOPEN")); - int res = waitResponse(GF("+NETOPEN: 0"),GF("+IP ERROR: Network is already opened")); - if (res != 1 && res != 2){ - return false; - } - return true; - } - - bool disableNetwork(){ - sendAT(GF("+NETCLOSE")); - if (waitResponse() != 1){ - return false; - } - int res = waitResponse(GF("+NETCLOSE: 0"),GF("+NETCLOSE: 2")); - if (res != 1 || res != 2){ - return false; - } - return true; - } - - /* - * Return code: - * -1 ping failed - * 1 Ping success - * 2 Ping time out - * 3 Ping result - * * */ - int ping(const char *url, String &resolved_ip_addr, - uint32_t &rep_data_packet_size, - uint32_t &tripTime, - uint8_t &TTL) - { - uint8_t dest_addr_type = 1; - uint8_t num_pings = 1; - uint8_t data_packet_size = 64; - uint32_t interval_time = 1000; - uint32_t wait_time = 10000; - uint8_t ttl = 0xFF; - int result_type = -1; - - sendAT("+CPING=\"", url, "\"", ",", dest_addr_type, ",", - num_pings, ",", data_packet_size, ",", - interval_time, ",", wait_time, ",", ttl); - - if (waitResponse() != 1) { - return -1; - } - if (waitResponse(10000UL, "+CPING: ") == 1) { - result_type = streamGetIntBefore(','); - switch (result_type) { - case 1: - resolved_ip_addr = stream.readStringUntil(','); - rep_data_packet_size = streamGetIntBefore(','); - tripTime = streamGetIntBefore(','); - TTL = streamGetIntBefore('\n'); - break; - case 2: - break; - case 3: - break; - default: - break; - } - } - return result_type; - } /* * GPRS functions @@ -513,11 +249,6 @@ class TinyGsmA7608 : public TinyGsmModem, // May return +NETOPEN: 1, 0. We just confirm that the first number is 1 if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); - - // sendAT(GF("+IPADDR")); // Inquire Socket PDP address - // // sendAT(GF("+CGPADDR=1")); // Show PDP address - // if (waitResponse() != 1) { return false; } - return true; } @@ -525,24 +256,7 @@ class TinyGsmA7608 : public TinyGsmModem, * SIM card functions */ protected: - // Gets the CCID of a sim card via AT+CCID - String getSimCCIDImpl() { - sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - - /* - * Phone Call functions - */ - protected: - bool callHangupImpl() { - sendAT(GF("+CHUP")); - return waitResponse() == 1; - } + // Follows the A76xx template /* * Messaging functions @@ -550,261 +264,16 @@ class TinyGsmA7608 : public TinyGsmModem, protected: // Follows all messaging functions per template - /* - * GSM Location functions - */ - protected: - // Can return a GSM-based location from CLBS as per the template - /* * GPS/GNSS/GLONASS location functions */ protected: - // enable GPS - bool enableGPSImpl(int8_t power_en_pin ,uint8_t enable_level) { - if(power_en_pin == GSM_MODEM_AUX_POWER){ - sendAT("+CVAUXS=1"); - waitResponse(); - }else if(power_en_pin != -1){ - sendAT("+CGDRT=",power_en_pin,",1"); - waitResponse(); - sendAT("+CGSETV=",power_en_pin,",",enable_level); - waitResponse(); - } - sendAT(GF("+CGNSSPWR=1")); - if (waitResponse(10000UL,"+CGNSSPWR: READY!") != 1) { return false; } - return true; - } - - bool disableGPSImpl(int8_t power_en_pin ,uint8_t disbale_level) { - if(power_en_pin == GSM_MODEM_AUX_POWER){ - sendAT("+CVAUXS=0"); - waitResponse(); - }else if(power_en_pin != -1){ - sendAT("+CGSETV=",power_en_pin,",",disbale_level); - waitResponse(); - sendAT("+CGDRT=",power_en_pin,",0"); - waitResponse(); - } - sendAT(GF("+CGNSSPWR=0")); - if (waitResponse() != 1) { return false; } - return true; - } - - bool isEnableGPSImpl(){ - sendAT(GF("+CGNSSPWR?")); - if (waitResponse("+CGNSSPWR:") != 1) { return false; } - // +CGNSSPWR:,, - return 1 == streamGetIntBefore(','); - } - - bool enableAGPSImpl(){ - sendAT(GF("+CGNSSPWR?")); - if (waitResponse("+CGNSSPWR:") != 1) { return false; } - // +CGNSSPWR:,, - if(1 == streamGetIntBefore(',')){ - sendAT("+CAGPS"); - if (waitResponse(30000UL,"+AGPS:") != 1) { return false; } - String res = stream.readStringUntil('\n'); - if(res.startsWith(" success.")){ - return true; - } - } - return false; - } - - // get the RAW GPS output - String getGPSrawImpl() { - sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - - // get GPS informations - bool getGPSImpl(uint8_t *status,float* lat, float* lon, float* speed = 0, float* alt = 0, - int* vsat = 0, int* usat = 0, float* accuracy = 0, - 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; } - - uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix - // TODO(?) Can 1 be returned - if (fixMode == 1 || fixMode == 2 || fixMode == 3) { - // init variables - float ilat = 0; - char north; - float ilon = 0; - char east; - float ispeed = 0; - float ialt = 0; - int ivsat = 0; - int iusat = 0; - float iaccuracy = 0; - int iyear = 0; - int imonth = 0; - int iday = 0; - int ihour = 0; - int imin = 0; - float secondWithSS = 0; - // 20240513 fixed - // A7600M7_B11V05_231108 - // +CGNSSINFO: 3,13,14,,,xx.xxxx,N,xx.xxxx,E,130524,035736.00,53.6,0.000,,1.7,1.2,1.1, - ivsat = streamGetIntBefore(','); // GPS-SVs satellite valid numbers - streamSkipUntil(','); // BEIDOU-SVs satellite valid numbers - streamSkipUntil(','); // GLONASS-SVs satellite valid numbers - streamSkipUntil(','); // GALILEO-SVs satellite valid numbers - ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm - north = stream.read(); // N/S Indicator, N=north or S=south - streamSkipUntil(','); - ilon = streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm - east = stream.read(); // E/W Indicator, E=east or W=west - streamSkipUntil(','); - - // Date. Output format is ddmmyy - iday = streamGetIntLength(2); // Two digit day - imonth = streamGetIntLength(2); // Two digit month - iyear = streamGetIntBefore(','); // Two digit year - - // UTC Time. Output format is hhmmss.s - ihour = streamGetIntLength(2); // Two digit hour - imin = streamGetIntLength(2); // Two digit minute - secondWithSS = streamGetFloatBefore(','); // 4 digit second with subseconds - - 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?? - if (status){ - *status = fixMode; - } - // Set pointers - if (lat != NULL){ - *lat = (ilat) * (north == 'N' ? 1 : -1); - } - if (lon != NULL){ - *lon = (ilon) * (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 (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); - - waitResponse(); - // Sometimes, although fix is displayed, - // the value of longitude and latitude 0 will be set as invalid - if(ilat == 0 || ilon == 0){ - return false; - } - return true; - } - waitResponse(); - return false; - } - - bool setGPSBaudImpl(uint32_t baud){ - sendAT("+CGNSSIPR=",baud); - return waitResponse(1000L) == 1; - } - - bool setGPSModeImpl(uint8_t mode){ - sendAT("+CGNSSMODE=",mode); - return waitResponse(1000L) == 1; - } - - bool setGPSOutputRateImpl(uint8_t rate_hz){ - sendAT("+CGPSNMEARATE=",rate_hz); - return waitResponse(1000L) == 1; - } - - bool enableNMEAImpl(){ - sendAT("+CGNSSTST=1"); - if(waitResponse(1000L) != 1){ - return false; - } - // Select the output port for NMEA sentence - sendAT("+CGNSSPORTSWITCH=0,1"); - if(waitResponse(1000L) != 1){ - return false; - } - - - /* - 20240513 - Manufacturer: INCORPORATED - Model: A7608E-H - Revision: A50C4B11A7600M7 - A7600M7_B11V05_231108 - QCN: - IMEI: 861513066221920 - MEID: - +GCAP: +CGSM,+FCLASS,+DS - DeviceInfo: - A7600M7_B11V05_231108 version removes turning off GPS when NMEA is enabled - REMOVE: - // if(!disableGPSImpl(-1,0)){ - // return false; - // } - * * * * */ - - - /* - 20240507 - A7600M7_B11V05_231108 version will not return <+CGNSSPWR: READY!> - but will be returned in earlier versions. Redirect the NMEA sentence to the AT port and only check whether it returns OK. - - Manufacturer: INCORPORATED - Model: A7608SA-H - Revision: A50C4B11A7600M7 - A7600M7_B11V05_231108 - QCN: - IMEI: XXXXXXXXXXXXXXXXXXX - MEID: - +GCAP: +CGSM,+FCLASS,+DS - DeviceInfo: - // if(!enableGPSImpl(-1,0)){ - // return false; - // } - * - * * */ - // sendAT(GF("+CGNSSPWR=1")); - // if (waitResponse(10000UL) != 1) { return false; } - return true; - } + // Follows the A76xx template - bool disableNMEAImpl(){ - sendAT("+CGNSSTST=0"); - waitResponse(1000L); - // Select the output port for NMEA sentence - sendAT("+CGNSSPORTSWITCH=1,0"); - return waitResponse(1000L) == 1; - } - - bool configNMEASentenceImpl(bool CGA,bool GLL,bool GSA,bool GSV,bool RMC,bool VTG,bool ZDA,bool ANT){ - char buffer[32]; - snprintf(buffer,32,"%u,%u,%u,%u,%u,%u,%u,0", CGA, GLL, GSA, GSV, RMC, VTG, ZDA); - sendAT("+CGNSSNMEA=",buffer); - return waitResponse(1000L) == 1; - } /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Can follow CCLK as per template /* * NTP server functions @@ -815,46 +284,7 @@ class TinyGsmA7608 : public TinyGsmModem, * Battery functions */ protected: - // returns volts, multiply by 1000 to get mV - uint16_t getBattVoltageImpl() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } - - // get voltage in VOLTS - float voltage = streamGetFloatBefore('\n'); - // Wait for final OK - waitResponse(); - // Return millivolts - uint16_t res = voltage * 1000; - return res; - } - - int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - - uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { - chargeState = 0; - percent = 0; - milliVolts = getBattVoltage(); - return true; - } - - /* - * Temperature functions - */ - protected: - // get temperature in degree celsius - uint16_t getTemperatureImpl() { - sendAT(GF("+CPMUTEMP")); - if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; } - // return temperature in C - uint16_t res = streamGetIntBefore('\n'); - // Wait for final OK - waitResponse(); - return res; - } + // Follows all battery functions per template /* * Client related functions @@ -1113,12 +543,8 @@ class TinyGsmA7608 : public TinyGsmModem, return waitResponse(1000, r1, r2, r3, r4, r5); } - public: - Stream& stream; - protected: GsmClientA7608* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTA7608_H_ diff --git a/src/TinyGsmClientA7670.h b/src/TinyGsmClientA7670.h index 3a7db7e0..61d72c9c 100644 --- a/src/TinyGsmClientA7670.h +++ b/src/TinyGsmClientA7670.h @@ -1,9 +1,10 @@ /** - * @file TinyGsmClientA7670.h - * @author Volodymyr Shymanskyy - * @license LGPL-3.0 - * @copyright Copyright (c) 2016 Volodymyr Shymanskyy - * @date Nov 2016 + * @file TinyGsmClientA7670.h + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2023 Shenzhen Xin Yuan Electronic Technology Co., Ltd + * @date 2024-12-05 + * */ #ifndef SRC_TINYGSMCLIENTA7670_H_ @@ -15,72 +16,17 @@ #define TINY_GSM_MUX_COUNT 10 #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE -#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 "TinyGsmTime.tpp" -#include "TinyGsmNTP.tpp" +#include "TinyGsmClientA76xx.h" #include "TinyGsmMqttA76xx.h" #include "TinyGsmHttpsA76xx.h" +#include "TinyGsmTCP.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 { - REG_NO_RESULT = -1, - REG_UNREGISTERED = 0, - REG_SEARCHING = 2, - REG_DENIED = 3, - REG_OK_HOME = 1, - REG_OK_ROAMING = 5, - REG_UNKNOWN = 4, - REG_SMS_ONLY = 6, - REG_EMERGENCY = 11 -}; - -enum NetworkMode { - MODEM_NETWORK_AUTO = 2, - MODEM_NETWORK_GSM = 13, - MODEM_NETWORK_WCDMA = 14, - MODEM_NETWORK_LTE = 38, -}; - -class TinyGsmA7670 : public TinyGsmModem, - public TinyGsmGPRS, - public TinyGsmTCP, - public TinyGsmSMS, - public TinyGsmGSMLocation, - public TinyGsmGPS, - public TinyGsmTime, - public TinyGsmNTP, - public TinyGsmBattery, - public TinyGsmTemperature, - public TinyGsmCalling, - public TinyGsmMqttA76xx, - public TinyGsmHttpsA76xx - { - friend class TinyGsmModem; - friend class TinyGsmGPRS; +class TinyGsmA7670 : public TinyGsmA76xx, + public TinyGsmTCP, + public TinyGsmMqttA76xx, + public TinyGsmHttpsA76xx { + friend class TinyGsmA76xx; friend class TinyGsmTCP; - friend class TinyGsmSMS; - friend class TinyGsmGPS; - friend class TinyGsmGSMLocation; - friend class TinyGsmTime; - friend class TinyGsmNTP; - friend class TinyGsmBattery; - friend class TinyGsmTemperature; - friend class TinyGsmCalling; friend class TinyGsmMqttA76xx; friend class TinyGsmHttpsA76xx; @@ -145,34 +91,14 @@ class TinyGsmA7670 : public TinyGsmModem, /* * Inner Secure Client */ - - /*TODO(?)) - class GsmClientSecureA7670 : public GsmClientA7670 - { - public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmA7670& modem, uint8_t mux = 0) - : public GsmClient(modem, mux) - {} - - public: - int connect(const char* host, uint16_t port, int timeout_s) override { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } - TINY_GSM_CLIENT_CONNECT_OVERRIDES - }; - */ + // NOTE: Use modem TINYGSMA7670SSL for a secure client! /* * Constructor */ public: - explicit TinyGsmA7670(Stream& stream) : stream(stream) { + explicit TinyGsmA7670(Stream& stream) + : TinyGsmA76xx(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -218,208 +144,16 @@ class TinyGsmA7670 : public TinyGsmModem, } } - String getModemNameImpl() { - String name = "UNKOWN"; - String res; - - sendAT(GF("E0")); // Echo Off - waitResponse(); - - sendAT("I"); - if (waitResponse(10000L, res) != 1) { - DBG("MODEM STRING NO FOUND!"); - return name; - } - int modelIndex = res.indexOf("Model:") + 6; - int nextLineIndex = res.indexOf('\n', modelIndex); - if (nextLineIndex != -1) { - String modelString = res.substring(modelIndex, nextLineIndex); - modelString.trim(); - if(modelString.startsWith("A7670")){ - name = modelString; - DBG("### Modem:", name); - } - } else { - DBG("Model string not found."); - } - return name; - } - - bool factoryDefaultImpl() { - sendAT("&F"); //Set all current parameters to manufacturer defaults - if (waitResponse() != 1) { return false; } - return true; - } - /* * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { - if (!testAT()) { return false; } - sendAT(GF("+CRESET")); - if (waitResponse(10000L) != 1) { return false; } - delay(5000L); // TODO(?): Test this delay! - return init(pin); - } - - bool powerOffImpl() { - sendAT(GF("+CPOF")); - return waitResponse() == 1; - } - - bool radioOffImpl() { - if (!setPhoneFunctionality(4)) { return false; } - delay(3000); - return true; - } - - bool sleepEnableImpl(bool enable = true) { - sendAT(GF("+CSCLK="), enable); - return waitResponse() == 1; - } - - bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) { - sendAT(GF("+CFUN="), fun, reset ? ",1" : ""); - return waitResponse(10000L) == 1; - } + // Follows the A7670X template /* * Generic network functions */ - public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CGREG"); - } - protected: - bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); - return (s == REG_OK_HOME || s == REG_OK_ROAMING); - } - - public: - String getNetworkModes() { - int16_t mode = getNetworkMode(); - switch (mode) - { - case MODEM_NETWORK_AUTO: - return "AUTO"; - case MODEM_NETWORK_GSM: - return "GSM"; - case MODEM_NETWORK_WCDMA: - return "WCDMA"; - case MODEM_NETWORK_LTE: - return "LTE"; - default: - break; - } - return "UNKNOWN"; - } - - int16_t getNetworkMode() { - sendAT(GF("+CNMP?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return -1; } - int16_t mode = streamGetIntBefore('\n'); - waitResponse(); - return mode; - } - - bool setNetworkMode(NetworkMode mode) { - switch (mode) - { - case MODEM_NETWORK_AUTO: - case MODEM_NETWORK_GSM: - case MODEM_NETWORK_WCDMA: - case MODEM_NETWORK_LTE: - break; - default: - return false; - } - sendAT(GF("+CNMP="), mode); - return waitResponse() == 1; - } - - String getLocalIPImpl() { - String res; - sendAT(GF("+IPADDR")); // Inquire Socket PDP address - if (waitResponse(GF("+IPADDR: ")) != 1) { return ""; } - waitResponse(1000,res); - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); - res.trim(); - return res; - } - - - bool enableNetwork(){ - sendAT(GF("+NETOPEN")); - int res = waitResponse(GF("+NETOPEN: 0"),GF("+IP ERROR: Network is already opened")); - if (res != 1 && res != 2){ - return false; - } - return true; - } - - bool disableNetwork(){ - sendAT(GF("+NETCLOSE")); - if (waitResponse() != 1){ - return false; - } - int res = waitResponse(GF("+NETCLOSE: 0"),GF("+NETCLOSE: 2")); - if (res != 1 && res != 2){ - return false; - } - return true; - } - - /* - * Return code: - * -1 ping failed - * 1 Ping success - * 2 Ping time out - * 3 Ping result - * * */ - int ping(const char *url, String &resolved_ip_addr, - uint32_t &rep_data_packet_size, - uint32_t &tripTime, - uint8_t &TTL) - { - uint8_t dest_addr_type = 1; - uint8_t num_pings = 1; - uint8_t data_packet_size = 64; - uint32_t interval_time = 1000; - uint32_t wait_time = 10000; - uint8_t ttl = 0xFF; - int result_type = -1; - - sendAT("+CPING=\"", url, "\"", ",", dest_addr_type, ",", - num_pings, ",", data_packet_size, ",", - interval_time, ",", wait_time, ",", ttl); - - if (waitResponse() != 1) { - return -1; - } - if (waitResponse(10000UL, "+CPING: ") == 1) { - result_type = streamGetIntBefore(','); - switch (result_type) { - case 1: - resolved_ip_addr = stream.readStringUntil(','); - rep_data_packet_size = streamGetIntBefore(','); - tripTime = streamGetIntBefore(','); - TTL = streamGetIntBefore('\n'); - break; - case 2: - break; - case 3: - break; - default: - break; - } - } - return result_type; - } - /* * GPRS functions @@ -514,11 +248,6 @@ class TinyGsmA7670 : public TinyGsmModem, // May return +NETOPEN: 1, 0. We just confirm that the first number is 1 if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); - - // sendAT(GF("+IPADDR")); // Inquire Socket PDP address - // // sendAT(GF("+CGPADDR=1")); // Show PDP address - // if (waitResponse() != 1) { return false; } - return true; } @@ -526,24 +255,7 @@ class TinyGsmA7670 : public TinyGsmModem, * SIM card functions */ protected: - // Gets the CCID of a sim card via AT+CCID - String getSimCCIDImpl() { - sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - - /* - * Phone Call functions - */ - protected: - bool callHangupImpl() { - sendAT(GF("+CHUP")); - return waitResponse() == 1; - } + // Follows the A7670X template /* * Messaging functions @@ -551,203 +263,16 @@ class TinyGsmA7670 : public TinyGsmModem, protected: // Follows all messaging functions per template - /* - * GSM Location functions - */ - protected: - // Can return a GSM-based location from CLBS as per the template - /* * GPS/GNSS/GLONASS location functions */ protected: - // enable GPS - bool enableGPSImpl(int8_t power_en_pin ,uint8_t enable_level) { - if(power_en_pin!= -1){ - sendAT("+CGDRT=",power_en_pin,",1"); - waitResponse(); - sendAT("+CGSETV=",power_en_pin,",",enable_level); - waitResponse(); - } - sendAT(GF("+CGNSSPWR=1")); - if (waitResponse(10000UL, GF("+CGNSSPWR: READY!")) != 1) { return false; } - return true; - } - - bool disableGPSImpl(int8_t power_en_pin ,uint8_t disbale_level) { - if(power_en_pin!= -1){ - sendAT("+CGSETV=",power_en_pin,",",disbale_level); - waitResponse(); - sendAT("+CGDRT=",power_en_pin,",0"); - waitResponse(); - } - sendAT(GF("+CGNSSPWR=0")); - if (waitResponse() != 1) { return false; } - return true; - } - - bool isEnableGPSImpl(){ - sendAT(GF("+CGNSSPWR?")); - if (waitResponse("+CGNSSPWR:") != 1) { return false; } - // +CGNSSPWR:,, - return 1 == streamGetIntBefore(','); - } - - bool enableAGPSImpl(){ - sendAT(GF("+CGNSSPWR?")); - if (waitResponse("+CGNSSPWR:") != 1) { return false; } - // +CGNSSPWR:,, - if(1 == streamGetIntBefore(',')){ - sendAT("+CAGPS"); - if (waitResponse(30000UL,"+AGPS:") != 1) { return false; } - String res = stream.readStringUntil('\n'); - if(res.startsWith(" success.")){ - return true; - } - } - return false; - } - - // get the RAW GPS output - String getGPSrawImpl() { - sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - - // get GPS informations - bool getGPSImpl(uint8_t *status,float* lat, float* lon, float* speed = 0, float* alt = 0, - int* vsat = 0, int* usat = 0, float* accuracy = 0, - 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; } - - uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix - // TODO(?) Can 1 be returned - if (fixMode == 1 || fixMode == 2 || fixMode == 3) { - // init variables - float ilat = 0; - char north; - float ilon = 0; - char east; - float ispeed = 0; - float ialt = 0; - int ivsat = 0; - int iusat = 0; - float iaccuracy = 0; - int iyear = 0; - int imonth = 0; - int iday = 0; - int ihour = 0; - int imin = 0; - float secondWithSS = 0; - - ivsat = streamGetIntBefore(','); // GPS satellite valid numbers - streamSkipUntil(','); // GLONASS satellite valid numbers - streamSkipUntil(','); // skip dump , A7670 - streamSkipUntil(','); // BEIDOU satellite valid numbers - ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm - north = stream.read(); // N/S Indicator, N=north or S=south - streamSkipUntil(','); - ilon = streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm - east = stream.read(); // E/W Indicator, E=east or W=west - streamSkipUntil(','); - - // Date. Output format is ddmmyy - iday = streamGetIntLength(2); // Two digit day - imonth = streamGetIntLength(2); // Two digit month - iyear = streamGetIntBefore(','); // Two digit year - - // UTC Time. Output format is hhmmss.s - ihour = streamGetIntLength(2); // Two digit hour - imin = streamGetIntLength(2); // Two digit minute - secondWithSS = - streamGetFloatBefore(','); // 4 digit second with subseconds + // Follows the A7670X template - 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?? - if (status){ - *status = fixMode; - } - // Set pointers - if (lat != NULL){ - *lat = (ilat) * (north == 'N' ? 1 : -1); - } - if (lon != NULL){ - *lon = (ilon) * (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 (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); - - waitResponse(); - return true; - } - waitResponse(); - return false; - } - - bool setGPSBaudImpl(uint32_t baud){ - sendAT("+CGNSSIPR=",baud); - return waitResponse(1000L) == 1; - } - - bool setGPSModeImpl(uint8_t mode){ - sendAT("+CGNSSMODE=",mode); - return waitResponse(1000L) == 1; - } - - bool setGPSOutputRateImpl(uint8_t rate_hz){ - sendAT("+CGPSNMEARATE=",rate_hz); - return waitResponse(1000L) == 1; - } - - bool enableNMEAImpl(){ - sendAT("+CGNSSTST=1"); - waitResponse(1000L); - // Select the output port for NMEA sentence - sendAT("+CGNSSPORTSWITCH=0,1"); - return waitResponse(1000L) == 1; - } - - bool disableNMEAImpl(){ - sendAT("+CGNSSTST=0"); - waitResponse(1000L); - // Select the output port for NMEA sentence - sendAT("+CGNSSPORTSWITCH=1,0"); - return waitResponse(1000L) == 1; - } - - bool configNMEASentenceImpl(bool CGA,bool GLL,bool GSA,bool GSV,bool RMC,bool VTG,bool ZDA,bool ANT){ - char buffer[32]; - snprintf(buffer,32,"%u,%u,%u,%u,%u,%u,%u,0", CGA, GLL, GSA, GSV, RMC, VTG, ZDA); - sendAT("+CGNSSNMEA=",buffer); - return waitResponse(1000L) == 1; - } /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Can follow CCLK as per template /* * NTP server functions @@ -758,46 +283,7 @@ class TinyGsmA7670 : public TinyGsmModem, * Battery functions */ protected: - // returns volts, multiply by 1000 to get mV - uint16_t getBattVoltageImpl() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } - - // get voltage in VOLTS - float voltage = streamGetFloatBefore('\n'); - // Wait for final OK - waitResponse(); - // Return millivolts - uint16_t res = voltage * 1000; - return res; - } - - int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - - uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { - chargeState = 0; - percent = 0; - milliVolts = getBattVoltage(); - return true; - } - - /* - * Temperature functions - */ - protected: - // get temperature in degree celsius - uint16_t getTemperatureImpl() { - sendAT(GF("+CPMUTEMP")); - if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; } - // return temperature in C - uint16_t res = streamGetIntBefore('\n'); - // Wait for final OK - waitResponse(); - return res; - } + // Follows all battery functions per template /* * Client related functions @@ -843,7 +329,7 @@ class TinyGsmA7670 : public TinyGsmModem, return true; } - int16_t modemSend(const void* buff, size_t len, uint8_t mux) { + int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } stream.write(reinterpret_cast(buff), len); @@ -913,7 +399,7 @@ class TinyGsmA7670 : public TinyGsmModem, if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } - + bool modemGetConnected(uint8_t mux) { // Read the status of all sockets at once sendAT(GF("+CIPCLOSE?")); @@ -1056,12 +542,9 @@ class TinyGsmA7670 : public TinyGsmModem, return waitResponse(1000, r1, r2, r3, r4, r5); } - public: - Stream& stream; protected: GsmClientA7670* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTA7670_H_ diff --git a/src/TinyGsmClientA76xx.h b/src/TinyGsmClientA76xx.h new file mode 100644 index 00000000..d324c3ca --- /dev/null +++ b/src/TinyGsmClientA76xx.h @@ -0,0 +1,666 @@ +/** + * @file TinyGsmClientA76xx.h + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2023 Shenzhen Xin Yuan Electronic Technology Co., Ltd + * @date 2024-12-05 + * + */ + +#ifndef SRC_TINYGSMCLIENTA76XX_H_ +#define SRC_TINYGSMCLIENTA76XX_H_ + +// #define TINY_GSM_DEBUG Serial +// #define TINY_GSM_USE_HEX + +#include "TinyGsmBattery.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGPS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTime.tpp" +#include "TinyGsmNTP.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTextToSpeech.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 { + REG_NO_RESULT = -1, + REG_UNREGISTERED = 0, + REG_SEARCHING = 2, + REG_DENIED = 3, + REG_OK_HOME = 1, + REG_OK_ROAMING = 5, + REG_UNKNOWN = 4, +}; + +enum NetworkMode { + MODEM_NETWORK_AUTO = 2, + MODEM_NETWORK_GSM = 13, + MODEM_NETWORK_WCDMA = 14, + MODEM_NETWORK_LTE = 38, +}; + +template +class TinyGsmA76xx : public TinyGsmModem>, + public TinyGsmGPRS>, + public TinyGsmSMS>, + public TinyGsmGPS>, + public TinyGsmTime>, + public TinyGsmNTP>, + public TinyGsmBattery>, + public TinyGsmTemperature>, + public TinyGsmTextToSpeech>, + public TinyGsmGSMLocation> { + friend class TinyGsmModem>; + friend class TinyGsmGPRS>; + friend class TinyGsmSMS>; + friend class TinyGsmGPS>; + friend class TinyGsmTime>; + friend class TinyGsmNTP>; + friend class TinyGsmBattery>; + friend class TinyGsmGSMLocation>; + friend class TinyGsmTemperature>; + friend class TinyGsmTextToSpeech>; + /* + * CRTP Helper + */ + protected: + inline const modemType& thisModem() const { + return static_cast(*this); + } + inline modemType& thisModem() { + return static_cast(*this); + } + + /* + * Constructor + */ + public: + explicit TinyGsmA76xx(Stream& stream) : stream(stream) {} + + /* + * Basic functions + */ + protected: + bool initImpl(const char* pin = NULL) { + return thisModem().initImpl(pin); + } + + String getModemNameImpl() { + String name = "UNKOWN"; + String res; + + thisModem().sendAT(GF("E0")); // Echo Off + thisModem().waitResponse(); + + thisModem().sendAT("I"); + if (thisModem().waitResponse(10000L, res) != 1) { + DBG("MODEM STRING NO FOUND!"); + return name; + } + int modelIndex = res.indexOf("Model:") + 6; + int nextLineIndex = res.indexOf('\n', modelIndex); + if (nextLineIndex != -1) { + String modelString = res.substring(modelIndex, nextLineIndex); + modelString.trim(); + if (modelString.startsWith("A76")) { + name = modelString; + DBG("### Modem:", name); + } + } else { + DBG("Model string not found."); + } + return name; + } + + bool factoryDefaultImpl() { // these commands aren't supported + thisModem().sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write + thisModem().waitResponse(); + thisModem().sendAT(GF("+IPR=0")); // Auto-baud + thisModem().waitResponse(); + thisModem().sendAT(GF("+IFC=0,0")); // No Flow Control + thisModem().waitResponse(); + thisModem().sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop + thisModem().waitResponse(); + thisModem().sendAT(GF("+CSCLK=0")); // Disable Slow Clock + thisModem().waitResponse(); + thisModem().sendAT(GF("&W")); // Write configuration + return thisModem().waitResponse() == 1; + } + + /* + * Power functions + */ + protected: + bool restartImpl(const char* pin = NULL) { + thisModem().sendAT(GF("+CRESET")); + thisModem().waitResponse(); + thisModem().waitResponse(30000L, GF("SMS Ready")); + return thisModem().initImpl(pin); + } + + bool powerOffImpl() { + thisModem().sendAT(GF("+CPOF")); + return thisModem().waitResponse() == 1; + } + + // During sleep, the SIM70xx module has its serial communication disabled. + // In order to reestablish communication pull the DRT-pin of the SIM70xx + // module LOW for at least 50ms. Then use this function to disable sleep + // mode. The DTR-pin can then be released again. + bool sleepEnableImpl(bool enable = true) { + thisModem().sendAT(GF("+CSCLK="), enable); + return thisModem().waitResponse() == 1; + } + + bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) { + thisModem().sendAT(GF("+CFUN="), fun, reset ? ",1" : ""); + return thisModem().waitResponse(10000L) == 1; + } + + /* + * Generic network functions + */ + public: + RegStatus getRegistrationStatus() { + RegStatus epsStatus = (RegStatus)thisModem().getRegistrationStatusXREG("CEREG"); + // If we're connected on EPS, great! + if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) { + return epsStatus; + } else { + // Otherwise, check GPRS network status + // We could be using GPRS fall-back or the board could be being moody + return (RegStatus)thisModem().getRegistrationStatusXREG("CGREG"); + } + } + + protected: + bool isNetworkConnectedImpl() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + public: + String getNetworkModes() { + int16_t mode = getNetworkMode(); + switch (mode) { + case MODEM_NETWORK_AUTO: return "AUTO"; + case MODEM_NETWORK_GSM: return "GSM"; + case MODEM_NETWORK_WCDMA: return "WCDMA"; + case MODEM_NETWORK_LTE: return "LTE"; + default: break; + } + return "UNKNOWN"; + } + + int16_t getNetworkMode() { + thisModem().sendAT(GF("+CNMP?")); + if (thisModem().waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return -1; } + int16_t mode = thisModem().streamGetIntBefore('\n'); + thisModem().waitResponse(); + return mode; + } + + bool setNetworkMode(uint8_t mode) { + switch (mode) { + case MODEM_NETWORK_AUTO: + case MODEM_NETWORK_GSM: + case MODEM_NETWORK_WCDMA: + case MODEM_NETWORK_LTE: break; + default: return false; + } + thisModem().sendAT(GF("+CNMP="), mode); + return thisModem().waitResponse() == 1; + } + + String getLocalIP() { + String res; + thisModem().sendAT(GF("+IPADDR")); // Inquire Socket PDP address + if (thisModem().waitResponse(GF("+IPADDR: ")) != 1) { return ""; } + thisModem().waitResponse(1000, res); + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + return res; + } + + bool setNetworkActive() { + thisModem().sendAT(GF("+NETOPEN")); + int res = thisModem().waitResponse(GF("+NETOPEN: 0"), + GF("+IP ERROR: Network is already opened")); + if (res == 2) { + DBG("> Network has open......"); + thisModem().waitResponse(); + } + if (res != 1 && res != 2) { return false; } + return true; + } + + bool setNetworkDeactivate() { + thisModem().sendAT(GF("+NETCLOSE")); + if (thisModem().waitResponse() != 1) { return false; } + int res = thisModem().waitResponse(GF("+NETCLOSE: 0"), GF("+NETCLOSE: 2")); + if (res != 1 && res != 2) { return false; } + return true; + } + + bool getNetworkActive() { + // return thisModem().getNetworkActiveImpl(); + thisModem().sendAT(GF("+NETOPEN?")); + int res = thisModem().waitResponse(GF("+NETOPEN: 1")); + if (res == 1) { return true; } + return false; + } + + + /* + * GPRS functions + */ + protected: + // should implement in sub-classes + bool gprsConnectImpl(const char* apn, const char* user = NULL, const char* pwd = NULL) { + return thisModem().gprsConnectImpl(apn, user, pwd); + } + + bool gprsDisconnectImpl() { + return thisModem().gprsDisconnectImpl(); + } + + /* + * SIM card functions + */ + protected: + // Doesn't return the "+CICCID" before the number + String getSimCCIDImpl() { + thisModem().sendAT(GF("+CICCID")); + if (thisModem().waitResponse(GF(GSM_NL)) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + thisModem().waitResponse(); + res.trim(); + return res; + } + + /* + * Messaging functions + */ + protected: + // Follows all messaging functions per template + + /* + * GPS/GNSS/GLONASS location functions + */ + protected: + // enable GPS + bool enableGPSImpl(int8_t power_en_pin, uint8_t enable_level) { + if (power_en_pin == GSM_MODEM_AUX_POWER) { + thisModem().sendAT("+CVAUXS=1"); + thisModem().waitResponse(); + } else if (power_en_pin != -1) { + thisModem().sendAT("+CGDRT=", power_en_pin, ",1"); + thisModem().waitResponse(); + thisModem().sendAT("+CGSETV=", power_en_pin, ",", enable_level); + thisModem().waitResponse(); + } + thisModem().sendAT(GF("+CGNSSPWR=1")); + if (thisModem().waitResponse(10000UL, "+CGNSSPWR: READY!") != 1) { return false; } + return true; + } + + bool disableGPSImpl(int8_t power_en_pin, uint8_t disbale_level) { + if (power_en_pin == GSM_MODEM_AUX_POWER) { + thisModem().sendAT("+CVAUXS=0"); + thisModem().waitResponse(); + } else if (power_en_pin != -1) { + thisModem().sendAT("+CGSETV=", power_en_pin, ",", disbale_level); + thisModem().waitResponse(); + thisModem().sendAT("+CGDRT=", power_en_pin, ",0"); + thisModem().waitResponse(); + } + thisModem().sendAT(GF("+CGNSSPWR=0")); + if (thisModem().waitResponse() != 1) { return false; } + return true; + } + + bool isEnableGPSImpl() { + thisModem().sendAT(GF("+CGNSSPWR?")); + if (thisModem().waitResponse("+CGNSSPWR:") != 1) { return false; } + // +CGNSSPWR:,, + return 1 == thisModem().streamGetIntBefore(','); + } + + bool enableAGPSImpl() { + thisModem().sendAT(GF("+CGNSSPWR?")); + if (thisModem().waitResponse("+CGNSSPWR:") != 1) { return false; } + // +CGNSSPWR:,, + if (1 == thisModem().streamGetIntBefore(',')) { + thisModem().sendAT("+CAGPS"); + if (thisModem().waitResponse(30000UL, "+AGPS:") != 1) { return false; } + String res = stream.readStringUntil('\n'); + if (res.startsWith(" success.")) { return true; } + } + return false; + } + + bool setGPSBaudImpl(uint32_t baud) { + thisModem().sendAT("+CGNSSIPR=", baud); + return thisModem().waitResponse(1000L) == 1; + } + + bool setGPSModeImpl(uint8_t mode) { + thisModem().sendAT("+CGNSSMODE=", mode); + return thisModem().waitResponse(1000L) == 1; + } + + bool setGPSOutputRateImpl(uint8_t rate_hz) { + thisModem().sendAT("+CGPSNMEARATE=", rate_hz); + return thisModem().waitResponse(1000L) == 1; + } + + bool enableNMEAImpl() { + thisModem().sendAT("+CGNSSTST=1"); + if (thisModem().waitResponse(1000L) != 1) { return false; } + // Select the output port for NMEA sentence + thisModem().sendAT("+CGNSSPORTSWITCH=0,1"); + if (thisModem().waitResponse(1000L) != 1) { return false; } + return true; + } + + bool disableNMEAImpl() { + thisModem().sendAT("+CGNSSTST=0"); + thisModem().waitResponse(1000L); + // Select the output port for NMEA sentence + thisModem().sendAT("+CGNSSPORTSWITCH=1,0"); + return thisModem().waitResponse(1000L) == 1; + } + + bool configNMEASentenceImpl(bool CGA, bool GLL, bool GSA, bool GSV, bool RMC, bool VTG, + bool ZDA, bool ANT) { + char buffer[32]; + snprintf(buffer, 32, "%u,%u,%u,%u,%u,%u,%u,0", CGA, GLL, GSA, GSV, RMC, VTG, ZDA); + thisModem().sendAT("+CGNSSNMEA=", buffer); + return thisModem().waitResponse(1000L) == 1; + } + + // get the RAW GPS output + String getGPSrawImpl() { + thisModem().sendAT(GF("+CGNSSINFO")); + if (thisModem().waitResponse(10000L, GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + thisModem().waitResponse(); + res.trim(); + return res; + } + + // get GPS informations + bool getGPSImpl(uint8_t* status, float* lat, float* lon, float* speed = 0, + float* alt = 0, int* vsat = 0, int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, int* hour = 0, + int* minute = 0, int* second = 0) { + thisModem().sendAT(GF("+CGNSSINFO")); + if (thisModem().waitResponse(GF(GSM_NL "+CGNSSINFO: ")) != 1) { return false; } + + uint8_t fixMode = thisModem().streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix + // TODO(?) Can 1 be returned + if (fixMode == 1 || fixMode == 2 || fixMode == 3) { + // init variables + float ilat = 0; + char north; + float ilon = 0; + char east; + float ispeed = 0; + float ialt = 0; + int ivsat = 0; + int iusat = 0; + float iaccuracy = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + // 20240513 fixed + // A7600M7_B11V05_231108 + // +CGNSSINFO: + // 3,13,14,,,xx.xxxx,N,xx.xxxx,E,130524,035736.00,53.6,0.000,,1.7,1.2,1.1, + ivsat = + thisModem().streamGetIntBefore(','); // GPS-SVs satellite valid numbers + thisModem().streamSkipUntil(','); // BEIDOU-SVs satellite valid numbers + thisModem().streamSkipUntil(','); // GLONASS-SVs satellite valid numbers + thisModem().streamSkipUntil(','); // GALILEO-SVs satellite valid numbers + ilat = thisModem().streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm + north = stream.read(); // N/S Indicator, N=north or S=south + thisModem().streamSkipUntil(','); + ilon = thisModem().streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm + east = stream.read(); // E/W Indicator, E=east or W=west + thisModem().streamSkipUntil(','); + + // Date. Output format is ddmmyy + iday = thisModem().streamGetIntLength(2); // Two digit day + imonth = thisModem().streamGetIntLength(2); // Two digit month + iyear = thisModem().streamGetIntBefore(','); // Two digit year + + // UTC Time. Output format is hhmmss.s + ihour = thisModem().streamGetIntLength(2); // Two digit hour + imin = thisModem().streamGetIntLength(2); // Two digit minute + secondWithSS = + thisModem().streamGetFloatBefore(','); // 4 digit second with subseconds + + ialt = thisModem().streamGetFloatBefore(','); // MSL Altitude. Unit is meters + ispeed = + thisModem().streamGetFloatBefore(','); // Speed Over Ground. Unit is knots. + thisModem().streamSkipUntil(','); // Course Over Ground. Degrees. + thisModem().streamSkipUntil(','); // After set, will report GPS every x seconds + iaccuracy = + thisModem().streamGetFloatBefore(','); // Position Dilution Of Precision + thisModem().streamSkipUntil(','); // Horizontal Dilution Of Precision + thisModem().streamSkipUntil(','); // Vertical Dilution Of Precision + thisModem().streamSkipUntil('\n'); // TODO(?) is one more field reported?? + if (status) { *status = fixMode; } + // Set pointers + if (lat != NULL) { *lat = (ilat) * (north == 'N' ? 1 : -1); } + if (lon != NULL) { *lon = (ilon) * (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 (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); + + thisModem().waitResponse(); + // Sometimes, although fix is displayed, + // the value of longitude and latitude 0 will be set as invalid + if (ilat == 0 || ilon == 0) { return false; } + return true; + } + thisModem().waitResponse(); + return false; + } + + /* + * Time functions + */ + // Can follow CCLK as per template + int NTPServerSyncImpl(const String& server = "pool.ntp.org", byte TimeZone = 3) { + // Set NTP server and timezone + thisModem().sendAT(GF("+CNTP=\""), server, "\",", String(TimeZone)); + if (thisModem().waitResponse(10000L) != 1) { return -1; } + + // Request network synchronization + thisModem().sendAT(GF("+CNTP")); + if (thisModem().waitResponse(10000L, GF("+CNTP:"))) { + return '0' == thisModem().stream.read(); + } else { + return -1; + } + return -1; + } + + bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour, int* minute, + int* second, float* timezone) { + thisModem().sendAT(GF("+CCLK?")); + if (thisModem().waitResponse(2000L, GF("+CCLK: \"")) != 1) { return false; } + + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + int isec = 0; + int itimezone = 0; + + // Date & Time + iyear = thisModem().streamGetIntBefore('/'); + imonth = thisModem().streamGetIntBefore('/'); + iday = thisModem().streamGetIntBefore(','); + ihour = thisModem().streamGetIntBefore(':'); + imin = thisModem().streamGetIntBefore(':'); + isec = thisModem().streamGetIntLength(2); + char tzSign = thisModem().stream.read(); + itimezone = thisModem().streamGetIntBefore('\n'); + if (tzSign == '-') { itimezone = itimezone * -1; } + + // Set pointers + 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 = isec; + if (timezone != NULL) *timezone = static_cast(itimezone); + + // Final OK + thisModem().waitResponse(); + return true; + } + + /* + * NTP server functions + */ + // Can sync with server using CNTP as per template + + /* + * Battery functions + */ + // returns volts, multiply by 1000 to get mV + uint16_t getBattVoltageImpl() { + thisModem().sendAT(GF("+CBC")); + if (thisModem().waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } + + // get voltage in VOLTS + float voltage = thisModem().streamGetFloatBefore('\n'); + // Wait for final OK + thisModem().waitResponse(); + // Return millivolts + uint16_t res = voltage * 1000; + return res; + } + + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, uint16_t& milliVolts) { + chargeState = 0; + percent = 0; + milliVolts = getBattVoltageImpl(); + return true; + } + + protected: + // Follows all battery functions per template + + /* + * Client related functions + */ + // should implement in sub-classes + + /* + * Temperature functions + */ + protected: + // get temperature in degree celsius + uint16_t getTemperatureImpl() { + thisModem().sendAT(GF("+CPMUTEMP")); + if (thisModem().waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; } + // return temperature in C + uint16_t res = thisModem().streamGetIntBefore('\n'); + // Wait for final OK + waitResponse(); + return res; + } + + /* + * Text to speech functions + */ + protected: + bool textToSpeechImpl(String& text, uint8_t mode) { + thisModem().sendAT(GF("+CTTS="), mode, ',', text); + if (thisModem().waitResponse() != 1) { return false; } + if (thisModem().waitResponse(10000UL, GF("+CTTS: 0")) != 1) { return false; } + return true; + } + + /* + * Utilities + */ + public: + // should implement in sub-classes + 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) { + return thisModem().waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); + } + + 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 thisModem().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 thisModem().waitResponse(1000, r1, r2, r3, r4, r5); + } + + public: + Stream& stream; + + protected: + const char* gsmNL = GSM_NL; +}; + +#endif // SRC_TINYGSMCLIENTSIM70XX_H_