diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e405e6c..ce46eb8 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -23,7 +23,7 @@ with your board before submitting any issues. Main processor board: Modem: -TinyGSM version: +TinyGSM version: Code: ### Scenario, steps to reproduce diff --git a/.travis.yml b/.travis.yml index b30789b..1e3ec6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,8 @@ env: - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_MC60' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM800' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM5360' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" + - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM5360' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" + - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7600' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SARAR4' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev" diff --git a/library.json b/library.json index 5054958..a01c544 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TinyGSM", - "version": "0.9.6", + "version": "0.9.7", "description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.", "keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M", "authors": diff --git a/library.properties b/library.properties index 8f9275b..46aa51e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TinyGSM -version=0.9.6 +version=0.9.7 author=Volodymyr Shymanskyy maintainer=Volodymyr Shymanskyy sentence=A small Arduino library for GPRS modules, that just works. diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index 02f9910..0b25d28 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -41,13 +41,19 @@ // typedef TinyGsmSim7000::GsmClientSecure TinyGsmClientSecure; TODO! #elif defined(TINY_GSM_MODEM_SIM5320) || defined(TINY_GSM_MODEM_SIM5360) || \ - defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100) || \ - defined(TINY_GSM_MODEM_SIM7500) || defined(TINY_GSM_MODEM_SIM7600) + defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100) #define TINY_GSM_MODEM_HAS_GPRS #include typedef TinyGsmSim5360 TinyGsm; typedef TinyGsmSim5360::GsmClient TinyGsmClient; +#elif defined(TINY_GSM_MODEM_SIM7600) || defined(TINY_GSM_MODEM_SIM7800) || \ + defined(TINY_GSM_MODEM_SIM7500) + #define TINY_GSM_MODEM_HAS_GPRS + #include + typedef TinyGsmSim7600 TinyGsm; + typedef TinyGsmSim7600::GsmClient TinyGsmClient; + #elif defined(TINY_GSM_MODEM_UBLOX) #define TINY_GSM_MODEM_HAS_GPRS #define TINY_GSM_MODEM_HAS_SSL diff --git a/src/TinyGsmClientSIM5360.h b/src/TinyGsmClientSIM5360.h index 41da9b8..a19b83a 100644 --- a/src/TinyGsmClientSIM5360.h +++ b/src/TinyGsmClientSIM5360.h @@ -58,13 +58,13 @@ class GsmClient : public Client public: GsmClient() {} - GsmClient(TinyGsmSim5360& modem, uint8_t mux = 1) { + GsmClient(TinyGsmSim5360& modem, uint8_t mux = 0) { init(&modem, mux); } virtual ~GsmClient(){} - bool init(TinyGsmSim5360* modem, uint8_t mux = 1) { + bool init(TinyGsmSim5360* modem, uint8_t mux = 0) { this->at = modem; this->mux = mux; sock_available = 0; @@ -324,7 +324,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); } - // Define PDP context 1 + // Define external PDP context 1 sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"',",\"0.0.0.0\",0,0"); waitResponse(); @@ -399,24 +399,12 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // Stop the socket service - // Note: all sockets should be closed first + // Note: all sockets should be closed first - on 3G/4G models the sockets must be closed manually sendAT(GF("+NETCLOSE")); if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { return false; } - // Deactivate PDP context 1 - sendAT(GF("+CGACT=1,0")); - if (waitResponse(40000L) != 1) { - return false; - } - - // Detach from GPRS - sendAT(GF("+CGATT=0")); - if (waitResponse(360000L) != 1) { - return false; - } - return true; } @@ -473,8 +461,10 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() */ String sendUSSD(const String& code) { + // Select message format (1=text) sendAT(GF("+CMGF=1")); waitResponse(); + // Select TE character set sendAT(GF("+CSCS=\"HEX\"")); waitResponse(); sendAT(GF("+CUSD=1,\""), code, GF("\"")); @@ -499,14 +489,16 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } bool sendSMS(const String& number, const String& text) { - + // Get SMS service centre address sendAT(GF("+AT+CSCA?")); waitResponse(); + // Select message format (1=text) sendAT(GF("+CMGF=1")); waitResponse(); //Set GSM 7 bit default alphabet (3GPP TS 23.038) sendAT(GF("+CSCS=\"GSM\"")); waitResponse(); + // Send the message! sendAT(GF("+CMGS=\""), number, GF("\"")); if (waitResponse(GF(">")) != 1) { return false; @@ -518,13 +510,16 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } bool sendSMS_UTF16(const String& number, const void* text, size_t len) { + // Select message format (1=text) sendAT(GF("+CMGF=1")); waitResponse(); + // Select TE character set sendAT(GF("+CSCS=\"HEX\"")); waitResponse(); + // Set text mode parameters sendAT(GF("+CSMP=17,167,0,8")); waitResponse(); - + // Send the message sendAT(GF("+CMGS=\""), number, GF("\"")); if (waitResponse(GF(">")) != 1) { return false; @@ -571,10 +566,12 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } streamSkipUntil(','); // Skip battery charge status streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); + // get voltage in VOLTS + float voltage = stream.readStringUntil('\n').toFloat(); // Wait for final OK waitResponse(); + // Return millivolts + uint16_t res = voltage*1000; return res; } @@ -610,17 +607,31 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } chargeState = stream.readStringUntil(',').toInt(); percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); + // get voltage in VOLTS + float voltage = stream.readStringUntil('\n').toFloat(); + milliVolts = voltage*1000; // Wait for final OK waitResponse(); return true; } - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; - // ToDo: - // # Enable Temparature Reading: - //AT+CMTE=1 - //AT+CMTE? + // get temperature in degree celsius + float getTemperature() { + // Enable Temparature Reading + sendAT(GF("+CMTE=1")); + if (waitResponse() != 1) { + return 0; + } + // Get Temparature Value + sendAT(GF("+CMTE?")); + if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) { + return false; + } + float res = stream.readStringUntil('\n').toFloat(); + // Wait for final OK + waitResponse(); + return res; + } /* * Client related functions @@ -636,9 +647,9 @@ protected: return false; } - // Establish a connection in multi-socket mode + // Establish a connection in multi-socket mode sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); - // The reply is +CIPOPEN: ## of socket created + // The reply is +CIPOPEN: ## of socket created if (waitResponse(15000L, GF(GSM_NL "+CIPOPEN:")) != 1) { return false; } @@ -693,7 +704,6 @@ protected: #endif sockets[mux]->rx.put(c); } - DBG("### READ:", len_requested, "from", mux); // sockets[mux]->sock_available = modemGetAvailable(mux); sockets[mux]->sock_available = len_confirmed; @@ -719,9 +729,10 @@ protected: bool modemGetConnected(uint8_t mux) { // Read the status of all sockets at once - sendAT(GF("+CIPCLOSE?"), mux); - if (waitResponse(GFP(GSM_OK), GF("+CIPCLOSE: ")) != 2) + sendAT(GF("+CIPCLOSE?")); + if (waitResponse(GF("+CIPCLOSE:")) != 1) { return false; + } for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { // +CIPCLOSE:,,..., sockets[muxNo]->sock_connected = stream.parseInt(); diff --git a/src/TinyGsmClientSIM7600.h b/src/TinyGsmClientSIM7600.h new file mode 100644 index 0000000..94026ee --- /dev/null +++ b/src/TinyGsmClientSIM7600.h @@ -0,0 +1,873 @@ +/** + * @file TinyGsmClientSIM7600.h + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Nov 2016 + */ + +#ifndef TinyGsmClientSIM7600_h +#define TinyGsmClientSIM7600_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 10 + +#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, +}; + +enum TinyGSMDateTimeFormat { + DATE_FULL = 0, + DATE_TIME = 1, + DATE_DATE = 2 +}; + +class TinyGsmSim7600 +{ + +public: + +class GsmClient : public Client +{ + friend class TinyGsmSim7600; + typedef TinyGsmFifo RxFifo; + +public: + GsmClient() {} + + GsmClient(TinyGsmSim7600& modem, uint8_t mux = 0) { + init(&modem, mux); + } + + virtual ~GsmClient(){} + + bool init(TinyGsmSim7600* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; + + at->sockets[mux] = this; + + return true; + } + +public: + virtual int connect(const char *host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + +TINY_GSM_CLIENT_CONNECT_OVERLOADS() + + virtual void stop(uint32_t maxWaitMs) { + TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + + virtual void stop() { stop(15000L); } + +TINY_GSM_CLIENT_WRITE() + +TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + +TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + +TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + +private: + TinyGsmSim7600* at; + uint8_t mux; + uint16_t sock_available; + uint32_t prev_check; + bool sock_connected; + bool got_data; + RxFifo rx; +}; + + +public: + + TinyGsmSim7600(Stream& stream) + : stream(stream) + { + memset(sockets, 0, sizeof(sockets)); + } + + virtual ~TinyGsmSim7600(){} + + /* + * Basic functions + */ + + bool begin(const char* pin = NULL) { + return init(pin); + } + + bool init(const char* pin = NULL) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { + return false; + } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { + return false; + } + DBG(GF("### Modem:"), getModemName()); + getSimStatus(); + return true; + } + + String getModemName() { + 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; + } + +TINY_GSM_MODEM_SET_BAUD_IPR() + +TINY_GSM_MODEM_TEST_AT() + +TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() + + bool factoryDefault() { // these commands aren't supported + return false; + } + +TINY_GSM_MODEM_GET_INFO_ATI() + + bool hasSSL() { + return false; // TODO: Module supports SSL, but not yet implemented + } + + bool hasWifi() { + return false; + } + + bool hasGPRS() { + return true; + } + + /* + * Power functions + */ + + bool restart() { + if (!testAT()) { + return false; + } + sendAT(GF("+CRESET")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(5000L); // TODO: Test this delay! + return init(); + } + + bool poweroff() { + sendAT(GF("+CPOF")); + return waitResponse() == 1; + } + + bool radioOff() { + sendAT(GF("+CFUN=4")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return true; + } + + bool sleepEnable(bool enable = true) { + sendAT(GF("+CSCLK="), enable); + return waitResponse() == 1; + } + + /* + * SIM card functions + */ + +TINY_GSM_MODEM_SIM_UNLOCK_CPIN() + +// Gets the CCID of a sim card via AT+CCID + String getSimCCID() { + sendAT(GF("+CICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + +TINY_GSM_MODEM_GET_IMEI_GSN() + + SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout_ms; ) { + 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; + } + +TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) + +TINY_GSM_MODEM_GET_OPERATOR_COPS() + + /* + * Generic network functions + */ + +TINY_GSM_MODEM_GET_CSQ() + + bool isNetworkConnected() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + String getNetworkModes() { + sendAT(GF("+CNMP=?")); + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + return res; + } + +TINY_GSM_MODEM_WAIT_FOR_NETWORK() + + String setNetworkMode(uint8_t mode) { + sendAT(GF("+CNMP="), mode); + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { + return "OK"; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + return res; + } + + + /* + * GPRS functions + */ + + bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + gprsDisconnect(); // Make sure we're not connected first + + // Define the PDP context + + // The CGDCONT commands set up the "external" PDP context + + // Set the external authentication + if (user && strlen(user) > 0) { + sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); + waitResponse(); + } + + // Define external PDP context 1 + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"',",\"0.0.0.0\",0,0"); + waitResponse(); + + // Configure TCP parameters + + // Select TCP/IP application mode (command mode) + sendAT(GF("+CIPMODE=0")); + waitResponse(); + + // Set Sending Mode - send without waiting for peer TCP ACK + sendAT(GF("+CIPSENDMODE=0")); + waitResponse(); + + // Configure socket parameters + //AT+CIPCCFG= [][,[][,[][,[][,]][,[[][,[]]]]]]]] + // NmRetry = number of retransmission to be made for an IP packet = 10 (default) + // DelayTm = number of milliseconds to delay to output data of Receiving = 0 (default) + // Ack = sets whether reporting a string “Send ok” = 0 (don't report) + // errMode = mode of reporting error result code = 0 (numberic values) + // HeaderType = which data header of receiving data in multi-client mode = 1 (“+RECEIVE,,”) + // AsyncMode = sets mode of executing commands = 0 (synchronous command executing) + // TimeoutVal = minimum retransmission timeout in milliseconds = 75000 + sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000")); + if (waitResponse() != 1) { + return false; + } + + // Configure timeouts for opening and closing sockets + // AT+CIPTIMEOUT=[][, [][, []]] + sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000); + waitResponse(); + + // Start the socket service + + // This activates and attaches to the external PDP context that is tied + // to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1) + // Response may be an immediate "OK" followed later by "+NETOPEN: 0". + // 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; + } + + return true; + } + + bool gprsDisconnect() { + + // Close all sockets and stop the socket service + // Note: On the LTE models, this single command closes all sockets and the service + sendAT(GF("+NETCLOSE")); + if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { + return false; + } + + return true; + } + + bool isGprsConnected() { + 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; + } + waitResponse(); + + sendAT(GF("+IPADDR")); // Inquire Socket PDP address + // sendAT(GF("+CGPADDR=1")); // Show PDP address + if (waitResponse() != 1) { + return false; + } + + return true; + } + + /* + * IP Address functions + */ + + String getLocalIP() { + 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, ""); + res.trim(); + return res; + } + + IPAddress localIP() { + return TinyGsmIpFromString(getLocalIP()); + } + + /* + * Phone Call functions + */ + + bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + /* + * Messaging functions + */ + + String sendUSSD(const String& code) { + // Select message format (1=text) + sendAT(GF("+CMGF=1")); + waitResponse(); + // Select TE character set + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + sendAT(GF("+CUSD=1,\""), code, GF("\"")); + if (waitResponse() != 1) { + return ""; + } + if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { + return ""; + } + stream.readStringUntil('"'); + String hex = stream.readStringUntil('"'); + stream.readStringUntil(','); + int dcs = stream.readStringUntil('\n').toInt(); + + if (dcs == 15) { + return TinyGsmDecodeHex8bit(hex); + } else if (dcs == 72) { + return TinyGsmDecodeHex16bit(hex); + } else { + return hex; + } + } + + bool sendSMS(const String& number, const String& text) { + // Get SMS service centre address + sendAT(GF("+AT+CSCA?")); + waitResponse(); + // Select message format (1=text) + sendAT(GF("+CMGF=1")); + waitResponse(); + //Set GSM 7 bit default alphabet (3GPP TS 23.038) + sendAT(GF("+CSCS=\"GSM\"")); + waitResponse(); + // Send the message! + 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) { + // Select message format (1=text) + sendAT(GF("+CMGF=1")); + waitResponse(); + // Select TE character set + sendAT(GF("+CSCS=\"HEX\"")); + waitResponse(); + // Set text mode parameters + sendAT(GF("+CSMP=17,167,0,8")); + waitResponse(); + // Send the message + 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; + } + + + /* + * Location functions + */ + + String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + /* + * GPS location functions + */ + // enable GPS + bool enableGPS() { + sendAT(GF("+CGPS=1")); + if (waitResponse() != 1) { + return false; + } + return true; + } + + bool disableGPS() { + sendAT(GF("+CGPS=0")); + if (waitResponse() != 1) { + return false; + } + return true; + } + + // get the RAW GPS output + String getGPSraw() { + sendAT(GF("+CGNSSINFO=32")); + if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + // get GPS informations + bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) { + //String buffer = ""; + bool fix = false; + + sendAT(GF("+CGNSSINFO")); + if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { + return false; + } + + //stream.readStringUntil(','); // mode + if ( stream.readStringUntil(',').toInt() == 1 ) fix = true; + stream.readStringUntil(','); //gps + stream.readStringUntil(','); // glonass + stream.readStringUntil(','); // beidu + *lat = stream.readStringUntil(',').toFloat(); //lat + stream.readStringUntil(','); // N/S + *lon = stream.readStringUntil(',').toFloat(); //lon + stream.readStringUntil(','); // E/W + stream.readStringUntil(','); // date + stream.readStringUntil(','); // UTC time + if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); //alt + if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); //speed + stream.readStringUntil(','); //course + stream.readStringUntil(','); //time + stream.readStringUntil(',');//PDOP + stream.readStringUntil(',');//HDOP + stream.readStringUntil(',');//VDOP + //if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites + //if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites + stream.readStringUntil('\n'); + + waitResponse(); + + return fix; + } + /* + * Time functions + */ + + /* + * Battery & temperature functions + */ + + // Use: float vBatt = modem.getBattVoltage() / 1000.0; + uint16_t getBattVoltage() { + sendAT(GF("+CBC")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return 0; + } + + // get voltage in VOLTS + float voltage = stream.readStringUntil('\n').toFloat(); + // Wait for final OK + waitResponse(); + // Return millivolts + uint16_t res = voltage*1000; + return res; + } + + int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; + + uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; + + bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + sendAT(GF("+CBC?")); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { + return false; + } + // get voltage in VOLTS + float voltage = stream.readStringUntil('\n').toFloat(); + milliVolts = voltage*1000; + // Wait for final OK + waitResponse(); + return true; + } + + // get temperature in degree celsius + uint16_t getTemperature() { + sendAT(GF("+CPMUTEMP")); + if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { + return 0; + } + // return temperature in C + uint16_t res = stream.readStringUntil('\n').toInt(); + // Wait for final OK + waitResponse(); + return res; + } + + /* + * Client related functions + */ + +protected: + + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 75) { + // Make sure we'll be getting data manually on this connection + sendAT(GF("+CIPRXGET=1")); + if (waitResponse() != 1) { + return false; + } + + // Establish a connection in multi-socket mode + sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); + // The reply is +CIPOPEN: ## of socket created + if (waitResponse(15000L, GF(GSM_NL "+CIPOPEN:")) != 1) { + return false; + } + return true; + } + + int16_t modemSend(const void* buff, size_t len, uint8_t mux) { + sendAT(GF("+CIPSEND="), mux, ',', len); + if (waitResponse(GF(">")) != 1) { + return 0; + } + stream.write((uint8_t*)buff, len); + stream.flush(); + if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { + return 0; + } + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip requested bytes to send + // TODO: make sure requested and confirmed bytes match + return stream.readStringUntil('\n').toInt(); + } + + size_t modemRead(size_t size, uint8_t mux) { +#ifdef TINY_GSM_USE_HEX + sendAT(GF("+CIPRXGET=3,"), mux, ',', size); + if (waitResponse(GF("+CIPRXGET:")) != 1) { + return 0; + } +#else + sendAT(GF("+CIPRXGET=2,"), mux, ',', size); + if (waitResponse(GF("+CIPRXGET:")) != 1) { + return 0; + } +#endif + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux/cid (connecion id) + size_t len_requested = stream.readStringUntil(',').toInt(); + // ^^ Requested number of data bytes (1-1460 bytes)to be read + size_t len_confirmed = stream.readStringUntil('\n').toInt(); + // ^^ The data length which not read in the buffer + for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } + char buf[4] = { 0, }; + buf[0] = stream.read(); + buf[1] = stream.read(); + char c = strtol(buf, NULL, 16); +#else + while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } + char c = stream.read(); +#endif + sockets[mux]->rx.put(c); + } + DBG("### READ:", len_requested, "from", mux); + // sockets[mux]->sock_available = modemGetAvailable(mux); + sockets[mux]->sock_available = len_confirmed; + waitResponse(); + return len_requested; + } + + size_t modemGetAvailable(uint8_t mux) { + sendAT(GF("+CIPRXGET=4,"), mux); + size_t result = 0; + if (waitResponse(GF("+CIPRXGET:")) == 1) { + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = stream.readStringUntil('\n').toInt(); + waitResponse(); + } + DBG("### Available:", result, "on", mux); + 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?")); + if (waitResponse(GF("+CIPCLOSE:")) != 1) { + // return false; // TODO: Why does this not read correctly? + } + for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { + // +CIPCLOSE:,,..., + sockets[muxNo]->sock_connected = stream.parseInt(); + } + waitResponse(); // Should be an OK at the end + return sockets[mux]->sock_connected; + } + +public: + + /* + Utilities + */ + +TINY_GSM_MODEM_STREAM_UTILITIES() + + // TODO: Optimize this! + uint8_t waitResponse(uint32_t timeout_ms, 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) { + TINY_GSM_YIELD(); + 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 "+CIPRXGET:"))) { + String mode = stream.readStringUntil(','); + if (mode.toInt() == 1) { + int mux = stream.readStringUntil('\n').toInt(); + 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:"))) { + int mux = stream.readStringUntil(',').toInt(); + int len = stream.readStringUntil('\n').toInt(); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + sockets[mux]->sock_available = len; + } + data = ""; + DBG("### Got Data:", len, "on", mux); + } else if (data.endsWith(GF("+IPCLOSE:"))) { + int mux = stream.readStringUntil(',').toInt(); + 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 = ""; + } + } + } while (millis() - startMillis < timeout_ms); +finish: + if (!index) { + data.trim(); + if (data.length()) { + DBG("### Unhandled:", data); + } + data = ""; + } + //data.replace(GSM_NL, "/"); + //DBG('<', index, '>', data); + return index; + } + + uint8_t waitResponse(uint32_t timeout_ms, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + String data; + return waitResponse(timeout_ms, 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/TinyGsmClientSaraR4.h b/src/TinyGsmClientSaraR4.h index 8a1ba36..464e0ea 100644 --- a/src/TinyGsmClientSaraR4.h +++ b/src/TinyGsmClientSaraR4.h @@ -326,7 +326,7 @@ TINY_GSM_MODEM_GET_SIMCCID_CCID() return SIM_ERROR; } -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG) +TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) TINY_GSM_MODEM_GET_OPERATOR_COPS() @@ -340,8 +340,8 @@ TINY_GSM_MODEM_GET_CSQ() RegStatus s = getRegistrationStatus(); if (s == REG_OK_HOME || s == REG_OK_ROAMING) return true; - else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown.. - return isGprsConnected(); + // else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown.. + // return isGprsConnected(); else return false; } diff --git a/src/TinyGsmCommon.h b/src/TinyGsmCommon.h index c0ec41d..658bc68 100644 --- a/src/TinyGsmCommon.h +++ b/src/TinyGsmCommon.h @@ -10,7 +10,7 @@ #define TinyGsmCommon_h // The current library version number -#define TINYGSM_VERSION "0.9.6" +#define TINYGSM_VERSION "0.9.7" #if defined(SPARK) || defined(PARTICLE) #include "Particle.h"