diff --git a/.travis.yml b/.travis.yml index 8fd3573..a3dc361 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,8 @@ env: - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6' --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 --board=mayfly" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590' --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 --board=mayfly" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --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 --board=mayfly" - - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_U201' --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 --board=mayfly" + - 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 --board=mayfly" + - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96' --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 --board=mayfly" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --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 --board=mayfly" # Energia test @@ -32,7 +33,8 @@ env: - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6' --project-option='framework=energia' --board=lplm4f120h5qr" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590' --project-option='framework=energia' --board=lplm4f120h5qr" - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --project-option='framework=energia' --board=lplm4f120h5qr" - - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_U201' --project-option='framework=energia' --board=lplm4f120h5qr" + - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX' --project-option='framework=energia' --board=lplm4f120h5qr" + - PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96' --project-option='framework=energia' --board=lplm4f120h5qr" # Disabled due to a bug in Energia readBytes implementation #- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --project-option='framework=energia' --board=lplm4f120h5qr" diff --git a/README.md b/README.md index 47a631a..f7fc0db 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope ## Features -Feature \ Modem | SIM8xx | U201 | A6/A7/A20 | M590 | ESP8266 | XBee +Feature \ Modem | SIM8xx | Ublox | A6/A7/A20 | M590 | ESP8266 | XBee --- | --- | --- | --- | --- | --- | --- **Data connections** TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ @@ -67,22 +67,25 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | - SIMCom SIM800 series (SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868) - SIMCom SIM900 series (SIM900A, SIM900D, SIM908, SIM968) - AI-Thinker A6, A6C, A7, A20 -- U-blox SARA U201 (*alpha*) - ESP8266 (AT commands interface, similar to GSM modems) - Digi XBee WiFi and Cellular (using XBee command mode) - Neoway M590 +- U-blox SARA U2 (U201, U260, U270) +- Quectel BG96 ***(alpha)*** ### Supported boards/modules -- Arduino MKR GSM 1400 (*alpha*) +- Arduino MKR GSM 1400 - GPRSbee - Microduino GSM - Adafruit FONA (Mini Cellular GSM Breakout) - Adafruit FONA 800/808 Shield - Industruino GSM -- ... other modules, based on supported modems +- 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, M95, UG95 +- [ ] Quectel M10, M35, M95, UG95, EC21 +- [ ] Sequans Monarch LTE Cat M1/NB1 - [ ] SIMCom SIM5320, SIM5360, SIM5216, SIM7xxx - [ ] Telit GL865 - [ ] ZTE MG2639 @@ -110,9 +113,17 @@ If you have found TinyGSM to be useful in your work, research or company, please - Check your balance - Check that APN,User,Pass are correct and you have internet 2. Ensure the SIM card is correctly inserted into the module - 3. Check if serial connection is working (Hardware Serial is recommended) + 3. Ensure that GSM antenna is firmly attached + 4. Check if serial connection is working (Hardware Serial is recommended) Send an ```AT``` command using [this sketch](tools/AT_Debug/AT_Debug.ino) - 4. Ensure that GSM antenna is firmly attached + +If you have any issues: + + 1. Read the whole README (you're looking at it!) + 2. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration). + 3. Try running the Diagnostics sketch + 4. Check for [**highlighted topics here**](https://github.com/vshymanskyy/TinyGSM/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22for+reference%22+) + 5. If you have a question, please post it in our [Gitter chat](https://gitter.im/tinygsm) ## How does it work? @@ -133,8 +144,9 @@ Use this sketch to diagnose your SIM card and GPRS connection: ### Ensure stable data & power connection -This actually solves stability problems in **many** cases: -- Provide a good, [stable power supply](https://github.com/vshymanskyy/TinyGSM/wiki/Powering-GSM-module) (up to 2A and specific voltage according to your module documentation) +Most modules require up to 2A and specific voltage - according to the module documentation. +So this actually solves stability problems in **many** cases: +- Provide a good stable power supply. Read about [**powering your module**](https://github.com/vshymanskyy/TinyGSM/wiki/Powering-GSM-module). - Keep your wires as short as possible - Consider soldering them for a stable connection - Do not put your wires next to noisy signal sources (buck converters, antennas, oscillators etc.) @@ -143,6 +155,7 @@ This actually solves stability problems in **many** cases: When using ```SoftwareSerial``` (on Uno, Nano, etc), the speed **115200** may not work. Try selecting **57600**, **38400**, or even lower - the one that works best for you. +In some cases **9600** is unstable, but using **38400** helps, etc. Be sure to set correct TX/RX pins in the sketch. Please note that not every Arduino pin can serve as TX or RX pin. **Read more about SoftSerial options and configuration [here](https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html) and [here](https://www.arduino.cc/en/Reference/SoftwareSerial).** @@ -171,7 +184,7 @@ To return module to **Factory Defaults**, use this sketch: ### Goouuu Tech IOT-GA6 vs AI-Thinker A6 confusion -It turns out that **Goouuu Tech IOT-GA6** is not the same as **AI-Thinker A6**. Unfortunately IOT-GA6 is not supported out of the box yet. There are some hints that IOT-GA6 firmware may be updated to match A6... But no one confirmed that up to my knowledge. +It turns out that **Goouuu Tech IOT-GA6** is not the same as **AI-Thinker A6**. Unfortunately IOT-GA6 is not supported out of the box yet. There are some hints that IOT-GA6 firmware may be updated to match A6... See [this topic](https://github.com/vshymanskyy/TinyGSM/issues/164). __________ diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index 38cb365..d175ec2 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -126,6 +126,12 @@ void loop() { String gsmLoc = modem.getGsmLocation(); DBG("GSM location:", gsmLoc); + // This is only supported on SIMxxx series + String gsmTime = modem.getGSMDateTime(DATE_TIME); + DBG("GSM Time:", gsmTime); + String gsmDate = modem.getGSMDateTime(DATE_DATE); + DBG("GSM Date:", gsmDate); + String ussd_balance = modem.sendUSSD("*111#"); DBG("Balance (USSD):", ussd_balance); diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index e764242..5fa5111 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -25,6 +25,9 @@ // Uncomment this if you want to see all AT commands //#define DUMP_AT_COMMANDS +// Uncomment this if you want to use SSL +//#define USE_SSL + // Set serial for debug console (to the Serial Monitor, default speed 115200) #define SerialMon Serial @@ -45,7 +48,6 @@ const char pass[] = ""; // Server details const char server[] = "vsh.pp.ua"; const char resource[] = "/TinyGSM/logo.txt"; -const int port = 80; #ifdef DUMP_AT_COMMANDS #include @@ -55,7 +57,13 @@ const int port = 80; TinyGsm modem(SerialAT); #endif -TinyGsmClient client(modem); +#ifdef USE_SSL + TinyGsmClientSecure client(modem); + const int port = 443; +#else + TinyGsmClient client(modem); + const int port = 80; +#endif void setup() { // Set console baud rate diff --git a/examples/more/Hologram_Dash/Hologram_Dash.ino b/examples/more/Hologram_Dash/Hologram_Dash.ino new file mode 100644 index 0000000..532fee4 --- /dev/null +++ b/examples/more/Hologram_Dash/Hologram_Dash.ino @@ -0,0 +1,135 @@ +/************************************************************** + * + * This sketch connects to a website and downloads a page. + * It can be used to perform HTTP/RESTful API calls. + * + * TinyGSM Getting Started guide: + * http://tiny.cc/tiny-gsm-readme + * + **************************************************************/ + +// Hologram Dash uses UBLOX U2 modems +#define TINY_GSM_MODEM_UBLOX + +// Increase RX buffer if needed +//#define TINY_GSM_RX_BUFFER 512 + +#include + +// Uncomment this if you want to see all AT commands +//#define DUMP_AT_COMMANDS + +// Uncomment this if you want to use SSL +//#define USE_SSL + +// Set serial for debug console (to the Serial Monitor, speed 115200) +#define SerialMon Serial + +// We'll be using SerialSystem in Passthrough mode +#define SerialAT SerialSystem + +// Your GPRS credentials +// Leave empty, if missing user or pass +const char apn[] = "YourAPN"; +const char user[] = ""; +const char pass[] = ""; + +// Server details +const char server[] = "vsh.pp.ua"; +const char resource[] = "/TinyGSM/logo.txt"; + +#ifdef DUMP_AT_COMMANDS + #include + StreamDebugger debugger(SerialAT, SerialMon); + TinyGsm mdm(debugger); +#else + TinyGsm mdm(SerialAT); +#endif + +#ifdef USE_SSL + TinyGsmClientSecure client(mdm); + const int port = 443; +#else + TinyGsmClient client(mdm); + const int port = 80; +#endif + +void setup() { + // Set console baud rate + SerialMon.begin(115200); + delay(10); + + // Set up Passthrough + HologramCloud.enterPassthrough(); + delay(3000); + + // Restart takes quite some time + // To skip it, call init() instead of restart() + SerialMon.println(F("Initializing modem...")); + mdm.restart(); + + String modemInfo = mdm.getModemInfo(); + SerialMon.print(F("Modem: ")); + SerialMon.println(modemInfo); + + // Unlock your SIM card with a PIN + //mdm.simUnlock("1234"); +} + +void loop() { + SerialMon.print(F("Waiting for network...")); + if (!mdm.waitForNetwork()) { + SerialMon.println(" fail"); + delay(10000); + return; + } + SerialMon.println(" OK"); + + SerialMon.print(F("Connecting to ")); + SerialMon.print(apn); + if (!mdm.gprsConnect(apn, user, pass)) { + SerialMon.println(" fail"); + delay(10000); + return; + } + SerialMon.println(" OK"); + + SerialMon.print(F("Connecting to ")); + SerialMon.print(server); + if (!client.connect(server, port)) { + SerialMon.println(" fail"); + delay(10000); + return; + } + SerialMon.println(" OK"); + + // Make a HTTP GET request: + client.print(String("GET ") + resource + " HTTP/1.0\r\n"); + client.print(String("Host: ") + server + "\r\n"); + client.print("Connection: close\r\n\r\n"); + + unsigned long timeout = millis(); + while (client.connected() && millis() - timeout < 10000L) { + // Print available data + while (client.available()) { + char c = client.read(); + SerialMon.print(c); + timeout = millis(); + } + } + SerialMon.println(); + + // Shutdown + + client.stop(); + SerialMon.println(F("Server disconnected")); + + mdm.gprsDisconnect(); + SerialMon.println(F("GPRS disconnected")); + + // Do nothing forevermore + while (true) { + delay(1000); + } +} + diff --git a/extras/doc/Quectel_BG96_AT_Commands_Manual_V2_0.pdf b/extras/doc/Quectel_BG96_AT_Commands_Manual_V2_0.pdf new file mode 100644 index 0000000..283ac0a Binary files /dev/null and b/extras/doc/Quectel_BG96_AT_Commands_Manual_V2_0.pdf differ diff --git a/extras/doc/Quectel_BG96_SSL_AT_Commands_Manual_V1.0.pdf b/extras/doc/Quectel_BG96_SSL_AT_Commands_Manual_V1.0.pdf new file mode 100644 index 0000000..4c1c3b1 Binary files /dev/null and b/extras/doc/Quectel_BG96_SSL_AT_Commands_Manual_V1.0.pdf differ diff --git a/extras/doc/Quectel_BG96_TCPIP_AT_Commands_Manual_V1.0.pdf b/extras/doc/Quectel_BG96_TCPIP_AT_Commands_Manual_V1.0.pdf new file mode 100644 index 0000000..9080aeb Binary files /dev/null and b/extras/doc/Quectel_BG96_TCPIP_AT_Commands_Manual_V1.0.pdf differ diff --git a/extras/doc/Quectel_M35_at_command.pdf b/extras/doc/Quectel_M35_at_command.pdf new file mode 100644 index 0000000..8557235 Binary files /dev/null and b/extras/doc/Quectel_M35_at_command.pdf differ diff --git a/keywords.txt b/keywords.txt index 33f98c4..b6b4b10 100644 --- a/keywords.txt +++ b/keywords.txt @@ -24,3 +24,6 @@ factoryReset KEYWORD2 ####################################### # Literals (LITERAL1) ####################################### +DATE_FULL LITERAL1 +DATE_TIME LITERAL1 +DATE_DATE LITERAL1 diff --git a/src/ArduinoCompat/Client.h b/src/ArduinoCompat/Client.h new file mode 100644 index 0000000..e0c85de --- /dev/null +++ b/src/ArduinoCompat/Client.h @@ -0,0 +1,45 @@ +/* + Client.h - Base class that provides Client + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef client_h +#define client_h +#include "Print.h" +#include "Stream.h" +#include "ArduinoCompat/IPAddress.h" + +class Client : public Stream { + +public: + virtual int connect(IPAddress ip, uint16_t port) =0; + virtual int connect(const char *host, uint16_t port) =0; + virtual size_t write(uint8_t) =0; + virtual size_t write(const uint8_t *buf, size_t size) =0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); }; +}; + +#endif diff --git a/src/ArduinoCompat/IPAddress.h b/src/ArduinoCompat/IPAddress.h new file mode 100644 index 0000000..991b2cb --- /dev/null +++ b/src/ArduinoCompat/IPAddress.h @@ -0,0 +1,146 @@ +/* + IPAddress.h - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef IPAddress_h +#define IPAddress_h + +#include +#include "Printable.h" +#include "WString.h" + +// A class to make it easier to handle and pass around IP addresses + +class IPAddress : public Printable { +private: + union { + uint8_t bytes[4]; // IPv4 address + uint32_t dword; + } _address; + + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { return _address.bytes; }; + +public: + // Constructors + IPAddress() { + _address.dword = 0; + } + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { + _address.bytes[0] = first_octet; + _address.bytes[1] = second_octet; + _address.bytes[2] = third_octet; + _address.bytes[3] = fourth_octet; + } + + IPAddress(uint32_t address) { + _address.dword = address; + } + IPAddress(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); + } + + bool fromString(const char *address) { + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) { + // Value out of [0..255] range + return false; + } + } + else if (c == '.') + { + if (dots == 3) { + // Too much dots (there must be 3 dots) + return false; + } + _address.bytes[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return false; + } + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return false; + } + _address.bytes[3] = acc; + return true; + } + + bool fromString(const String &address) { return fromString(address.c_str()); } + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() const { return _address.dword; }; + bool operator==(const IPAddress& addr) const { return _address.dword == addr._address.dword; }; + bool operator==(const uint8_t* addr) const { + return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; + } + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { return _address.bytes[index]; }; + uint8_t& operator[](int index) { return _address.bytes[index]; }; + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); + return *this; + } + IPAddress& operator=(uint32_t address) { + _address.dword = address; + return *this; + } + + virtual size_t printTo(Print& p) const { + size_t n = 0; + for (int i =0; i < 3; i++) + { + n += p.print(_address.bytes[i], DEC); + n += p.print('.'); + } + n += p.print(_address.bytes[3], DEC); + return n; + } + + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; + friend class DNSClient; +}; + +const IPAddress INADDR_NONE(0,0,0,0); + +#endif diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index de6de0f..235d249 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -9,7 +9,7 @@ #ifndef TinyGsmClient_h #define TinyGsmClient_h -#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_U201) || defined(TINY_GSM_MODEM_ESP8266) +#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_UBLOX) || defined(TINY_GSM_MODEM_ESP8266) #define TINY_GSM_MODEM_HAS_SSL #endif @@ -31,6 +31,19 @@ typedef TinyGsmSim808::GsmClient TinyGsmClient; typedef TinyGsmSim808::GsmClientSecure TinyGsmClientSecure; +#elif defined(TINY_GSM_MODEM_UBLOX) + #define TINY_GSM_MODEM_HAS_GPRS + #include + typedef TinyGsmU201 TinyGsm; + typedef TinyGsmU201::GsmClient TinyGsmClient; + typedef TinyGsmU201::GsmClientSecure TinyGsmClientSecure; + +#elif defined(TINY_GSM_MODEM_BG96) + #define TINY_GSM_MODEM_HAS_GPRS + #include + typedef TinyGsmBG96 TinyGsm; + typedef TinyGsmBG96::GsmClient TinyGsmClient; + #elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7) #define TINY_GSM_MODEM_HAS_GPRS #include @@ -43,13 +56,6 @@ typedef TinyGsmM590 TinyGsm; typedef TinyGsm::GsmClient TinyGsmClient; -#elif defined(TINY_GSM_MODEM_U201) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmU201 TinyGsm; - typedef TinyGsmU201::GsmClient TinyGsmClient; - typedef TinyGsmU201::GsmClientSecure TinyGsmClientSecure; - #elif defined(TINY_GSM_MODEM_ESP8266) #define TINY_GSM_MODEM_HAS_WIFI #include diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h new file mode 100644 index 0000000..f65da65 --- /dev/null +++ b/src/TinyGsmClientBG96.h @@ -0,0 +1,770 @@ +/** + * @file TinyGsmClientBG96.h + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Apr 2018 + */ + +#ifndef TinyGsmClientBG96_h +#define TinyGsmClientBG96_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 5 + +#include + +#define GSM_NL "\r\n" +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; + +enum SimStatus { + SIM_ERROR = 0, + SIM_READY = 1, + SIM_LOCKED = 2, +}; + +enum RegStatus { + REG_UNREGISTERED = 0, + REG_SEARCHING = 2, + REG_DENIED = 3, + REG_OK_HOME = 1, + REG_OK_ROAMING = 5, + REG_UNKNOWN = 4, +}; + + +class TinyGsmBG96 +{ + +public: + +class GsmClient : public Client +{ + friend class TinyGsmBG96; + typedef TinyGsmFifo RxFifo; + +public: + GsmClient() {} + + GsmClient(TinyGsmBG96& modem, uint8_t mux = 1) { + init(&modem, mux); + } + + bool init(TinyGsmBG96* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + sock_connected = false; + got_data = false; + + at->sockets[mux] = this; + + return true; + } + +public: + virtual int connect(const char *host, uint16_t port) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux); + return sock_connected; + } + + virtual int connect(IPAddress ip, uint16_t port) { + String host; host.reserve(16); + host += ip[0]; + host += "."; + host += ip[1]; + host += "."; + host += ip[2]; + host += "."; + host += ip[3]; + return connect(host.c_str(), port); + } + + virtual void stop() { + TINY_GSM_YIELD(); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse(); + rx.clear(); + } + + virtual size_t write(const uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + return at->modemSend(buf, size, mux); + } + + virtual size_t write(uint8_t c) { + return write(&c, 1); + } + + virtual int available() { + TINY_GSM_YIELD(); + if (!rx.size()) { + at->maintain(); + } + return rx.size() + sock_available; + } + + virtual int read(uint8_t *buf, size_t size) { + TINY_GSM_YIELD(); + at->maintain(); + size_t cnt = 0; + while (cnt < size) { + size_t chunk = TinyGsmMin(size-cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } + // TODO: Read directly into user buffer? + at->maintain(); + if (sock_available > 0) { + sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); + } else { + break; + } + } + return cnt; + } + + virtual int read() { + uint8_t c; + if (read(&c, 1) == 1) { + return c; + } + return -1; + } + + virtual int peek() { return -1; } //TODO + virtual void flush() { at->stream.flush(); } + + virtual uint8_t connected() { + if (available()) { + return true; + } + return sock_connected; + } + virtual operator bool() { return connected(); } + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + +private: + TinyGsmBG96* at; + uint8_t mux; + uint16_t sock_available; + bool sock_connected; + bool got_data; + RxFifo rx; +}; + +class GsmClientSecure : public GsmClient +{ +public: + GsmClientSecure() {} + + GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1) + : GsmClient(modem, mux) + {} + +public: + virtual int connect(const char *host, uint16_t port) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true); + return sock_connected; + } +}; + +public: + + TinyGsmBG96(Stream& stream) + : stream(stream) + { + memset(sockets, 0, sizeof(sockets)); + } + + /* + * Basic functions + */ + bool begin() { + return init(); + } + + bool init() { + if (!testAT()) { + return false; + } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off + if (waitResponse() != 1) { + return false; + } + getSimStatus(); + return true; + } + + void setBaud(unsigned long baud) { + sendAT(GF("+IPR="), baud); + } + + bool testAT(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("")); + if (waitResponse(200) == 1) { + delay(100); + return true; + } + delay(100); + } + return false; + } + + void maintain() { + for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { + GsmClient* sock = sockets[mux]; + if (sock && sock->got_data) { + sock->got_data = false; + sock->sock_available = modemGetAvailable(mux); + } + } + while (stream.available()) { + waitResponse(10, NULL, NULL); + } + } + + bool factoryDefault() { + sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write + waitResponse(); + sendAT(GF("+IPR=0")); // Auto-baud + waitResponse(); + sendAT(GF("&W")); // Write configuration + return waitResponse() == 1; + } + + String getModemInfo() { + sendAT(GF("I")); + String res; + if (waitResponse(1000L, res) != 1) { + return ""; + } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, " "); + res.trim(); + return res; + } + + bool hasSSL() { + return false; // TODO: For now + } + + /* + * Power functions + */ + + bool restart() { + if (!testAT()) { + return false; + } + sendAT(GF("+CFUN=1,1")); + if (waitResponse(60000L, GF("POWERED DOWN")) != 1) { + return false; + } + delay(3000); + return init(); + } + + bool poweroff() { + sendAT(GF("+QPOWD")); + return waitResponse(GF("POWERED DOWN")) == 1; // TODO + } + + bool radioOff() { + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return true; + } + + /* + * SIM card functions + */ + + bool simUnlock(const char *pin) { + sendAT(GF("+CPIN=\""), pin, GF("\"")); + return waitResponse() == 1; + } + + String getSimCCID() { + sendAT(GF("+ICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + String getIMEI() { + sendAT(GF("+GSN")); + if (waitResponse(GF(GSM_NL)) != 1) { + return ""; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + SimStatus getSimStatus(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(GF("+CPIN?")); + if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { + delay(1000); + continue; + } + int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); + waitResponse(); + switch (status) { + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; + } + } + return SIM_ERROR; + } + + RegStatus getRegistrationStatus() { + sendAT(GF("+CREG?")); + if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { + return REG_UNKNOWN; + } + streamSkipUntil(','); // Skip format (0) + int status = stream.readStringUntil('\n').toInt(); + waitResponse(); + return (RegStatus)status; + } + + String getOperator() { + sendAT(GF("+COPS?")); + if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { + return ""; + } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + + /* + * Generic network functions + */ + + int getSignalQuality() { + sendAT(GF("+CSQ")); + if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { + return 99; + } + int res = stream.readStringUntil(',').toInt(); + waitResponse(); + return res; + } + + bool isNetworkConnected() { + RegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + bool waitForNetwork(unsigned long timeout = 60000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + if (isNetworkConnected()) { + return true; + } + delay(250); + } + return false; + } + + /* + * GPRS functions + */ + bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + gprsDisconnect(); + + sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); + if (waitResponse() != 1) { + return false; + } + + sendAT(GF("+QIACT=1")); + if (waitResponse(150000L) != 1) { + return false; + } + + sendAT(GF("+CGATT=1")); + if (waitResponse(60000L) != 1) { + return false; + } + + + return true; + } + + bool gprsDisconnect() { + sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context + if (waitResponse(40000L) != 1) + return false; + + return true; + } + + bool isGprsConnected() { + sendAT(GF("+CGATT?")); + if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { + return false; + } + int res = stream.readStringUntil('\n').toInt(); + waitResponse(); + if (res != 1) + return false; + + return localIP() != 0; + } + + String getLocalIP() { + sendAT(GF("+CGPADDR=1")); + if (waitResponse(10000L, GF(GSM_NL "+CGPADDR:")) != 1) { + return ""; + } + streamSkipUntil(','); + String res = stream.readStringUntil('\n'); + if (waitResponse() != 1) { + return ""; + } + return res; + } + + IPAddress localIP() { + return TinyGsmIpFromString(getLocalIP()); + } + + /* + * Phone Call functions + */ + + bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; + + bool callAnswer() { + sendAT(GF("A")); + return waitResponse() == 1; + } + + // Returns true on pick-up, false on error/busy + bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; + + bool callHangup() { + sendAT(GF("H")); + return waitResponse() == 1; + } + + // 0-9,*,#,A,B,C,D + bool dtmfSend(char cmd, int duration_ms = 100) { // TODO: check + duration_ms = constrain(duration_ms, 100, 1000); + + sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second + waitResponse(); + + sendAT(GF("+VTS="), cmd); + return waitResponse(10000L) == 1; + } + + /* + * Messaging functions + */ + + String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; + + bool sendSMS(const String& number, const String& text) { + sendAT(GF("+CMGF=1")); + waitResponse(); + //Set GSM 7 bit default alphabet (3GPP TS 23.038) + sendAT(GF("+CSCS=\"GSM\"")); + waitResponse(); + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + stream.print(text); + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + bool sendSMS_UTF16(const String& number, const void* text, size_t len) { + sendAT(GF("+CMGF=1")); + waitResponse(); + sendAT(GF("+CSMP=17,167,0,8")); + waitResponse(); + + sendAT(GF("+CMGS=\""), number, GF("\"")); + if (waitResponse(GF(">")) != 1) { + return false; + } + + uint16_t* t = (uint16_t*)text; + for (size_t i=0; i> 8; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + c = t[i] & 0xFF; + if (c < 0x10) { stream.print('0'); } + stream.print(c, HEX); + } + stream.write((char)0x1A); + stream.flush(); + return waitResponse(60000L) == 1; + } + + + /* + * Location functions + */ + + String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + + /* + * Battery functions + */ + uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED; + +protected: + + bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) { + int rsp; + sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0")); + rsp = waitResponse(); + + if (waitResponse(20000L, GF(GSM_NL "+QIOPEN:")) != 1) { + return false; + } + + if (stream.readStringUntil(',').toInt() != mux) { + return false; + } + // Read status + rsp = stream.readStringUntil('\n').toInt(); + + return (0 == rsp); + } + + int modemSend(const void* buff, size_t len, uint8_t mux) { + sendAT(GF("+QISEND="), mux, ',', len); + if (waitResponse(GF(">")) != 1) { + return 0; + } + stream.write((uint8_t*)buff, len); + stream.flush(); + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { + return 0; + } + // TODO: Wait for ACK? AT+QISEND=id,0 + return len; + } + + size_t modemRead(size_t size, uint8_t mux) { + sendAT(GF("+QIRD="), mux, ',', size); + if (waitResponse(GF("+QIRD:")) != 1) { + return 0; + } + size_t len = stream.readStringUntil('\n').toInt(); + + for (size_t i=0; irx.put(c); + } + waitResponse(); + DBG("### READ:", mux, ",", len); + return len; + } + + size_t modemGetAvailable(uint8_t mux) { + sendAT(GF("+QIRD="), mux, GF(",0")); + size_t result = 0; + if (waitResponse(GF("+QIRD:")) == 1) { + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read + result = stream.readStringUntil('\n').toInt(); + DBG("### STILL:", mux, "has", result); + waitResponse(); + } + if (!result) { + sockets[mux]->sock_connected = modemGetConnected(mux); + } + return result; + } + + bool modemGetConnected(uint8_t mux) { + sendAT(GF("+QISTATE=1,"), mux); + //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + + if (waitResponse(GF("+QISTATE:"))) + return false; + + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state + + waitResponse(); + + // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing + return 2 == res; + } + +public: + + /* Utilities */ + + template + void streamWrite(T last) { + stream.print(last); + } + + template + void streamWrite(T head, Args... tail) { + stream.print(head); + streamWrite(tail...); + } + + bool streamSkipUntil(char c) { //TODO: timeout + while (true) { + while (!stream.available()) { TINY_GSM_YIELD(); } + if (stream.read() == c) + return true; + } + return false; + } + + template + void sendAT(Args... cmd) { + streamWrite("AT", cmd..., GSM_NL); + stream.flush(); + TINY_GSM_YIELD(); + //DBG("### AT:", cmd...); + } + + // TODO: Optimize this! + uint8_t waitResponse(uint32_t timeout, String& data, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + /*String r1s(r1); r1s.trim(); + String r2s(r2); r2s.trim(); + String r3s(r3); r3s.trim(); + String r4s(r4); r4s.trim(); + String r5s(r5); r5s.trim(); + DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ + data.reserve(64); + int index = 0; + unsigned long startMillis = millis(); + do { + TINY_GSM_YIELD(); + while (stream.available() > 0) { + int a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += (char)a; + if (r1 && data.endsWith(r1)) { + index = 1; + goto finish; + } else if (r2 && data.endsWith(r2)) { + index = 2; + goto finish; + } else if (r3 && data.endsWith(r3)) { + index = 3; + goto finish; + } else if (r4 && data.endsWith(r4)) { + index = 4; + goto finish; + } else if (r5 && data.endsWith(r5)) { + index = 5; + goto finish; + } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) { + stream.readStringUntil('\"'); + String urc = stream.readStringUntil('\"'); + stream.readStringUntil(','); + if (urc == "recv") { + int mux = stream.readStringUntil('\n').toInt(); + DBG("### URC RECV:", mux); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + } + } else if (urc == "closed") { + int mux = stream.readStringUntil('\n').toInt(); + DBG("### URC CLOSE:", mux); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + } else { + stream.readStringUntil('\n'); + } + data = ""; + } + } + } while (millis() - startMillis < timeout); +finish: + if (!index) { + data.trim(); + if (data.length()) { + DBG("### Unhandled:", data); + } + data = ""; + } + return index; + } + + uint8_t waitResponse(uint32_t timeout, + GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + String data; + return waitResponse(timeout, data, r1, r2, r3, r4, r5); + } + + uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + return waitResponse(1000, r1, r2, r3, r4, r5); + } + +public: + Stream& stream; + +protected: + GsmClient* sockets[TINY_GSM_MUX_COUNT]; +}; + +#endif diff --git a/src/TinyGsmClientSIM800.h b/src/TinyGsmClientSIM800.h index b4c16cd..3f91c10 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -39,6 +39,11 @@ enum RegStatus { REG_UNKNOWN = 4, }; +enum DateTime { + DATE_FULL = 0, + DATE_TIME = 1, + DATE_DATE = 2 +}; //============================================================================// //============================================================================// @@ -342,6 +347,13 @@ public: if (!testAT()) { return false; } + //Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { + return false; + } + sendAT(GF("&W")); + waitResponse(); sendAT(GF("+CFUN=0")); if (waitResponse(10000L) != 1) { return false; @@ -708,6 +720,32 @@ public: return res; } + /* + * Time functions + */ + String getGSMDateTime(DateTime format) { + sendAT(GF("+CCLK?")); + if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { + return ""; + } + + String res; + + switch(format) { + case DATE_FULL: + res = stream.readStringUntil('"'); + break; + case DATE_TIME: + streamSkipUntil(','); + res = stream.readStringUntil('"'); + break; + case DATE_DATE: + res = stream.readStringUntil(','); + break; + } + return res; + } + /* * Battery functions */ diff --git a/src/TinyGsmClientU201.h b/src/TinyGsmClientUBLOX.h similarity index 80% rename from src/TinyGsmClientU201.h rename to src/TinyGsmClientUBLOX.h index 910f21b..9ea3c63 100644 --- a/src/TinyGsmClientU201.h +++ b/src/TinyGsmClientUBLOX.h @@ -9,7 +9,7 @@ #ifndef TinyGsmClientU201_h #define TinyGsmClientU201_h -// #define TINY_GSM_DEBUG Serial +//#define TINY_GSM_DEBUG Serial #if !defined(TINY_GSM_RX_BUFFER) #define TINY_GSM_RX_BUFFER 64 @@ -22,6 +22,7 @@ #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; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum SimStatus { SIM_ERROR = 0, @@ -39,22 +40,9 @@ enum RegStatus { }; -//============================================================================// -//============================================================================// -// Declaration of the TinyGsmU201 Class -//============================================================================// -//============================================================================// - class TinyGsmU201 { -//============================================================================// -//============================================================================// -// The U201 Client Class -//============================================================================// -//============================================================================// - - public: class GsmClient : public Client @@ -121,7 +109,7 @@ public: virtual int available() { TINY_GSM_YIELD(); - if (!rx.size() && sock_connected) { + if (!rx.size()) { at->maintain(); } return rx.size() + sock_available; @@ -142,7 +130,7 @@ public: // TODO: Read directly into user buffer? at->maintain(); if (sock_available > 0) { - at->modemRead(rx.free(), mux); + sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); } else { break; } @@ -184,13 +172,6 @@ private: RxFifo rx; }; -//============================================================================// -//============================================================================// -// The Secure U201 Client Class -//============================================================================// -//============================================================================// - - class GsmClientSecure : public GsmClient { public: @@ -211,13 +192,6 @@ public: } }; - -//============================================================================// -//============================================================================// -// The U201 Modem Functions -//============================================================================// -//============================================================================// - public: #ifdef GSM_DEFAULT_STREAM @@ -260,8 +234,8 @@ public: for (unsigned long start = millis(); millis() - start < timeout; ) { sendAT(GF("")); if (waitResponse(200) == 1) { - delay(100); - return true; + delay(100); + return true; } delay(100); } @@ -288,7 +262,17 @@ public: return waitResponse() == 1; } - String getModemInfo() TINY_GSM_ATTR_NOT_IMPLEMENTED; + String getModemInfo() { + sendAT(GF("I")); + String res; + if (waitResponse(1000L, res) != 1) { + return ""; + } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, " "); + res.trim(); + return res; + } bool hasSSL() { return true; @@ -312,9 +296,14 @@ public: bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool radioOff() { + sendAT(GF("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + return true; + } /* * SIM card functions @@ -366,6 +355,17 @@ public: return SIM_ERROR; } + RegStatus getRegistrationStatus() { + sendAT(GF("+CGREG?")); + if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) { + return REG_UNKNOWN; + } + streamSkipUntil(','); // Skip format (0) + int status = stream.readStringUntil('\n').toInt(); + waitResponse(); + return (RegStatus)status; + } + String getOperator() { sendAT(GF("+COPS?")); if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { @@ -381,17 +381,6 @@ public: * Generic network functions */ - RegStatus getRegistrationStatus() { - sendAT(GF("+CGREG?")); - if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) { - return REG_UNKNOWN; - } - streamSkipUntil(','); // Skip format (0) - int status = stream.readStringUntil('\n').toInt(); - waitResponse(); - return (RegStatus)status; - } - int getSignalQuality() { sendAT(GF("+CSQ")); if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { @@ -417,24 +406,6 @@ public: return false; } - String getLocalIP() { - sendAT(GF("+CIFSR;E0")); - String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } - res.trim(); - return res; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - - /* - * WiFi functions - */ - /* * GPRS functions */ @@ -442,7 +413,9 @@ public: gprsDisconnect(); sendAT(GF("+CGATT=1")); - waitResponse(5000L); + if (waitResponse(60000L) != 1) { + return false; + } sendAT(GF("+UPSD=0,1,\""), apn, '"'); waitResponse(); @@ -460,13 +433,16 @@ public: waitResponse(); sendAT(GF("+UPSDA=0,3")); - waitResponse(6000L); + if (waitResponse(60000L) != 1) { + return false; + } // Open a GPRS context sendAT(GF("+UPSND=0,8")); if (waitResponse(GF(",8,1")) != 1) { return false; } + waitResponse(); return true; } @@ -492,13 +468,39 @@ public: if (res != 1) return false; - sendAT(GF("+CIFSR")); - if (waitResponse() != 1) - return false; + return localIP() != 0; + } - return true; + String getLocalIP() { + sendAT(GF("+UPSND=0,0")); + if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { + return ""; + } + streamSkipUntil(','); // Skip PSD profile + streamSkipUntil('\"'); // Skip request type + String res = stream.readStringUntil('\"'); + if (waitResponse() != 1) { + return ""; + } + return res; + } + + IPAddress localIP() { + return TinyGsmIpFromString(getLocalIP()); } + /* + * Phone Call functions + */ + + bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; + + bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; + + bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* * Messaging functions */ @@ -516,7 +518,7 @@ public: String getGsmLocation() { sendAT(GF("+ULOC=2,3,0,120,1")); - if (waitResponse(GF(GSM_NL "+UULOC:")) != 1) { + if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); @@ -528,21 +530,19 @@ public: /* * Battery functions */ + uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CIND")); + int getBattPercent() { + sendAT(GF("+CIND?")); if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { return 0; } - uint16_t res = stream.readStringUntil(',').toInt(); + int res = stream.readStringUntil(',').toInt(); waitResponse(); return res; } - int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED; - protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) { @@ -558,6 +558,14 @@ protected: waitResponse(); } + // Enable NODELAY + sendAT(GF("+USOSO="), *mux, GF(",6,1,1")); + waitResponse(); + + // Enable KEEPALIVE, 30 sec + //sendAT(GF("+USOSO="), *mux, GF(",6,2,30000")); + //waitResponse(); + sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); int rsp = waitResponse(75000L); return (1 == rsp); @@ -566,17 +574,19 @@ protected: int modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+USOWR="), mux, ',', len); if (waitResponse(GF("@")) != 1) { - return -1; + return 0; } // 50ms delay, see AT manual section 25.10.4 delay(50); stream.write((uint8_t*)buff, len); stream.flush(); if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { - return -1; + return 0; } streamSkipUntil(','); // Skip mux - return stream.readStringUntil('\n').toInt(); + int sent = stream.readStringUntil('\n').toInt(); + waitResponse(); + return sent; } size_t modemRead(size_t size, uint8_t mux) { @@ -599,7 +609,7 @@ protected: } size_t modemGetAvailable(uint8_t mux) { - sendAT(GF("+USORD="), mux, ',', 0); + sendAT(GF("+USORD="), mux, ",0"); size_t result = 0; if (waitResponse(GF(GSM_NL "+USORD:")) == 1) { streamSkipUntil(','); // Skip mux @@ -620,7 +630,7 @@ protected: streamSkipUntil(','); // Skip mux streamSkipUntil(','); // Skip type int result = stream.readStringUntil('\n').toInt(); - + waitResponse(); return result != 0; } @@ -639,13 +649,9 @@ public: streamWrite(tail...); } - bool streamSkipUntil(char c) { - const unsigned long timeout = 1000L; - unsigned long startMillis = millis(); - while (millis() - startMillis < timeout) { - while (millis() - startMillis < timeout && !stream.available()) { - TINY_GSM_YIELD(); - } + bool streamSkipUntil(char c) { //TODO: timeout + while (true) { + while (!stream.available()) { TINY_GSM_YIELD(); } if (stream.read() == c) return true; } @@ -657,20 +663,20 @@ public: streamWrite("AT", cmd..., GSM_NL); stream.flush(); TINY_GSM_YIELD(); - DBG("### AT:", cmd...); + //DBG("### AT:", cmd...); } // TODO: Optimize this! uint8_t waitResponse(uint32_t timeout, String& data, GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) { - String r1s(r1); r1s.trim(); + /*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); + DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); int index = 0; unsigned long startMillis = millis(); @@ -678,7 +684,7 @@ public: TINY_GSM_YIELD(); while (stream.available() > 0) { int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case + if (a < 0) continue; data += (char)a; if (r1 && data.endsWith(r1)) { index = 1; @@ -697,17 +703,19 @@ public: goto finish; } else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) { int mux = stream.readStringUntil(',').toInt(); + streamSkipUntil('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } data = ""; + DBG("### Got Data:", mux); } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) { int mux = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } data = ""; - DBG("### Closed: ", mux); + DBG("### Closed:", mux); } } } while (millis() - startMillis < timeout); @@ -719,20 +727,19 @@ finish: } data = ""; } - DBG('<', index, '>'); return index; } uint8_t waitResponse(uint32_t timeout, GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) { String data; return waitResponse(timeout, data, r1, r2, r3, r4, r5); } uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) { return waitResponse(1000, r1, r2, r3, r4, r5); } diff --git a/src/TinyGsmCommon.h b/src/TinyGsmCommon.h index b145bb7..b803651 100644 --- a/src/TinyGsmCommon.h +++ b/src/TinyGsmCommon.h @@ -19,7 +19,12 @@ #endif #endif -#include +#if defined(ARDUINO_DASH) + #include +#else + #include +#endif + #include #ifndef TINY_GSM_YIELD @@ -44,15 +49,23 @@ #ifdef TINY_GSM_DEBUG namespace { template - static void DBG(T last) { + static void DBG_PLAIN(T last) { TINY_GSM_DEBUG.println(last); } template - static void DBG(T head, Args... tail) { + static void DBG_PLAIN(T head, Args... tail) { TINY_GSM_DEBUG.print(head); TINY_GSM_DEBUG.print(' '); - DBG(tail...); + DBG_PLAIN(tail...); + } + + template + static void DBG(Args... args) { + TINY_GSM_DEBUG.print(GF("[")); + TINY_GSM_DEBUG.print(millis()); + TINY_GSM_DEBUG.print(GF("] ")); + DBG_PLAIN(args...); } } #else diff --git a/tools/AT_Debug/AT_Debug.ino b/tools/AT_Debug/AT_Debug.ino index 56b63f7..878d7d2 100644 --- a/tools/AT_Debug/AT_Debug.ino +++ b/tools/AT_Debug/AT_Debug.ino @@ -39,7 +39,7 @@ uint32_t rate = 0; // Set to 0 for Auto-Detect void setup() { // Set console baud rate SerialMon.begin(115200); - delay(5000); + delay(3000); } void loop() { diff --git a/tools/Diagnostics/Diagnostics.ino b/tools/Diagnostics/Diagnostics.ino index 2ee17fb..38d3ffa 100644 --- a/tools/Diagnostics/Diagnostics.ino +++ b/tools/Diagnostics/Diagnostics.ino @@ -13,6 +13,7 @@ #define TINY_GSM_MODEM_SIM800 // #define TINY_GSM_MODEM_SIM808 // #define TINY_GSM_MODEM_SIM900 +// #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 // #define TINY_GSM_MODEM_M590 @@ -22,6 +23,9 @@ // Increase the buffer #define TINY_GSM_RX_BUFFER 512 +// Define the serial console for debug prints, if needed +//#define TINY_GSM_DEBUG Serial + #include // Your GPRS credentials @@ -45,11 +49,15 @@ const char pass[] = ""; #include StreamDebugger debugger(SerialAT, SerialMon); TinyGsm modem(debugger); -TinyGsmClient client(modem); const char server[] = "vsh.pp.ua"; const char resource[] = "/TinyGSM/logo.txt"; + const int port = 80; +TinyGsmClient client(modem); + +//const int port = 443; +//TinyGsmClientSecure client(modem); void setup() { // Set console baud rate @@ -66,7 +74,7 @@ void loop() { // To skip it, call init() instead of restart() SerialMon.print("Initializing modem..."); if (!modem.restart()) { - SerialMon.println(" fail"); + SerialMon.println(F(" [fail]")); SerialMon.println(F("************************")); SerialMon.println(F(" Is your modem connected properly?")); SerialMon.println(F(" Is your serial speed (baud rate) correct?")); @@ -75,14 +83,20 @@ void loop() { SerialMon.println(F(" Try useing File -> Examples -> TinyGSM -> tools -> AT_Debug to find correct configuration")); SerialMon.println(F("************************")); delay(10000); + return; } + SerialMon.println(F(" [OK]")); + + String modemInfo = modem.getModemInfo(); + SerialMon.print("Modem: "); + SerialMon.println(modemInfo); // Unlock your SIM card with a PIN //modem.simUnlock("1234"); SerialMon.print("Waiting for network..."); if (!modem.waitForNetwork()) { - SerialMon.println(" fail"); + SerialMon.println(F(" [fail]")); SerialMon.println(F("************************")); SerialMon.println(F(" Is your sim card locked?")); SerialMon.println(F(" Do you have a good signal?")); @@ -92,12 +106,12 @@ void loop() { delay(10000); return; } - SerialMon.println(" OK"); + SerialMon.println(F(" [OK]")); SerialMon.print("Connecting to "); SerialMon.print(apn); if (!modem.gprsConnect(apn, user, pass)) { - SerialMon.println(" fail"); + SerialMon.println(F(" [fail]")); SerialMon.println(F("************************")); SerialMon.println(F(" Is GPRS enabled by network provider?")); SerialMon.println(F(" Try checking your card balance.")); @@ -105,22 +119,33 @@ void loop() { delay(10000); return; } - SerialMon.println(" OK"); + SerialMon.println(F(" [OK]")); - SerialMon.print("Connecting to "); + IPAddress local = modem.localIP(); + SerialMon.print("Local IP: "); + SerialMon.println(local); + + SerialMon.print(F("Connecting to ")); SerialMon.print(server); if (!client.connect(server, port)) { - SerialMon.println(" fail"); + SerialMon.println(F(" [fail]")); delay(10000); return; } - SerialMon.println(" OK"); + SerialMon.println(F(" [OK]")); // Make a HTTP GET request: client.print(String("GET ") + resource + " HTTP/1.0\r\n"); client.print(String("Host: ") + server + "\r\n"); client.print("Connection: close\r\n\r\n"); + // Wait for data to arrive + while (client.connected() && !client.available()) { + delay(100); + SerialMon.print('.'); + }; + SerialMon.println(); + // Skip all headers client.find("\r\n\r\n"); @@ -130,26 +155,26 @@ void loop() { while (client.connected() && millis() - timeout < 10000L) { while (client.available()) { char c = client.read(); - //SerialMon.print(c); + SerialMon.print(c); bytesReceived += 1; timeout = millis(); } } client.stop(); - SerialMon.println("Server disconnected"); + SerialMon.println(F("Server disconnected")); modem.gprsDisconnect(); - SerialMon.println("GPRS disconnected"); + SerialMon.println(F("GPRS disconnected")); SerialMon.println(); - SerialMon.println("************************"); - SerialMon.print (" Received: "); + SerialMon.println(F("************************")); + SerialMon.print (F(" Received: ")); SerialMon.print(bytesReceived); - SerialMon.println(" bytes"); - SerialMon.print (" Test: "); + SerialMon.println(F(" bytes")); + SerialMon.print (F(" Test: ")); SerialMon.println((bytesReceived == 121) ? "PASSED" : "FAILED"); - SerialMon.println("************************"); + SerialMon.println(F("************************")); // Do nothing forevermore while (true) { diff --git a/tools/test_build/test_build.ino b/tools/test_build/test_build.ino index 32d8a65..245e38a 100644 --- a/tools/test_build/test_build.ino +++ b/tools/test_build/test_build.ino @@ -42,7 +42,7 @@ void loop() { // Test the Networking functions modem.getSignalQuality(); - + modem.localIP(); #if defined(TINY_GSM_MODEM_HAS_GPRS) modem.waitForNetwork();