Split SIM7000, add SIM7070, NTP
Signed-off-by: Sara Damiano <sdamiano@stroudcenter.org>
This commit is contained in:
2
.github/workflows/build_examples.yaml
vendored
2
.github/workflows/build_examples.yaml
vendored
@@ -32,6 +32,8 @@ jobs:
|
||||
TINY_GSM_MODEM_SIM5360,
|
||||
TINY_GSM_MODEM_SIM7600,
|
||||
TINY_GSM_MODEM_SIM7000,
|
||||
TINY_GSM_MODEM_SIM7000SSL,
|
||||
TINY_GSM_MODEM_SIM7070,
|
||||
TINY_GSM_MODEM_UBLOX,
|
||||
TINY_GSM_MODEM_SARAR4,
|
||||
TINY_GSM_MODEM_XBEE,
|
||||
|
58
README.md
58
README.md
@@ -17,6 +17,31 @@ If you like **TinyGSM** - give it a star, or fork it and contribute!
|
||||
You can also join our chat:
|
||||
[](https://gitter.im/tinygsm)
|
||||
|
||||
- [Supported modems](#supported-modems)
|
||||
- [Supported boards/modules](#supported-boardsmodules)
|
||||
- [Features](#features)
|
||||
- [Getting Started](#getting-started)
|
||||
- [First Steps](#first-steps)
|
||||
- [Writing your own code](#writing-your-own-code)
|
||||
- [If you have any issues](#if-you-have-any-issues)
|
||||
- [How does it work?](#how-does-it-work)
|
||||
- [API Reference](#api-reference)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Ensure stable data & power connection](#ensure-stable-data--power-connection)
|
||||
- [Baud rates](#baud-rates)
|
||||
- [Broken initial configuration](#broken-initial-configuration)
|
||||
- [Diagnostics sketch](#diagnostics-sketch)
|
||||
- [Web request formatting problems - "but it works with PostMan"](#web-request-formatting-problems---but-it-works-with-postman)
|
||||
- [SoftwareSerial problems](#softwareserial-problems)
|
||||
- [ESP32 Notes](#esp32-notes)
|
||||
- [HardwareSerial](#hardwareserial)
|
||||
- [HttpClient](#httpclient)
|
||||
- [SAMD21](#samd21)
|
||||
- [Goouuu Tech IOT-GA6 vs AI-Thinker A6 confusion](#goouuu-tech-iot-ga6-vs-ai-thinker-a6-confusion)
|
||||
- [SIM800 and SSL](#sim800-and-ssl)
|
||||
- [Which versions of the SIM7000 code to use](#which-versions-of-the-sim7000-code-to-use)
|
||||
- [License](#license)
|
||||
|
||||
### Arduino Client interface support
|
||||
This library is easy to integrate with lots of sketches which use Ethernet or WiFi.
|
||||
**PubSubClient ([MQTT](http://mqtt.org/))**, **[Blynk](http://blynk.cc)**, **HTTP Client** and **File Download** examples are provided.
|
||||
@@ -41,6 +66,7 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope
|
||||
- SIMCom WCDMA/HSPA/HSPA+ Modules (SIM5360, SIM5320, SIM5300E, SIM5300E/A)
|
||||
- SIMCom LTE Modules (SIM7100E, SIM7500E, SIM7500A, SIM7600C, SIM7600E)
|
||||
- SIMCom SIM7000E/A/G CAT-M1/NB-IoT Module
|
||||
- SIMCom SIM7070/SIM7080/SIM7090 CAT-M1/NB-IoT Module ***(alpha)***
|
||||
- AI-Thinker A6, A6C, A7, A20
|
||||
- ESP8266 (AT commands interface, similar to GSM modems)
|
||||
- Digi XBee WiFi and Cellular (using XBee command mode)
|
||||
@@ -86,7 +112,8 @@ Watch this repo for new updates! And of course, contributions are welcome ;)
|
||||
- Sequans Monarch - 6
|
||||
- SIM 800/900 - 5
|
||||
- SIM 5360/5320/5300/7100 - 10
|
||||
- SIM7000 - 2 (8 possible without SSL, only 2 with)
|
||||
- SIM7000 - 8 possible without SSL, only 2 with
|
||||
- SIM 7070/7080/7090 - 12
|
||||
- SIM 7500/7600/7800 - 10
|
||||
- u-blox 2G/3G - 7
|
||||
- u-blox SARA R4/N4 - 7
|
||||
@@ -96,7 +123,7 @@ Watch this repo for new updates! And of course, contributions are welcome ;)
|
||||
- SSL/TLS (HTTPS)
|
||||
- Supported on:
|
||||
- SIM800, SIM7000, u-Blox, XBee _cellular_, ESP8266, and Sequans Monarch
|
||||
- Note: only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.)
|
||||
- Note: **only some device models or firmware revisions have this feature** (SIM8xx R14.18, A7, etc.)
|
||||
- Not yet supported on:
|
||||
- Quectel modems, SIM 5360/5320/7100, SIM 7500/7600/7800
|
||||
- Not possible on:
|
||||
@@ -207,7 +234,7 @@ The general flow of your code should be:
|
||||
|
||||
|
||||
|
||||
#### If you have any issues:
|
||||
#### If you have any issues
|
||||
|
||||
1. Read the whole README (you're looking at it!), particularly the troubleshooting section below.
|
||||
2. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration).
|
||||
@@ -238,6 +265,13 @@ Improving the power supply actually solves stability problems in **many** cases!
|
||||
- Do not put your wires next to noisy signal sources (buck converters, antennas, oscillators etc.)
|
||||
- If everything else seems to be working but you are unable to connect to the network, check your power supply!
|
||||
|
||||
### Baud rates
|
||||
|
||||
Most modules support some sort of "auto-bauding" feature where the module will attempt to adjust it's baud rate to match what it is receiving.
|
||||
TinyGSM also implements its own auto bauding function (`TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);`).
|
||||
While very useful when initially connecting to a module and doing tests, these should **NOT** be used in any sort of production code.
|
||||
Once you've established communication with the module, set the baud rate using the `setBaud(#)` function and stick with that rate.
|
||||
|
||||
### Broken initial configuration
|
||||
|
||||
Sometimes (especially if you played with AT commands), your module configuration may become invalid.
|
||||
@@ -338,6 +372,24 @@ Please [refer to this comment](https://github.com/vshymanskyy/TinyGSM/issues/102
|
||||
|
||||
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).
|
||||
|
||||
### SIM800 and SSL
|
||||
|
||||
Some, but not all, versions of the SIM800 support SSL.
|
||||
Having SSL support depends on the firmware version and the individual module.
|
||||
Users have had varying levels of success in using SSL on the SIM800 even with apparently identical firmware.
|
||||
If you need SSL and it does not appear to be working on your SIM800, try a different module or try using a secondary SSL library.
|
||||
|
||||
### Which versions of the SIM7000 code to use
|
||||
|
||||
There are two versions of the SIM7000 code, one using `TINY_GSM_MODEM_SIM7000` and another with `TINY_GSM_MODEM_SIM7000SSL`.
|
||||
The `TINY_GSM_MODEM_SIM7000` version *does not support SSL*.
|
||||
The `TINY_GSM_MODEM_SIM7000SSL` version supports both SSL *and unsecured connections*.
|
||||
So why are there two versions?
|
||||
The "SSL" version uses the SIM7000's "application" commands while the other uses the "TCP-IP toolkit".
|
||||
Depending on your region/firmware, one or the other may not work for you.
|
||||
Try both and use whichever is more stable.
|
||||
If you do not need SSL, I recommend starting with `TINY_GSM_MODEM_SIM7000`.
|
||||
|
||||
__________
|
||||
|
||||
### License
|
||||
|
@@ -164,9 +164,7 @@ void loop() {
|
||||
|
||||
#if TINY_GSM_TEST_GPRS
|
||||
// Unlock your SIM card with a PIN if needed
|
||||
if (GSM_PIN && modem.getSimStatus() != 3) {
|
||||
modem.simUnlock(GSM_PIN);
|
||||
}
|
||||
if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); }
|
||||
#endif
|
||||
|
||||
#if TINY_GSM_TEST_WIFI
|
||||
@@ -190,9 +188,7 @@ void loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (modem.isNetworkConnected()) {
|
||||
DBG("Network connected");
|
||||
}
|
||||
if (modem.isNetworkConnected()) { DBG("Network connected"); }
|
||||
|
||||
#if TINY_GSM_TEST_GPRS
|
||||
DBG("Connecting to", apn);
|
||||
@@ -233,7 +229,7 @@ void loop() {
|
||||
|
||||
#if TINY_GSM_TEST_TCP && defined TINY_GSM_MODEM_HAS_TCP
|
||||
TinyGsmClient client(modem, 0);
|
||||
const int port = 80;
|
||||
const int port = 80;
|
||||
DBG("Connecting to ", server);
|
||||
if (!client.connect(server, port)) {
|
||||
DBG("... failed");
|
||||
@@ -252,7 +248,7 @@ void loop() {
|
||||
|
||||
// Read data
|
||||
start = millis();
|
||||
while (client.connected() && millis() - start < 5000L) {
|
||||
while (client.connected() && millis() - start < 10000L) {
|
||||
while (client.available()) {
|
||||
SerialMon.write(client.read());
|
||||
start = millis();
|
||||
@@ -264,7 +260,7 @@ void loop() {
|
||||
|
||||
#if TINY_GSM_TEST_SSL && defined TINY_GSM_MODEM_HAS_SSL
|
||||
TinyGsmClientSecure secureClient(modem, 1);
|
||||
const int securePort = 443;
|
||||
const int securePort = 443;
|
||||
DBG("Connecting to ", server);
|
||||
if (!secureClient.connect(server, securePort)) {
|
||||
DBG("... failed");
|
||||
@@ -294,7 +290,7 @@ void loop() {
|
||||
#endif
|
||||
|
||||
#if TINY_GSM_TEST_CALL && defined TINY_GSM_MODEM_HAS_CALLING && \
|
||||
defined CALL_TARGET
|
||||
defined CALL_TARGET
|
||||
DBG("Calling:", CALL_TARGET);
|
||||
|
||||
// This is NOT supported on M590
|
||||
@@ -308,9 +304,7 @@ void loop() {
|
||||
modem.dtmfSend('A', 1000);
|
||||
|
||||
// Play DTMF 0..4, default duration (100ms)
|
||||
for (char tone = '0'; tone <= '4'; tone++) {
|
||||
modem.dtmfSend(tone);
|
||||
}
|
||||
for (char tone = '0'; tone <= '4'; tone++) { modem.dtmfSend(tone); }
|
||||
|
||||
delay(5000);
|
||||
|
||||
@@ -344,7 +338,7 @@ void loop() {
|
||||
int day = 0;
|
||||
int hour = 0;
|
||||
int min = 0;
|
||||
int sec = 0;
|
||||
int sec = 0;
|
||||
for (int8_t i = 15; i; i--) {
|
||||
DBG("Requesting current GSM location");
|
||||
if (modem.getGsmLocation(&lat, &lon, &accuracy, &year, &month, &day, &hour,
|
||||
@@ -384,7 +378,7 @@ void loop() {
|
||||
for (int8_t i = 15; i; i--) {
|
||||
DBG("Requesting current GPS/GNSS/GLONASS location");
|
||||
if (modem.getGPS(&lat2, &lon2, &speed2, &alt2, &vsat2, &usat2, &accuracy2,
|
||||
&year2, &month2, &day2, &hour2, &min2, &sec2)) {
|
||||
&year2, &month2, &day2, &hour2, &min2, &sec2)) {
|
||||
DBG("Latitude:", String(lat2, 8), "\tLongitude:", String(lon2, 8));
|
||||
DBG("Speed:", speed2, "\tAltitude:", alt2);
|
||||
DBG("Visible Satellites:", vsat2, "\tUsed Satellites:", usat2);
|
||||
@@ -405,17 +399,17 @@ void loop() {
|
||||
#endif
|
||||
|
||||
#if TINY_GSM_TEST_TIME && defined TINY_GSM_MODEM_HAS_TIME
|
||||
int year3 = 0;
|
||||
int month3 = 0;
|
||||
int day3 = 0;
|
||||
int hour3 = 0;
|
||||
int min3 = 0;
|
||||
int sec3 = 0;
|
||||
int year3 = 0;
|
||||
int month3 = 0;
|
||||
int day3 = 0;
|
||||
int hour3 = 0;
|
||||
int min3 = 0;
|
||||
int sec3 = 0;
|
||||
float timezone = 0;
|
||||
for (int8_t i = 5; i; i--) {
|
||||
DBG("Requesting current network time");
|
||||
if (modem.getNetworkTime(&year3, &month3, &day3, &hour3, &min3, &sec3,
|
||||
&timezone)) {
|
||||
&timezone)) {
|
||||
DBG("Year:", year3, "\tMonth:", month3, "\tDay:", day3);
|
||||
DBG("Hour:", hour3, "\tMinute:", min3, "\tSecond:", sec3);
|
||||
DBG("Timezone:", timezone);
|
||||
@@ -470,7 +464,5 @@ void loop() {
|
||||
DBG("End of tests.");
|
||||
|
||||
// Do nothing forevermore
|
||||
while (true) {
|
||||
modem.maintain();
|
||||
}
|
||||
while (true) { modem.maintain(); }
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "TinyGSM",
|
||||
"version": "0.10.9",
|
||||
"version": "0.11.0",
|
||||
"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": [
|
||||
|
@@ -30,7 +30,19 @@ typedef TinyGsmSim800::GsmClientSim800 TinyGsmClient;
|
||||
#include "TinyGsmClientSIM7000.h"
|
||||
typedef TinyGsmSim7000 TinyGsm;
|
||||
typedef TinyGsmSim7000::GsmClientSim7000 TinyGsmClient;
|
||||
typedef TinyGsmSim7000::GsmClientSecureSIM7000 TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_SIM7000SSL)
|
||||
#include "TinyGsmClientSIM7000SSL.h"
|
||||
typedef TinyGsmSim7000SSL TinyGsm;
|
||||
typedef TinyGsmSim7000SSL::GsmClientSim7000SSL TinyGsmClient;
|
||||
typedef TinyGsmSim7000SSL::GsmClientSecureSIM7000SSL TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_SIM7070) || defined(TINY_GSM_MODEM_SIM7080) || \
|
||||
defined(TINY_GSM_MODEM_SIM7090)
|
||||
#include "TinyGsmClientSIM70x0.h"
|
||||
typedef TinyGsmSim70x0 TinyGsm;
|
||||
typedef TinyGsmSim70x0::GsmClientSim70x0 TinyGsmClient;
|
||||
typedef TinyGsmSim70x0::GsmClientSecureSIM70x0 TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_SIM5320) || defined(TINY_GSM_MODEM_SIM5360) || \
|
||||
defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100)
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmTemperature.tpp"
|
||||
#include "TinyGsmTime.tpp"
|
||||
#include "TinyGsmNTP.tpp"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
@@ -49,6 +50,7 @@ class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
|
||||
public TinyGsmCalling<TinyGsmBG96>,
|
||||
public TinyGsmSMS<TinyGsmBG96>,
|
||||
public TinyGsmTime<TinyGsmBG96>,
|
||||
public TinyGsmNTP<TinyGsmBG96>,
|
||||
public TinyGsmGPS<TinyGsmBG96>,
|
||||
public TinyGsmBattery<TinyGsmBG96>,
|
||||
public TinyGsmTemperature<TinyGsmBG96> {
|
||||
@@ -58,6 +60,7 @@ class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
|
||||
friend class TinyGsmCalling<TinyGsmBG96>;
|
||||
friend class TinyGsmSMS<TinyGsmBG96>;
|
||||
friend class TinyGsmTime<TinyGsmBG96>;
|
||||
friend class TinyGsmNTP<TinyGsmBG96>;
|
||||
friend class TinyGsmGPS<TinyGsmBG96>;
|
||||
friend class TinyGsmBattery<TinyGsmBG96>;
|
||||
friend class TinyGsmTemperature<TinyGsmBG96>;
|
||||
@@ -477,6 +480,27 @@ class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
|
||||
byte NTPServerSyncImpl(String server = "pool.ntp.org", byte TimeZone = 3) {
|
||||
// Request network synchronization
|
||||
// AT+QNTP=<contextID>,<server>[,<port>][,<autosettime>]
|
||||
sendAT(GF("+QNTP=1,\""), server, '"');
|
||||
if (waitResponse(10000L, GF("+QNTP:"))) {
|
||||
String result = stream.readStringUntil(',');
|
||||
streamSkipUntil('\n');
|
||||
result.trim();
|
||||
if (TinyGsmIsValidNumber(result)) { return result.toInt(); }
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
String ShowNTPErrorImpl(byte error) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmTemperature.tpp"
|
||||
#include "TinyGsmTime.tpp"
|
||||
#include "TinyGsmNTP.tpp"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
@@ -47,6 +48,7 @@ class TinyGsmSim5360 : public TinyGsmModem<TinyGsmSim5360>,
|
||||
public TinyGsmTCP<TinyGsmSim5360, TINY_GSM_MUX_COUNT>,
|
||||
public TinyGsmSMS<TinyGsmSim5360>,
|
||||
public TinyGsmTime<TinyGsmSim5360>,
|
||||
public TinyGsmNTP<TinyGsmSim5360>,
|
||||
public TinyGsmGSMLocation<TinyGsmSim5360>,
|
||||
public TinyGsmBattery<TinyGsmSim5360>,
|
||||
public TinyGsmTemperature<TinyGsmSim5360> {
|
||||
@@ -55,6 +57,7 @@ class TinyGsmSim5360 : public TinyGsmModem<TinyGsmSim5360>,
|
||||
friend class TinyGsmTCP<TinyGsmSim5360, TINY_GSM_MUX_COUNT>;
|
||||
friend class TinyGsmSMS<TinyGsmSim5360>;
|
||||
friend class TinyGsmTime<TinyGsmSim5360>;
|
||||
friend class TinyGsmNTP<TinyGsmSim5360>;
|
||||
friend class TinyGsmGSMLocation<TinyGsmSim5360>;
|
||||
friend class TinyGsmBattery<TinyGsmSim5360>;
|
||||
friend class TinyGsmTemperature<TinyGsmSim5360>;
|
||||
@@ -270,12 +273,17 @@ class TinyGsmSim5360 : public TinyGsmModem<TinyGsmSim5360>,
|
||||
return res;
|
||||
}
|
||||
|
||||
String setNetworkMode(uint8_t mode) {
|
||||
sendAT(GF("+CNMP="), mode);
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
int16_t getNetworkMode() {
|
||||
sendAT(GF("+CNMP?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; }
|
||||
int16_t mode = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return res;
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool setNetworkMode(uint8_t mode) {
|
||||
sendAT(GF("+CNMP="), mode);
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getLocalIPImpl() {
|
||||
@@ -435,6 +443,11 @@ class TinyGsmSim5360 : public TinyGsmModem<TinyGsmSim5360>,
|
||||
protected:
|
||||
// Can follow the standard CCLK function in the template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
|
@@ -12,49 +12,17 @@
|
||||
// #define TINY_GSM_DEBUG Serial
|
||||
// #define TINY_GSM_USE_HEX
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 2
|
||||
#define TINY_GSM_MUX_COUNT 8
|
||||
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
|
||||
|
||||
#include "TinyGsmBattery.tpp"
|
||||
#include "TinyGsmGPRS.tpp"
|
||||
#include "TinyGsmGPS.tpp"
|
||||
#include "TinyGsmModem.tpp"
|
||||
#include "TinyGsmSMS.tpp"
|
||||
#include "TinyGsmClientSIM70xx.h"
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmTime.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,
|
||||
};
|
||||
|
||||
class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
public TinyGsmGPRS<TinyGsmSim7000>,
|
||||
public TinyGsmTCP<TinyGsmSim7000, TINY_GSM_MUX_COUNT>,
|
||||
public TinyGsmSMS<TinyGsmSim7000>,
|
||||
public TinyGsmGPS<TinyGsmSim7000>,
|
||||
public TinyGsmTime<TinyGsmSim7000>,
|
||||
public TinyGsmBattery<TinyGsmSim7000> {
|
||||
friend class TinyGsmModem<TinyGsmSim7000>;
|
||||
friend class TinyGsmGPRS<TinyGsmSim7000>;
|
||||
class TinyGsmSim7000 : public TinyGsmSim70xx<TinyGsmSim7000>,
|
||||
public TinyGsmTCP<TinyGsmSim7000, TINY_GSM_MUX_COUNT> {
|
||||
friend class TinyGsmSim70xx<TinyGsmSim7000>;
|
||||
friend class TinyGsmTCP<TinyGsmSim7000, TINY_GSM_MUX_COUNT>;
|
||||
friend class TinyGsmSMS<TinyGsmSim7000>;
|
||||
friend class TinyGsmGPS<TinyGsmSim7000>;
|
||||
friend class TinyGsmTime<TinyGsmSim7000>;
|
||||
friend class TinyGsmBattery<TinyGsmSim7000>;
|
||||
|
||||
/*
|
||||
* Inner Client
|
||||
@@ -99,7 +67,7 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
|
||||
void stop(uint32_t maxWaitMs) {
|
||||
dumpModemBuffer(maxWaitMs);
|
||||
at->sendAT(GF("+CACLOSE="), mux);
|
||||
at->sendAT(GF("+CIPCLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse(3000);
|
||||
}
|
||||
@@ -117,79 +85,14 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
/*
|
||||
* Inner Secure Client
|
||||
*/
|
||||
|
||||
class GsmClientSecureSIM7000 : public GsmClientSim7000 {
|
||||
public:
|
||||
GsmClientSecureSIM7000() {}
|
||||
|
||||
GsmClientSecureSIM7000(TinyGsmSim7000& modem, uint8_t mux = 0)
|
||||
: GsmClientSim7000(modem, mux) {}
|
||||
|
||||
public:
|
||||
bool setCertificate(const String& certificateName) {
|
||||
return at->setCertificate(certificateName, mux);
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
public:
|
||||
boolean isValidNumber(String str) {
|
||||
if (!(str.charAt(0) == '+' || str.charAt(0) == '-' ||
|
||||
isDigit(str.charAt(0))))
|
||||
return false;
|
||||
|
||||
for (byte i = 1; i < str.length(); i++) {
|
||||
if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String ShowNTPError(byte error) {
|
||||
switch (error) {
|
||||
case 1: return "Network time synchronization is successful";
|
||||
case 61: return "Network error";
|
||||
case 62: return "DNS resolution error";
|
||||
case 63: return "Connection error";
|
||||
case 64: return "Service response error";
|
||||
case 65: return "Service response timeout";
|
||||
default: return "Unknown error: " + String(error);
|
||||
}
|
||||
}
|
||||
|
||||
byte NTPServerSync(String server = "pool.ntp.org", byte TimeZone = 3) {
|
||||
// Set GPRS bearer profile to associate with NTP sync
|
||||
sendAT(GF("+CNTPCID=1"));
|
||||
if (waitResponse(10000L) != 1) { return -1; }
|
||||
|
||||
// Set NTP server and timezone
|
||||
sendAT(GF("+CNTP="), server, ',', String(TimeZone));
|
||||
if (waitResponse(10000L) != 1) { return -1; }
|
||||
|
||||
// Request network synchronization
|
||||
sendAT(GF("+CNTP"));
|
||||
if (waitResponse(10000L, GF(GSM_NL "+CNTP:"))) {
|
||||
String result = stream.readStringUntil('\n');
|
||||
result.trim();
|
||||
if (isValidNumber(result)) { return result.toInt(); }
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// NOTE: Use modem TINYGSMSIM7000SSL for a secure client!
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public:
|
||||
explicit TinyGsmSim7000(Stream& stream) : stream(stream), certificates() {
|
||||
explicit TinyGsmSim7000(Stream& stream)
|
||||
: TinyGsmSim70xx<TinyGsmSim7000>(stream) {
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
@@ -235,165 +138,23 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
}
|
||||
}
|
||||
|
||||
String getModemNameImpl() {
|
||||
String name = "SIMCom SIM7000";
|
||||
|
||||
sendAT(GF("+GMM"));
|
||||
String res2;
|
||||
if (waitResponse(1000L, res2) != 1) { return name; }
|
||||
res2.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res2.replace("_", " ");
|
||||
res2.trim();
|
||||
|
||||
name = res2;
|
||||
return name;
|
||||
}
|
||||
|
||||
bool factoryDefaultImpl() { // these commands aren't supported
|
||||
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
|
||||
waitResponse();
|
||||
sendAT(GF("+IPR=0")); // Auto-baud
|
||||
waitResponse();
|
||||
sendAT(GF("+IFC=0,0")); // No Flow Control
|
||||
waitResponse();
|
||||
sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop
|
||||
waitResponse();
|
||||
sendAT(GF("+CSCLK=0")); // Disable Slow Clock
|
||||
waitResponse();
|
||||
sendAT(GF("&W")); // Write configuration
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
protected:
|
||||
bool restartImpl(const char* pin = NULL) {
|
||||
if (!setPhoneFunctionality(0)) { return false; }
|
||||
if (!setPhoneFunctionality(1, true)) { return false; }
|
||||
waitResponse(10000L, GF("SMS Ready"), GF("RDY"));
|
||||
return init(pin);
|
||||
}
|
||||
|
||||
bool powerOffImpl() {
|
||||
sendAT(GF("+CPOWD=1"));
|
||||
return waitResponse(GF("NORMAL POWER DOWN")) == 1;
|
||||
}
|
||||
|
||||
// During sleep, the SIM7000 module has its serial communication disabled.
|
||||
// In order to reestablish communication pull the DRT-pin of the SIM7000
|
||||
// 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) {
|
||||
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 SIM70xx template
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
public:
|
||||
RegStatus getRegistrationStatus() {
|
||||
RegStatus epsStatus = (RegStatus)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)getRegistrationStatusXREG("CGREG");
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
bool setCertificate(const String& certificateName, const uint8_t mux = 0) {
|
||||
if (mux >= TINY_GSM_MUX_COUNT) return false;
|
||||
certificates[mux] = certificateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isNetworkConnectedImpl() {
|
||||
RegStatus s = getRegistrationStatus();
|
||||
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
|
||||
}
|
||||
|
||||
public:
|
||||
String getNetworkModes() {
|
||||
// Get the help string, not the setting value
|
||||
sendAT(GF("+CNMP=?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool getNetworkMode(int16_t& mode) {
|
||||
sendAT(GF("+CNMP?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; }
|
||||
mode = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
String setNetworkMode(uint8_t mode) {
|
||||
sendAT(GF("+CNMP="), mode);
|
||||
if (waitResponse() != 1) return "";
|
||||
return "OK";
|
||||
}
|
||||
|
||||
String getPreferredModes() {
|
||||
// Get the help string, not the setting value
|
||||
sendAT(GF("+CMNB=?"));
|
||||
if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool getPreferredMode(int16_t& mode) {
|
||||
sendAT(GF("+CMNB?"));
|
||||
if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return false; }
|
||||
mode = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
String setPreferredMode(uint8_t mode) {
|
||||
sendAT(GF("+CMNB="), mode);
|
||||
if (waitResponse() != 1) return "";
|
||||
return "OK";
|
||||
}
|
||||
|
||||
bool getNetworkSystemMode(bool& n, int16_t& stat) {
|
||||
// n: whether to automatically report the system mode info
|
||||
// stat: the current service. 0 if it not connected
|
||||
sendAT(GF("+CNSMOD?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { return false; }
|
||||
n = streamGetIntBefore(',') != 0;
|
||||
stat = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
String setNetworkSystemMode(bool n) {
|
||||
// n: whether to automatically report the system mode info
|
||||
sendAT(GF("+CNSMOD="), int8_t(n));
|
||||
if (waitResponse() != 1) return "";
|
||||
return "OK";
|
||||
}
|
||||
|
||||
String getLocalIPImpl() {
|
||||
sendAT(GF("+CNACT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return ""; }
|
||||
streamSkipUntil('\"');
|
||||
String res = stream.readStringUntil('\"');
|
||||
waitResponse();
|
||||
sendAT(GF("+CIFSR;E0"));
|
||||
String res;
|
||||
if (waitResponse(10000L, res) != 1) { return ""; }
|
||||
res.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res.replace(GSM_NL, "");
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -405,24 +166,23 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
const char* pwd = NULL) {
|
||||
gprsDisconnect();
|
||||
|
||||
// Open data connection
|
||||
sendAT(GF("+CNACT=1,\""), apn, GF("\""));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// Set the Bearer for the IP
|
||||
sendAT(GF(
|
||||
"+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the connection type to GPRS
|
||||
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); // Set the APN
|
||||
// Set the APN
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Set the user name
|
||||
if (user && strlen(user) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"'); // Set the user name
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
|
||||
waitResponse();
|
||||
}
|
||||
// Set the password
|
||||
if (pwd && strlen(pwd) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"'); // Set the password
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
@@ -430,6 +190,10 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Attach to GPRS
|
||||
sendAT(GF("+CGATT=1"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// Activate the PDP context
|
||||
sendAT(GF("+CGACT=1,1"));
|
||||
waitResponse(60000L);
|
||||
@@ -441,24 +205,39 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
sendAT(GF("+SAPBR=2,1"));
|
||||
if (waitResponse(30000L) != 1) { return false; }
|
||||
|
||||
// Attach to GPRS
|
||||
sendAT(GF("+CGATT=1"));
|
||||
// Set the TCP application toolkit to multi-IP
|
||||
sendAT(GF("+CIPMUX=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
// Put the TCP application toolkit in "quick send" mode
|
||||
// (thus no extra "Send OK")
|
||||
sendAT(GF("+CIPQSEND=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
// Set the TCP application toolkit to get data manually
|
||||
sendAT(GF("+CIPRXGET=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
// Start the TCP application toolkit task and set APN, USER NAME, PASSWORD
|
||||
sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// Check data connection
|
||||
// Bring up the TCP application toolkit wireless connection with GPRS or CSD
|
||||
sendAT(GF("+CIICR"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
sendAT(GF("+CNACT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return false; }
|
||||
int res = streamGetIntBefore(',');
|
||||
waitResponse();
|
||||
// Get local IP address for the TCP application toolkit
|
||||
// only assigned after connection
|
||||
sendAT(GF("+CIFSR;E0"));
|
||||
if (waitResponse(10000L) != 1) { return false; }
|
||||
|
||||
return res == 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnectImpl() {
|
||||
// Shut the TCP/IP connection
|
||||
// CNACT will close *all* open connections
|
||||
sendAT(GF("+CNACT=0"));
|
||||
// Shut the TCP application toolkit connection
|
||||
// CIPSHUT will close *all* open TCP application toolkit connections
|
||||
sendAT(GF("+CIPSHUT"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
|
||||
@@ -471,15 +250,7 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
* SIM card functions
|
||||
*/
|
||||
protected:
|
||||
// Doesn't return the "+CCID" before the number
|
||||
String getSimCCIDImpl() {
|
||||
sendAT(GF("+CCID"));
|
||||
if (waitResponse(GF(GSM_NL)) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
@@ -491,113 +262,18 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
* GPS/GNSS/GLONASS location functions
|
||||
*/
|
||||
protected:
|
||||
// enable GPS
|
||||
bool enableGPSImpl() {
|
||||
sendAT(GF("+CGNSPWR=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool disableGPSImpl() {
|
||||
sendAT(GF("+CGNSPWR=0"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the RAW GPS output
|
||||
String getGPSrawImpl() {
|
||||
sendAT(GF("+CGNSINF"));
|
||||
if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
// get GPS informations
|
||||
bool getGPSImpl(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("+CGNSINF"));
|
||||
if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return false; }
|
||||
|
||||
streamSkipUntil(','); // GNSS run status
|
||||
if (streamGetIntBefore(',') == 1) { // fix status
|
||||
// init variables
|
||||
float ilat = 0;
|
||||
float ilon = 0;
|
||||
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;
|
||||
|
||||
// UTC date & Time
|
||||
iyear = streamGetIntLength(4); // Four digit year
|
||||
imonth = streamGetIntLength(2); // Two digit month
|
||||
iday = streamGetIntLength(2); // Two digit day
|
||||
ihour = streamGetIntLength(2); // Two digit hour
|
||||
imin = streamGetIntLength(2); // Two digit minute
|
||||
secondWithSS =
|
||||
streamGetFloatBefore(','); // 6 digit second with subseconds
|
||||
|
||||
ilat = streamGetFloatBefore(','); // Latitude
|
||||
ilon = streamGetFloatBefore(','); // Longitude
|
||||
ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters
|
||||
ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots.
|
||||
streamSkipUntil(','); // Course Over Ground. Degrees.
|
||||
streamSkipUntil(','); // Fix Mode
|
||||
streamSkipUntil(','); // Reserved1
|
||||
iaccuracy =
|
||||
streamGetFloatBefore(','); // Horizontal Dilution Of Precision
|
||||
streamSkipUntil(','); // Position Dilution Of Precision
|
||||
streamSkipUntil(','); // Vertical Dilution Of Precision
|
||||
streamSkipUntil(','); // Reserved2
|
||||
ivsat = streamGetIntBefore(','); // GNSS Satellites in View
|
||||
iusat = streamGetIntBefore(','); // GNSS Satellites Used
|
||||
streamSkipUntil(','); // GLONASS Satellites Used
|
||||
streamSkipUntil(','); // Reserved3
|
||||
streamSkipUntil(','); // C/N0 max
|
||||
streamSkipUntil(','); // HPA
|
||||
streamSkipUntil('\n'); // VPA
|
||||
|
||||
// Set pointers
|
||||
if (lat != NULL) *lat = ilat;
|
||||
if (lon != NULL) *lon = ilon;
|
||||
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<int>(secondWithSS);
|
||||
|
||||
waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
streamSkipUntil('\n'); // toss the row of commas
|
||||
waitResponse();
|
||||
return false;
|
||||
}
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Time functions
|
||||
*/
|
||||
// Can follow CCLK as per template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
@@ -610,161 +286,106 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
protected:
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
|
||||
bool ssl = false, int timeout_s = 75) {
|
||||
if (ssl) { DBG("SSL only supported using application on SIM7000!"); }
|
||||
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
|
||||
|
||||
sendAT(GF("+CACID="), mux);
|
||||
if (waitResponse(timeout_ms) != 1) return false;
|
||||
|
||||
if (ssl) {
|
||||
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
|
||||
if (waitResponse() != 1) return false;
|
||||
|
||||
sendAT(GF("+CSSLCFG=\"ctxindex\",0"));
|
||||
if (waitResponse() != 1) return false;
|
||||
|
||||
if (certificates[mux] != "") {
|
||||
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
|
||||
"\"");
|
||||
if (waitResponse() != 1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
sendAT(GF("+CASSLCFG="), mux, ',', GF("ssl,"), ssl);
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0"));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+CSSLCFG=\"sni\","), mux, ',', GF("\""), host, GF("\""));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+CAOPEN="), mux, ',', GF("\""), host, GF("\","), port);
|
||||
|
||||
if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; }
|
||||
streamSkipUntil(','); // Skip mux
|
||||
|
||||
int8_t res = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
|
||||
return 0 == res;
|
||||
// when not using SSL, the TCP application toolkit is more stable
|
||||
sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host,
|
||||
GF("\","), port);
|
||||
return (1 ==
|
||||
waitResponse(timeout_ms, GF("CONNECT OK" GSM_NL),
|
||||
GF("CONNECT FAIL" GSM_NL),
|
||||
GF("ALREADY CONNECT" GSM_NL), GF("ERROR" GSM_NL),
|
||||
GF("CLOSE OK" GSM_NL)));
|
||||
}
|
||||
|
||||
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
|
||||
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
|
||||
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
|
||||
if (waitResponse(GF(">")) != 1) { return 0; }
|
||||
|
||||
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
|
||||
stream.flush();
|
||||
|
||||
if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; }
|
||||
streamSkipUntil(','); // Skip mux
|
||||
if (streamGetIntBefore(',') != 0) { return 0; } // If result != success
|
||||
if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { return 0; }
|
||||
streamSkipUntil(','); // Skip mux
|
||||
return streamGetIntBefore('\n');
|
||||
}
|
||||
|
||||
size_t modemRead(size_t size, uint8_t mux) {
|
||||
if (!sockets[mux]) return 0;
|
||||
|
||||
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
|
||||
|
||||
if (waitResponse(GF("+CARECV:")) != 1) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream.read();
|
||||
if (stream.peek() == '0') {
|
||||
waitResponse();
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int16_t len_confirmed = streamGetIntBefore(',');
|
||||
if (len_confirmed <= 0) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
waitResponse();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len_confirmed; i++) {
|
||||
#ifdef TINY_GSM_USE_HEX
|
||||
sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
|
||||
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
|
||||
#else
|
||||
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
|
||||
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
|
||||
#endif
|
||||
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
|
||||
streamSkipUntil(','); // Skip mux
|
||||
int16_t len_requested = streamGetIntBefore(',');
|
||||
// ^^ Requested number of data bytes (1-1460 bytes)to be read
|
||||
int16_t len_confirmed = streamGetIntBefore('\n');
|
||||
// ^^ Confirmed number of data bytes to be read, which may be less than
|
||||
// requested. 0 indicates that no data can be read.
|
||||
// SRGD NOTE: Contrary to above (which is copied from AT command manual)
|
||||
// this is actually be the number of bytes that will be remaining in the
|
||||
// buffer after the read.
|
||||
for (int i = 0; i < len_requested; i++) {
|
||||
uint32_t startMillis = millis();
|
||||
#ifdef TINY_GSM_USE_HEX
|
||||
while (stream.available() < 2 &&
|
||||
(millis() - startMillis < sockets[mux]->_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);
|
||||
auto diff = int64_t(size) - int64_t(len_confirmed);
|
||||
if (diff < 0) diff = 0;
|
||||
sockets[mux]->sock_available = diff;
|
||||
sockets[mux]->sock_available = len_confirmed;
|
||||
waitResponse();
|
||||
return len_confirmed;
|
||||
return len_requested;
|
||||
}
|
||||
|
||||
size_t modemGetAvailable(uint8_t mux) {
|
||||
if (!sockets[mux]) return 0;
|
||||
|
||||
if (!sockets[mux]->sock_connected) {
|
||||
sockets[mux]->sock_connected = modemGetConnected(mux);
|
||||
sendAT(GF("+CIPRXGET=4,"), mux);
|
||||
size_t result = 0;
|
||||
if (waitResponse(GF("+CIPRXGET:")) == 1) {
|
||||
streamSkipUntil(','); // Skip mode 4
|
||||
streamSkipUntil(','); // Skip mux
|
||||
result = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
}
|
||||
if (!sockets[mux]->sock_connected) return 0;
|
||||
|
||||
sendAT(GF("+CARECV?"));
|
||||
|
||||
int8_t readMux = -1;
|
||||
size_t result = 0;
|
||||
while (readMux != mux) {
|
||||
if (waitResponse(GF("+CARECV:")) != 1) {
|
||||
sockets[mux]->sock_connected = modemGetConnected(mux);
|
||||
return 0;
|
||||
};
|
||||
readMux = streamGetIntBefore(',');
|
||||
result = streamGetIntBefore('\n');
|
||||
}
|
||||
waitResponse();
|
||||
|
||||
// DBG("### Available:", result, "on", mux);
|
||||
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
|
||||
return result;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux) {
|
||||
sendAT(GF("+CASTATE?"));
|
||||
int8_t readMux = -1;
|
||||
while (readMux != mux) {
|
||||
if (waitResponse(3000, GF("+CASTATE:"), GFP(GSM_OK)) != 1) { return 0; }
|
||||
readMux = streamGetIntBefore(',');
|
||||
}
|
||||
int8_t res = streamGetIntBefore('\n');
|
||||
sendAT(GF("+CIPSTATUS="), mux);
|
||||
waitResponse(GF("+CIPSTATUS"));
|
||||
int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
|
||||
GF(",\"CLOSING\""), GF(",\"REMOTE CLOSING\""),
|
||||
GF(",\"INITIAL\""));
|
||||
waitResponse();
|
||||
return 1 == res;
|
||||
}
|
||||
|
||||
public:
|
||||
bool modemGetConnected(const char* host, uint16_t port, uint8_t mux) {
|
||||
sendAT(GF("+CAOPEN?"));
|
||||
int8_t readMux = -1;
|
||||
while (readMux != mux) {
|
||||
if (waitResponse(GF("+CAOPEN:")) != 1) return 0;
|
||||
readMux = streamGetIntBefore(',');
|
||||
}
|
||||
streamSkipUntil('\"');
|
||||
|
||||
size_t hostLen = strlen(host);
|
||||
|
||||
char buffer[hostLen];
|
||||
stream.readBytesUntil('\"', buffer, hostLen);
|
||||
streamSkipUntil(',');
|
||||
uint16_t connectedPort = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
bool samePort = connectedPort == port;
|
||||
bool sameHost = memcmp(buffer, host, hostLen) == 0;
|
||||
sockets[mux]->sock_connected = sameHost && samePort;
|
||||
|
||||
return sockets[mux]->sock_connected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
@@ -828,30 +449,6 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
} else {
|
||||
data += mode;
|
||||
}
|
||||
} else if (data.endsWith(GF(GSM_NL "+CARECV:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
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 "+CADATAIND:"))) {
|
||||
int8_t mux = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->got_data = true;
|
||||
}
|
||||
data = "";
|
||||
// DBG("### Got Data:", mux);
|
||||
} else if (data.endsWith(GF(GSM_NL "+CASTATE:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
int8_t state = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
if (state != 1) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
int16_t len = streamGetIntBefore('\n');
|
||||
@@ -887,6 +484,10 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
'\n'); // Refresh Network Daylight Saving Time by network
|
||||
data = "";
|
||||
DBG("### Daylight savings time state updated.");
|
||||
} else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) {
|
||||
data = "";
|
||||
DBG("### Unexpected module reset!");
|
||||
init();
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout_ms);
|
||||
@@ -926,13 +527,8 @@ class TinyGsmSim7000 : public TinyGsmModem<TinyGsmSim7000>,
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
GsmClientSim7000* sockets[TINY_GSM_MUX_COUNT];
|
||||
String certificates[TINY_GSM_MUX_COUNT];
|
||||
const char* gsmNL = GSM_NL;
|
||||
};
|
||||
|
||||
#endif // SRC_TINYGSMCLIENTSIM7000_H_
|
||||
|
646
src/TinyGsmClientSIM7000SSL.h
Normal file
646
src/TinyGsmClientSIM7000SSL.h
Normal file
@@ -0,0 +1,646 @@
|
||||
/**
|
||||
* @file TinyGsmClientSim7000SSL.h
|
||||
* @author Volodymyr Shymanskyy
|
||||
* @license LGPL-3.0
|
||||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* @date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef SRC_TINYGSMCLIENTSIM7000SSL_H_
|
||||
#define SRC_TINYGSMCLIENTSIM7000SSL_H_
|
||||
|
||||
// #define TINY_GSM_DEBUG Serial
|
||||
// #define TINY_GSM_USE_HEX
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 2
|
||||
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
|
||||
|
||||
#include "TinyGsmClientSIM70xx.h"
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmSSL.tpp"
|
||||
|
||||
class TinyGsmSim7000SSL
|
||||
: public TinyGsmSim70xx<TinyGsmSim7000SSL>,
|
||||
public TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>,
|
||||
public TinyGsmSSL<TinyGsmSim7000SSL> {
|
||||
friend class TinyGsmSim70xx<TinyGsmSim7000SSL>;
|
||||
friend class TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>;
|
||||
friend class TinyGsmSSL<TinyGsmSim7000SSL>;
|
||||
|
||||
/*
|
||||
* Inner Client
|
||||
*/
|
||||
public:
|
||||
class GsmClientSim7000SSL : public GsmClient {
|
||||
friend class TinyGsmSim7000SSL;
|
||||
|
||||
public:
|
||||
GsmClientSim7000SSL() {}
|
||||
|
||||
explicit GsmClientSim7000SSL(TinyGsmSim7000SSL& modem, uint8_t mux = 0) {
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsmSim7000SSL* modem, uint8_t mux = 0) {
|
||||
this->at = modem;
|
||||
sock_available = 0;
|
||||
prev_check = 0;
|
||||
sock_connected = false;
|
||||
got_data = false;
|
||||
|
||||
if (mux < TINY_GSM_MUX_COUNT) {
|
||||
this->mux = mux;
|
||||
} else {
|
||||
this->mux = (mux % TINY_GSM_MUX_COUNT);
|
||||
}
|
||||
at->sockets[this->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_OVERRIDES
|
||||
|
||||
void stop(uint32_t maxWaitMs) {
|
||||
dumpModemBuffer(maxWaitMs);
|
||||
at->sendAT(GF("+CACLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse(3000);
|
||||
}
|
||||
void stop() override {
|
||||
stop(15000L);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
/*
|
||||
* Inner Secure Client
|
||||
*/
|
||||
|
||||
class GsmClientSecureSIM7000SSL : public GsmClientSim7000SSL {
|
||||
public:
|
||||
GsmClientSecureSIM7000SSL() {}
|
||||
|
||||
GsmClientSecureSIM7000SSL(TinyGsmSim7000SSL& modem, uint8_t mux = 0)
|
||||
: GsmClientSim7000SSL(modem, mux) {}
|
||||
|
||||
public:
|
||||
bool setCertificate(const String& certificateName) {
|
||||
return at->setCertificate(certificateName, mux);
|
||||
}
|
||||
|
||||
int connect(const char* host, uint16_t port, int timeout_s) {
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
|
||||
return sock_connected;
|
||||
}
|
||||
TINY_GSM_CLIENT_CONNECT_OVERRIDES
|
||||
};
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public:
|
||||
explicit TinyGsmSim7000SSL(Stream& stream)
|
||||
: TinyGsmSim70xx<TinyGsmSim7000SSL>(stream),
|
||||
certificates() {
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
protected:
|
||||
bool initImpl(const char* pin = NULL) {
|
||||
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
|
||||
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000SSL"));
|
||||
|
||||
if (!testAT()) { return false; }
|
||||
|
||||
sendAT(GF("E0")); // Echo Off
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
#ifdef TINY_GSM_DEBUG
|
||||
sendAT(GF("+CMEE=2")); // turn on verbose error codes
|
||||
#else
|
||||
sendAT(GF("+CMEE=0")); // turn off error codes
|
||||
#endif
|
||||
waitResponse();
|
||||
|
||||
DBG(GF("### Modem:"), getModemName());
|
||||
|
||||
// Enable Local Time Stamp for getting network time
|
||||
sendAT(GF("+CLTS=1"));
|
||||
if (waitResponse(10000L) != 1) { return false; }
|
||||
|
||||
// Enable battery checks
|
||||
sendAT(GF("+CBATCHK=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
SimStatus ret = getSimStatus();
|
||||
// if the sim isn't ready and a pin has been provided, try to unlock the sim
|
||||
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
|
||||
simUnlock(pin);
|
||||
return (getSimStatus() == SIM_READY);
|
||||
} else {
|
||||
// if the sim is ready, or it's locked but no pin has been provided,
|
||||
// return true
|
||||
return (ret == SIM_READY || ret == SIM_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
protected:
|
||||
String getLocalIPImpl() {
|
||||
sendAT(GF("+CNACT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return ""; }
|
||||
streamSkipUntil('\"');
|
||||
String res = stream.readStringUntil('\"');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secure socket layer functions
|
||||
*/
|
||||
protected:
|
||||
bool setCertificate(const String& certificateName, const uint8_t mux = 0) {
|
||||
if (mux >= TINY_GSM_MUX_COUNT) return false;
|
||||
certificates[mux] = certificateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
protected:
|
||||
bool gprsConnectImpl(const char* apn, const char* user = NULL,
|
||||
const char* pwd = NULL) {
|
||||
gprsDisconnect();
|
||||
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the connection type to GPRS
|
||||
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
|
||||
waitResponse();
|
||||
|
||||
// Set the APN
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Set the user name
|
||||
if (user && strlen(user) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
|
||||
waitResponse();
|
||||
}
|
||||
// Set the password
|
||||
if (pwd && strlen(pwd) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
// Define the PDP context
|
||||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Attach to GPRS
|
||||
sendAT(GF("+CGATT=1"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// Activate the PDP context
|
||||
sendAT(GF("+CGACT=1,1"));
|
||||
waitResponse(60000L);
|
||||
|
||||
// Open the definied GPRS bearer context
|
||||
sendAT(GF("+SAPBR=1,1"));
|
||||
waitResponse(85000L);
|
||||
// Query the GPRS bearer context status
|
||||
sendAT(GF("+SAPBR=2,1"));
|
||||
if (waitResponse(30000L) != 1) { return false; }
|
||||
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the user name and password
|
||||
// AT+CNCFG=<ip_type>[,<APN>[,<usename>,<password>[,<authentication>]]]
|
||||
//<ip_type> 0: Dual PDN Stack
|
||||
// 1: Internet Protocol Version 4
|
||||
// 2: Internet Protocol Version 6
|
||||
//<authentication> 0: NONE
|
||||
// 1: PAP
|
||||
// 2: CHAP
|
||||
// 3: PAP or CHAP
|
||||
if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) {
|
||||
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", "\",\"", user, pwd, '"');
|
||||
waitResponse();
|
||||
} else if (user && strlen(user) > 0) {
|
||||
// Set the user name only
|
||||
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", user, '"');
|
||||
waitResponse();
|
||||
} else {
|
||||
// Set the APN only
|
||||
sendAT(GF("+CNCFG=1,\""), apn, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
// Activate application network connection
|
||||
// This is for most other supported applications outside of the
|
||||
// TCP application toolkit (ie, SSL)
|
||||
// AT+CNACT=<mode>,<action>
|
||||
// <mode> 0: Deactive
|
||||
// 1: Active
|
||||
// 2: Auto Active
|
||||
int res = 0;
|
||||
int ntries = 0;
|
||||
while (res != 1 && ntries < 5) {
|
||||
sendAT(GF("+CNACT=1,\""), apn, GF("\""));
|
||||
res = waitResponse(60000L, GF(GSM_NL "+APP PDP: ACTIVE"),
|
||||
GF(GSM_NL "+APP PDP: DEACTIVE"));
|
||||
waitResponse();
|
||||
ntries++;
|
||||
}
|
||||
|
||||
// return res == 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnectImpl() {
|
||||
// Shut down the general application TCP/IP connection
|
||||
// CNACT will close *all* open application connections
|
||||
sendAT(GF("+CNACT=0"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
protected:
|
||||
// Follows all messaging functions per template
|
||||
|
||||
/*
|
||||
* GPS/GNSS/GLONASS location functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Time functions
|
||||
*/
|
||||
// Can follow CCLK as per template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
protected:
|
||||
// Follows all battery functions per template
|
||||
|
||||
/*
|
||||
* Client related functions
|
||||
*/
|
||||
protected:
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
|
||||
bool ssl = false, int timeout_s = 75) {
|
||||
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
|
||||
|
||||
// set the connection (mux) identifier to use
|
||||
sendAT(GF("+CACID="), mux);
|
||||
if (waitResponse(timeout_ms) != 1) return false;
|
||||
|
||||
|
||||
if (ssl) {
|
||||
// set the ssl version
|
||||
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
|
||||
if (waitResponse(5000L) != 1) return false;
|
||||
|
||||
// set the PDP context to apply SSL to
|
||||
sendAT(GF("+CSSLCFG=\"ctxindex\",0"));
|
||||
if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false;
|
||||
streamSkipUntil('\n'); // read out the certificate information
|
||||
waitResponse();
|
||||
|
||||
if (certificates[mux] != "") {
|
||||
// apply the correct certificate to the connection
|
||||
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
|
||||
"\"");
|
||||
if (waitResponse(5000L) != 1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// enable or disable ssl
|
||||
sendAT(GF("+CASSLCFG="), mux, ',', GF("ssl,"), ssl);
|
||||
waitResponse();
|
||||
|
||||
// set the protocol
|
||||
// 0: TCP; 1: UDP
|
||||
sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0"));
|
||||
waitResponse();
|
||||
|
||||
// set the SSL SNI (server name indication)
|
||||
sendAT(GF("+CSSLCFG=\"sni\","), mux, ',', GF("\""), host, GF("\""));
|
||||
waitResponse();
|
||||
|
||||
// actually open the connection
|
||||
// AT+CAOPEN=<cid>[,<conn_type>],<server>,<port>
|
||||
// <cid> TCP/UDP identifier
|
||||
// <conn_type> "TCP" or "UDP"
|
||||
sendAT(GF("+CAOPEN="), mux, GF(",\"TCP\",\""), host, GF("\","), port);
|
||||
if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; }
|
||||
// returns OK/r/n/r/n+CAOPEN: <cid>,<result>
|
||||
// <result> 0: Success
|
||||
// 1: Socket error
|
||||
// 2: No memory
|
||||
// 3: Connection limit
|
||||
// 4: Parameter invalid
|
||||
// 6: Invalid IP address
|
||||
// 7: Not support the function
|
||||
// 12: Can’t bind the port
|
||||
// 13: Can’t listen the port
|
||||
// 20: Can’t resolv the host
|
||||
// 21: Network not active
|
||||
// 23: Remote refuse
|
||||
// 24: Certificate’s time expired
|
||||
// 25: Certificate’s common name does not match
|
||||
// 26: Certificate’s common name does not match and time expired
|
||||
// 27: Connect failed
|
||||
streamSkipUntil(','); // Skip mux
|
||||
|
||||
// make sure the connection really opened
|
||||
int8_t res = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
|
||||
return 0 == res;
|
||||
}
|
||||
|
||||
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
|
||||
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
|
||||
if (waitResponse(GF(">")) != 1) { return 0; }
|
||||
|
||||
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
|
||||
stream.flush();
|
||||
|
||||
if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; }
|
||||
streamSkipUntil(','); // Skip mux
|
||||
if (streamGetIntBefore(',') != 0) { return 0; } // If result != success
|
||||
return streamGetIntBefore('\n');
|
||||
}
|
||||
|
||||
size_t modemRead(size_t size, uint8_t mux) {
|
||||
if (!sockets[mux]) return 0;
|
||||
|
||||
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
|
||||
|
||||
if (waitResponse(GF("+CARECV:")) != 1) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream.read();
|
||||
if (stream.peek() == '0') {
|
||||
waitResponse();
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int16_t len_confirmed = streamGetIntBefore(',');
|
||||
if (len_confirmed <= 0) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
waitResponse();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len_confirmed; i++) {
|
||||
uint32_t startMillis = millis();
|
||||
while (!stream.available() &&
|
||||
(millis() - startMillis < sockets[mux]->_timeout)) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
char c = stream.read();
|
||||
sockets[mux]->rx.put(c);
|
||||
}
|
||||
// DBG("### READ:", len_requested, "from", mux);
|
||||
// sockets[mux]->sock_available = modemGetAvailable(mux);
|
||||
auto diff = int64_t(size) - int64_t(len_confirmed);
|
||||
if (diff < 0) diff = 0;
|
||||
sockets[mux]->sock_available = diff;
|
||||
waitResponse();
|
||||
return len_confirmed;
|
||||
}
|
||||
|
||||
size_t modemGetAvailable(uint8_t mux) {
|
||||
if (!sockets[mux]) return 0;
|
||||
|
||||
sendAT(GF("+CARECV?"));
|
||||
|
||||
int8_t readMux = -1;
|
||||
size_t result = 0;
|
||||
while (readMux != mux) {
|
||||
if (waitResponse(GF("+CARECV:")) != 1) {
|
||||
sockets[mux]->sock_connected = modemGetConnected(mux);
|
||||
return 0;
|
||||
};
|
||||
readMux = streamGetIntBefore(',');
|
||||
result = streamGetIntBefore('\n');
|
||||
}
|
||||
waitResponse();
|
||||
// DBG("### Available:", result, "on", mux);
|
||||
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
|
||||
return result;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux) {
|
||||
sendAT(GF("+CASTATE?"));
|
||||
int8_t readMux = -1;
|
||||
while (readMux != mux) {
|
||||
if (waitResponse(3000, GF("+CASTATE:"), GFP(GSM_OK)) != 1) { return 0; }
|
||||
readMux = streamGetIntBefore(',');
|
||||
}
|
||||
int8_t res = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return 1 == res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
public:
|
||||
// TODO(vshymanskyy): Optimize this!
|
||||
int8_t waitResponse(uint32_t timeout_ms, String& data,
|
||||
GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
uint8_t index = 0;
|
||||
uint32_t startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
TINY_GSM_YIELD();
|
||||
int8_t a = stream.read();
|
||||
if (a <= 0) continue; // Skip 0x00 bytes, just in case
|
||||
data += static_cast<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)) {
|
||||
#if defined TINY_GSM_DEBUG
|
||||
if (r3 == GFP(GSM_CME_ERROR)) {
|
||||
streamSkipUntil('\n'); // Read out the error
|
||||
}
|
||||
#endif
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF(GSM_NL "+CARECV:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
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 "+CADATAIND:"))) {
|
||||
int8_t mux = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->got_data = true;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Got Data:", mux);
|
||||
} else if (data.endsWith(GF(GSM_NL "+CASTATE:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
int8_t state = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
if (state != 1) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF("CLOSED" GSM_NL))) {
|
||||
int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8);
|
||||
int8_t coma = data.indexOf(',', nl + 2);
|
||||
int8_t mux = data.substring(nl + 2, coma).toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
} else if (data.endsWith(GF("*PSNWID:"))) {
|
||||
streamSkipUntil('\n'); // Refresh network name by network
|
||||
data = "";
|
||||
DBG("### Network name updated.");
|
||||
} else if (data.endsWith(GF("*PSUTTZ:"))) {
|
||||
streamSkipUntil('\n'); // Refresh time and time zone by network
|
||||
data = "";
|
||||
DBG("### Network time and time zone updated.");
|
||||
} else if (data.endsWith(GF("+CTZV:"))) {
|
||||
streamSkipUntil('\n'); // Refresh network time zone by network
|
||||
data = "";
|
||||
DBG("### Network time zone updated.");
|
||||
} else if (data.endsWith(GF("DST: "))) {
|
||||
streamSkipUntil(
|
||||
'\n'); // Refresh Network Daylight Saving Time by network
|
||||
data = "";
|
||||
DBG("### Daylight savings time state updated.");
|
||||
} else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) {
|
||||
data = "";
|
||||
DBG("### Unexpected module reset!");
|
||||
init();
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
String data;
|
||||
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
protected:
|
||||
GsmClientSim7000SSL* sockets[TINY_GSM_MUX_COUNT];
|
||||
String certificates[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif // SRC_TINYGSMCLIENTSIM7000SSL_H_
|
676
src/TinyGsmClientSIM70x0.h
Normal file
676
src/TinyGsmClientSIM70x0.h
Normal file
@@ -0,0 +1,676 @@
|
||||
/**
|
||||
* @file TinyGsmClientSim70x0.h
|
||||
* @author Volodymyr Shymanskyy
|
||||
* @license LGPL-3.0
|
||||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* @date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef SRC_TINYGSMCLIENTSIM70X0_H_
|
||||
#define SRC_TINYGSMCLIENTSIM70X0_H_
|
||||
|
||||
// #define TINY_GSM_DEBUG Serial
|
||||
// #define TINY_GSM_USE_HEX
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 12
|
||||
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
|
||||
|
||||
#include "TinyGsmClientSIM70xx.h"
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmSSL.tpp"
|
||||
|
||||
class TinyGsmSim70x0 : public TinyGsmSim70xx<TinyGsmSim70x0>,
|
||||
public TinyGsmTCP<TinyGsmSim70x0, TINY_GSM_MUX_COUNT>,
|
||||
public TinyGsmSSL<TinyGsmSim70x0> {
|
||||
friend class TinyGsmSim70xx<TinyGsmSim70x0>;
|
||||
friend class TinyGsmTCP<TinyGsmSim70x0, TINY_GSM_MUX_COUNT>;
|
||||
friend class TinyGsmSSL<TinyGsmSim70x0>;
|
||||
|
||||
/*
|
||||
* Inner Client
|
||||
*/
|
||||
public:
|
||||
class GsmClientSim70x0 : public GsmClient {
|
||||
friend class TinyGsmSim70x0;
|
||||
|
||||
public:
|
||||
GsmClientSim70x0() {}
|
||||
|
||||
explicit GsmClientSim70x0(TinyGsmSim70x0& modem, uint8_t mux = 0) {
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsmSim70x0* modem, uint8_t mux = 0) {
|
||||
this->at = modem;
|
||||
sock_available = 0;
|
||||
prev_check = 0;
|
||||
sock_connected = false;
|
||||
got_data = false;
|
||||
|
||||
if (mux < TINY_GSM_MUX_COUNT) {
|
||||
this->mux = mux;
|
||||
} else {
|
||||
this->mux = (mux % TINY_GSM_MUX_COUNT);
|
||||
}
|
||||
at->sockets[this->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_OVERRIDES
|
||||
|
||||
void stop(uint32_t maxWaitMs) {
|
||||
dumpModemBuffer(maxWaitMs);
|
||||
at->sendAT(GF("+CACLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse(3000);
|
||||
}
|
||||
void stop() override {
|
||||
stop(15000L);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
/*
|
||||
* Inner Secure Client
|
||||
*/
|
||||
|
||||
class GsmClientSecureSIM70x0 : public GsmClientSim70x0 {
|
||||
public:
|
||||
GsmClientSecureSIM70x0() {}
|
||||
|
||||
GsmClientSecureSIM70x0(TinyGsmSim70x0& modem, uint8_t mux = 0)
|
||||
: GsmClientSim70x0(modem, mux) {}
|
||||
|
||||
public:
|
||||
bool setCertificate(const String& certificateName) {
|
||||
return at->setCertificate(certificateName, mux);
|
||||
}
|
||||
|
||||
int connect(const char* host, uint16_t port, int timeout_s) {
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
|
||||
return sock_connected;
|
||||
}
|
||||
TINY_GSM_CLIENT_CONNECT_OVERRIDES
|
||||
};
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public:
|
||||
explicit TinyGsmSim70x0(Stream& stream)
|
||||
: TinyGsmSim70xx<TinyGsmSim70x0>(stream),
|
||||
certificates() {
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
protected:
|
||||
bool initImpl(const char* pin = NULL) {
|
||||
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
|
||||
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM70x0"));
|
||||
|
||||
if (!testAT()) { return false; }
|
||||
|
||||
sendAT(GF("E0")); // Echo Off
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
#ifdef TINY_GSM_DEBUG
|
||||
sendAT(GF("+CMEE=2")); // turn on verbose error codes
|
||||
#else
|
||||
sendAT(GF("+CMEE=0")); // turn off error codes
|
||||
#endif
|
||||
waitResponse();
|
||||
|
||||
DBG(GF("### Modem:"), getModemName());
|
||||
|
||||
// Enable Local Time Stamp for getting network time
|
||||
sendAT(GF("+CLTS=1"));
|
||||
if (waitResponse(10000L) != 1) { return false; }
|
||||
|
||||
// Enable battery checks
|
||||
sendAT(GF("+CBATCHK=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
|
||||
SimStatus ret = getSimStatus();
|
||||
// if the sim isn't ready and a pin has been provided, try to unlock the sim
|
||||
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
|
||||
simUnlock(pin);
|
||||
return (getSimStatus() == SIM_READY);
|
||||
} else {
|
||||
// if the sim is ready, or it's locked but no pin has been provided,
|
||||
// return true
|
||||
return (ret == SIM_READY || ret == SIM_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
protected:
|
||||
String getLocalIPImpl() {
|
||||
sendAT(GF("+CNACT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return ""; }
|
||||
streamSkipUntil('\"');
|
||||
String res = stream.readStringUntil('\"');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secure socket layer functions
|
||||
*/
|
||||
protected:
|
||||
bool setCertificate(const String& certificateName, const uint8_t mux = 0) {
|
||||
if (mux >= TINY_GSM_MUX_COUNT) return false;
|
||||
certificates[mux] = certificateName;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
protected:
|
||||
bool gprsConnectImpl(const char* apn, const char* user = NULL,
|
||||
const char* pwd = NULL) {
|
||||
gprsDisconnect();
|
||||
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the connection type to GPRS
|
||||
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
|
||||
waitResponse();
|
||||
|
||||
// Set the APN
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Set the user name
|
||||
if (user && strlen(user) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
|
||||
waitResponse();
|
||||
}
|
||||
// Set the password
|
||||
if (pwd && strlen(pwd) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
// Define the PDP context
|
||||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Attach to GPRS
|
||||
sendAT(GF("+CGATT=1"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// Activate the PDP context
|
||||
sendAT(GF("+CGACT=1,1"));
|
||||
waitResponse(60000L);
|
||||
|
||||
// Open the definied GPRS bearer context
|
||||
sendAT(GF("+SAPBR=1,1"));
|
||||
waitResponse(85000L);
|
||||
// Query the GPRS bearer context status
|
||||
sendAT(GF("+SAPBR=2,1"));
|
||||
if (waitResponse(30000L) != 1) { return false; }
|
||||
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the user name and password
|
||||
// AT+CNCFG=<pdpidx>,<ip_type>,[<APN>,[<usename>,<password>,[<authentication>]]]
|
||||
// <pdpidx> PDP Context Identifier (1 is setup above)
|
||||
// <ip_type> 0: Dual PDN Stack
|
||||
// 1: Internet Protocol Version 4
|
||||
// 2: Internet Protocol Version 6
|
||||
// <authentication> 0: NONE
|
||||
// 1: PAP
|
||||
// 2: CHAP
|
||||
// 3: PAP or CHAP
|
||||
if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) {
|
||||
sendAT(GF("+CNCFG=1,1,\""), apn, "\",\"", "\",\"", user, pwd, '"');
|
||||
waitResponse();
|
||||
} else if (user && strlen(user) > 0) {
|
||||
// Set the user name only
|
||||
sendAT(GF("+CNCFG=1,1,\""), apn, "\",\"", user, '"');
|
||||
waitResponse();
|
||||
} else {
|
||||
// Set the APN only
|
||||
sendAT(GF("+CNCFG=1,1,\""), apn, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
// Activate application network connection
|
||||
// This is for most other supported applications outside of the
|
||||
// TCP application toolkit (ie, SSL)
|
||||
// AT+CNACT=<pdpidx>,<action>
|
||||
// <pdpidx> PDP Context Identifier (1 is setup above)
|
||||
// <action> 0: Deactive
|
||||
// 1: Active
|
||||
// 2: Auto Active
|
||||
int res = 0;
|
||||
int ntries = 0;
|
||||
while (res != 1 && ntries < 5) {
|
||||
sendAT(GF("+CNACT=1,1,\""), apn, GF("\""));
|
||||
res = waitResponse(60000L, GF(GSM_NL "+APP PDP: ACTIVE"),
|
||||
GF(GSM_NL "+APP PDP: DEACTIVE"));
|
||||
waitResponse();
|
||||
ntries++;
|
||||
}
|
||||
|
||||
// return res == 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnectImpl() {
|
||||
// Shut down the general application TCP/IP connection
|
||||
// CNACT will close *all* open application connections
|
||||
sendAT(GF("+CNACT=1,0"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
protected:
|
||||
// Follows all messaging functions per template
|
||||
|
||||
/*
|
||||
* GPS/GNSS/GLONASS location functions
|
||||
*/
|
||||
protected:
|
||||
// Follows the SIM70xx template
|
||||
|
||||
/*
|
||||
* Time functions
|
||||
*/
|
||||
// Can follow CCLK as per template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
protected:
|
||||
// Follows all battery functions per template
|
||||
|
||||
/*
|
||||
* Client related functions
|
||||
*/
|
||||
protected:
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
|
||||
bool ssl = false, int timeout_s = 75) {
|
||||
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
|
||||
|
||||
// set the connection (mux) identifier to use
|
||||
sendAT(GF("+CACID="), mux);
|
||||
if (waitResponse(timeout_ms) != 1) return false;
|
||||
|
||||
|
||||
if (ssl) {
|
||||
// set the ssl version
|
||||
// AT+CSSLCFG="SSLVERSION",<ctxindex>,<sslversion>
|
||||
// <ctxindex> PDP context identifier
|
||||
// <sslversion> 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN
|
||||
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
|
||||
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
|
||||
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
|
||||
// 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0
|
||||
// 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2
|
||||
sendAT(GF("+CSSLCFG=\"SSLVERSION\",0,3")); // TLS 1.2
|
||||
if (waitResponse(5000L) != 1) return false;
|
||||
|
||||
// set the PDP context to apply SSL to
|
||||
// AT+CSSLCFG="CTXINDEX",<ctxindex>
|
||||
// <ctxindex> PDP context identifier
|
||||
sendAT(GF("+CSSLCFG=\"CTXINDEX\",0"));
|
||||
if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false;
|
||||
streamSkipUntil('\n'); // read out the certificate information
|
||||
waitResponse();
|
||||
|
||||
if (certificates[mux] != "") {
|
||||
// apply the correct certificate to the connection
|
||||
// AT+CASSLCFG=<cid>,"CACERT",<caname>
|
||||
// <cid> Application connection ID (set with AT+CACID above)
|
||||
// <certname> certificate name
|
||||
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
|
||||
"\"");
|
||||
if (waitResponse(5000L) != 1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// enable or disable ssl
|
||||
// AT+CASSLCFG=<cid>,"SSL",<sslFlag>
|
||||
// <cid> Application connection ID (set with AT+CACID above)
|
||||
// <sslFlag> 0: Not support SSL
|
||||
// 1: Support SSL
|
||||
sendAT(GF("+CASSLCFG="), mux, ',', GF("SSL,"), ssl);
|
||||
waitResponse();
|
||||
|
||||
// set the SSL SNI (server name indication)
|
||||
sendAT(GF("+CSSLCFG=\"SNI\","), mux, ',', GF("\""), host, GF("\""));
|
||||
waitResponse();
|
||||
|
||||
// actually open the connection
|
||||
// AT+CAOPEN=<cid>,<pdp_index>,<conn_type>,<server>,<port>[,<recv_mode>]
|
||||
// <cid> TCP/UDP identifier
|
||||
// <pdp_index> Index of PDP connection; we set up PCP context 1 above
|
||||
// <conn_type> "TCP" or "UDP"
|
||||
// <recv_mode> 0: The received data can only be read manually using
|
||||
// AT+CARECV=<cid>
|
||||
// 1: After receiving the data, it will automatically report
|
||||
// URC:
|
||||
// +CAURC:
|
||||
// "recv",<id>,<length>,<remoteIP>,<remote_port><CR><LF><data>
|
||||
sendAT(GF("+CAOPEN="), mux, GF(",0,\"TCP\",\""), host, GF("\","), port, ',',
|
||||
0);
|
||||
if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; }
|
||||
// returns OK/r/n/r/n+CAOPEN: <cid>,<result>
|
||||
// <result> 0: Success
|
||||
// 1: Socket error
|
||||
// 2: No memory
|
||||
// 3: Connection limit
|
||||
// 4: Parameter invalid
|
||||
// 6: Invalid IP address
|
||||
// 7: Not support the function
|
||||
// 12: Can’t bind the port
|
||||
// 13: Can’t listen the port
|
||||
// 20: Can’t resolve the host
|
||||
// 21: Network not active
|
||||
// 23: Remote refuse
|
||||
// 24: Certificate’s time expired
|
||||
// 25: Certificate’s common name does not match
|
||||
// 26: Certificate’s common name does not match and time expired
|
||||
// 27: Connect failed
|
||||
streamSkipUntil(','); // Skip mux
|
||||
|
||||
// make sure the connection really opened
|
||||
int8_t res = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
|
||||
return 0 == res;
|
||||
}
|
||||
|
||||
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
|
||||
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
|
||||
if (waitResponse(GF(">")) != 1) { return 0; }
|
||||
|
||||
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
|
||||
stream.flush();
|
||||
|
||||
if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; }
|
||||
streamSkipUntil(','); // Skip mux
|
||||
if (streamGetIntBefore(',') != 0) { return 0; } // If result != success
|
||||
return streamGetIntBefore('\n');
|
||||
}
|
||||
|
||||
size_t modemRead(size_t size, uint8_t mux) {
|
||||
if (!sockets[mux]) return 0;
|
||||
|
||||
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
|
||||
|
||||
if (waitResponse(GF("+CARECV:")) != 1) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream.read();
|
||||
if (stream.peek() == '0') {
|
||||
waitResponse();
|
||||
sockets[mux]->sock_available = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int16_t len_confirmed = streamGetIntBefore(',');
|
||||
if (len_confirmed <= 0) {
|
||||
sockets[mux]->sock_available = 0;
|
||||
waitResponse();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len_confirmed; i++) {
|
||||
uint32_t startMillis = millis();
|
||||
while (!stream.available() &&
|
||||
(millis() - startMillis < sockets[mux]->_timeout)) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
char c = stream.read();
|
||||
sockets[mux]->rx.put(c);
|
||||
}
|
||||
// DBG("### READ:", len_requested, "from", mux);
|
||||
// sockets[mux]->sock_available = modemGetAvailable(mux);
|
||||
auto diff = int64_t(size) - int64_t(len_confirmed);
|
||||
if (diff < 0) diff = 0;
|
||||
sockets[mux]->sock_available = diff;
|
||||
waitResponse();
|
||||
return len_confirmed;
|
||||
}
|
||||
|
||||
size_t modemGetAvailable(uint8_t mux) {
|
||||
// NOTE: This gets how many characters are available on all connections
|
||||
sendAT(GF("+CARECV?"));
|
||||
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
|
||||
if (waitResponse(3000, GF(GSM_NL "+CARECV: ")) != 1) { break; }
|
||||
size_t result = 0;
|
||||
// if (streamGetIntBefore(',') != muxNo) { // check the mux no
|
||||
// DBG("### Warning: misaligned mux numbers!");
|
||||
// }
|
||||
streamSkipUntil(','); // skip mux [use muxNo]
|
||||
result = streamGetIntBefore('\n');
|
||||
GsmClientSim70x0* sock = sockets[mux];
|
||||
if (sock && muxNo == mux) { sock->sock_available = result; }
|
||||
}
|
||||
waitResponse(); // Should be an OK at the end
|
||||
modemGetConnected(mux);
|
||||
if (!sockets[mux]) return 0;
|
||||
return sockets[mux]->sock_available;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux) {
|
||||
// NOTE: This gets the state of all connections
|
||||
sendAT(GF("+CASTATE?"));
|
||||
|
||||
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
|
||||
if (waitResponse(3000, GF(GSM_NL "+CASTATE: ")) != 1) { break; }
|
||||
uint8_t status = 0;
|
||||
// if (streamGetIntBefore(',') != muxNo) { // check the mux no
|
||||
// DBG("### Warning: misaligned mux numbers!");
|
||||
// }
|
||||
streamSkipUntil(','); // skip mux [use muxNo]
|
||||
status = stream.parseInt(); // Read the status
|
||||
// 0: Closed by remote server or internal error
|
||||
// 1: Connected to remote server
|
||||
// 2: Listening (server mode)
|
||||
GsmClientSim70x0* sock = sockets[mux];
|
||||
if (sock && muxNo == mux) { sock->sock_connected = (status == 1); }
|
||||
}
|
||||
waitResponse(); // Should be an OK at the end
|
||||
return sockets[mux]->sock_connected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
public:
|
||||
// TODO(vshymanskyy): Optimize this!
|
||||
int8_t waitResponse(uint32_t timeout_ms, String& data,
|
||||
GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
uint8_t index = 0;
|
||||
uint32_t startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
TINY_GSM_YIELD();
|
||||
int8_t a = stream.read();
|
||||
if (a <= 0) continue; // Skip 0x00 bytes, just in case
|
||||
data += static_cast<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)) {
|
||||
#if defined TINY_GSM_DEBUG
|
||||
if (r3 == GFP(GSM_CME_ERROR)) {
|
||||
streamSkipUntil('\n'); // Read out the error
|
||||
}
|
||||
#endif
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF(GSM_NL "+CARECV:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
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 "+CADATAIND:"))) {
|
||||
int8_t mux = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->got_data = true;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Got Data:", mux);
|
||||
} else if (data.endsWith(GF(GSM_NL "+CASTATE:"))) {
|
||||
int8_t mux = streamGetIntBefore(',');
|
||||
int8_t state = streamGetIntBefore('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
if (state != 1) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF("CLOSED" GSM_NL))) {
|
||||
int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8);
|
||||
int8_t coma = data.indexOf(',', nl + 2);
|
||||
int8_t mux = data.substring(nl + 2, coma).toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
} else if (data.endsWith(GF("*PSNWID:"))) {
|
||||
streamSkipUntil('\n'); // Refresh network name by network
|
||||
data = "";
|
||||
DBG("### Network name updated.");
|
||||
} else if (data.endsWith(GF("*PSUTTZ:"))) {
|
||||
streamSkipUntil('\n'); // Refresh time and time zone by network
|
||||
data = "";
|
||||
DBG("### Network time and time zone updated.");
|
||||
} else if (data.endsWith(GF("+CTZV:"))) {
|
||||
streamSkipUntil('\n'); // Refresh network time zone by network
|
||||
data = "";
|
||||
DBG("### Network time zone updated.");
|
||||
} else if (data.endsWith(GF("DST: "))) {
|
||||
streamSkipUntil(
|
||||
'\n'); // Refresh Network Daylight Saving Time by network
|
||||
data = "";
|
||||
DBG("### Daylight savings time state updated.");
|
||||
} else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) {
|
||||
data = "";
|
||||
DBG("### Unexpected module reset!");
|
||||
init();
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
String data;
|
||||
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
|
||||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||||
#if defined TINY_GSM_DEBUG
|
||||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||||
#else
|
||||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||||
#endif
|
||||
GsmConstStr r5 = NULL) {
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
protected:
|
||||
GsmClientSim70x0* sockets[TINY_GSM_MUX_COUNT];
|
||||
String certificates[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif // SRC_TINYGSMCLIENTSIM70X0_H_
|
456
src/TinyGsmClientSIM70xx.h
Normal file
456
src/TinyGsmClientSIM70xx.h
Normal file
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* @file TinyGsmClientSIM70xx.h
|
||||
* @author Volodymyr Shymanskyy
|
||||
* @license LGPL-3.0
|
||||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* @date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef SRC_TINYGSMCLIENTSIM70XX_H_
|
||||
#define SRC_TINYGSMCLIENTSIM70XX_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"
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
template <class modemType>
|
||||
class TinyGsmSim70xx : public TinyGsmModem<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmGPRS<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmSMS<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmGPS<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmTime<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmNTP<TinyGsmSim70xx<modemType>>,
|
||||
public TinyGsmBattery<TinyGsmSim70xx<modemType>> {
|
||||
friend class TinyGsmModem<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmGPRS<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmSMS<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmGPS<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmTime<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmNTP<TinyGsmSim70xx<modemType>>;
|
||||
friend class TinyGsmBattery<TinyGsmSim70xx<modemType>>;
|
||||
|
||||
/*
|
||||
* CRTP Helper
|
||||
*/
|
||||
protected:
|
||||
inline const modemType& thisModem() const {
|
||||
return static_cast<const modemType&>(*this);
|
||||
}
|
||||
inline modemType& thisModem() {
|
||||
return static_cast<modemType&>(*this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public:
|
||||
explicit TinyGsmSim70xx(Stream& stream) : stream(stream) {}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
protected:
|
||||
bool initImpl(const char* pin = NULL) {
|
||||
return thisModem().initImpl(pin);
|
||||
}
|
||||
|
||||
String getModemNameImpl() {
|
||||
String name = "SIMCom SIM7000";
|
||||
|
||||
thisModem().sendAT(GF("+GMM"));
|
||||
String res2;
|
||||
if (thisModem().waitResponse(5000L, res2) != 1) { return name; }
|
||||
res2.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res2.replace("_", " ");
|
||||
res2.trim();
|
||||
|
||||
name = res2;
|
||||
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("E0")); // Echo Off
|
||||
thisModem().waitResponse();
|
||||
if (!thisModem().setPhoneFunctionality(0)) { return false; }
|
||||
if (!thisModem().setPhoneFunctionality(1, true)) { return false; }
|
||||
thisModem().waitResponse(30000L, GF("SMS Ready"));
|
||||
return thisModem().initImpl(pin);
|
||||
}
|
||||
|
||||
bool powerOffImpl() {
|
||||
thisModem().sendAT(GF("+CPOWD=1"));
|
||||
return thisModem().waitResponse(GF("NORMAL POWER DOWN")) == 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() {
|
||||
// Get the help string, not the setting value
|
||||
thisModem().sendAT(GF("+CNMP=?"));
|
||||
if (thisModem().waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
thisModem().waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
int16_t getNetworkMode() {
|
||||
thisModem().sendAT(GF("+CNMP?"));
|
||||
if (thisModem().waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; }
|
||||
int16_t mode = thisModem().streamGetIntBefore('\n');
|
||||
thisModem().waitResponse();
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool setNetworkMode(uint8_t mode) {
|
||||
// 2 Automatic
|
||||
// 13 GSM only
|
||||
// 38 LTE only
|
||||
// 51 GSM and LTE only
|
||||
thisModem().sendAT(GF("+CNMP="), mode);
|
||||
return thisModem().waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getPreferredModes() {
|
||||
// Get the help string, not the setting value
|
||||
thisModem().sendAT(GF("+CMNB=?"));
|
||||
if (thisModem().waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return ""; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
thisModem().waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
int16_t getPreferredMode() {
|
||||
thisModem().sendAT(GF("+CMNB?"));
|
||||
if (thisModem().waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return false; }
|
||||
int16_t mode = thisModem().streamGetIntBefore('\n');
|
||||
thisModem().waitResponse();
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool setPreferredMode(uint8_t mode) {
|
||||
// 1 CAT-M
|
||||
// 2 NB-IoT
|
||||
// 3 CAT-M and NB-IoT
|
||||
thisModem().sendAT(GF("+CMNB="), mode);
|
||||
return thisModem().waitResponse() == 1;
|
||||
}
|
||||
|
||||
bool getNetworkSystemMode(bool& n, int16_t& stat) {
|
||||
// n: whether to automatically report the system mode info
|
||||
// stat: the current service. 0 if it not connected
|
||||
thisModem().sendAT(GF("+CNSMOD?"));
|
||||
if (thisModem().waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { return false; }
|
||||
n = thisModem().streamGetIntBefore(',') != 0;
|
||||
stat = thisModem().streamGetIntBefore('\n');
|
||||
thisModem().waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setNetworkSystemMode(bool n) {
|
||||
// n: whether to automatically report the system mode info
|
||||
thisModem().sendAT(GF("+CNSMOD="), int8_t(n));
|
||||
return thisModem().waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getLocalIPImpl() {
|
||||
return thisModem().getLocalIPImpl();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 "+CCID" before the number
|
||||
String getSimCCIDImpl() {
|
||||
thisModem().sendAT(GF("+CCID"));
|
||||
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() {
|
||||
thisModem().sendAT(GF("+CGNSPWR=1"));
|
||||
if (thisModem().waitResponse() != 1) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool disableGPSImpl() {
|
||||
thisModem().sendAT(GF("+CGNSPWR=0"));
|
||||
if (thisModem().waitResponse() != 1) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the RAW GPS output
|
||||
String getGPSrawImpl() {
|
||||
thisModem().sendAT(GF("+CGNSINF"));
|
||||
if (thisModem().waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
thisModem().waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
// get GPS informations
|
||||
bool getGPSImpl(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("+CGNSINF"));
|
||||
if (thisModem().waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
thisModem().streamSkipUntil(','); // GNSS run status
|
||||
if (thisModem().streamGetIntBefore(',') == 1) { // fix status
|
||||
// init variables
|
||||
float ilat = 0;
|
||||
float ilon = 0;
|
||||
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;
|
||||
|
||||
// UTC date & Time
|
||||
iyear = thisModem().streamGetIntLength(4); // Four digit year
|
||||
imonth = thisModem().streamGetIntLength(2); // Two digit month
|
||||
iday = thisModem().streamGetIntLength(2); // Two digit day
|
||||
ihour = thisModem().streamGetIntLength(2); // Two digit hour
|
||||
imin = thisModem().streamGetIntLength(2); // Two digit minute
|
||||
secondWithSS = thisModem().streamGetFloatBefore(
|
||||
','); // 6 digit second with subseconds
|
||||
|
||||
ilat = thisModem().streamGetFloatBefore(','); // Latitude
|
||||
ilon = thisModem().streamGetFloatBefore(','); // Longitude
|
||||
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(','); // Fix Mode
|
||||
thisModem().streamSkipUntil(','); // Reserved1
|
||||
iaccuracy = thisModem().streamGetFloatBefore(
|
||||
','); // Horizontal Dilution Of Precision
|
||||
thisModem().streamSkipUntil(','); // Position Dilution Of Precision
|
||||
thisModem().streamSkipUntil(','); // Vertical Dilution Of Precision
|
||||
thisModem().streamSkipUntil(','); // Reserved2
|
||||
ivsat = thisModem().streamGetIntBefore(','); // GNSS Satellites in View
|
||||
iusat = thisModem().streamGetIntBefore(','); // GNSS Satellites Used
|
||||
thisModem().streamSkipUntil(','); // GLONASS Satellites Used
|
||||
thisModem().streamSkipUntil(','); // Reserved3
|
||||
thisModem().streamSkipUntil(','); // C/N0 max
|
||||
thisModem().streamSkipUntil(','); // HPA
|
||||
thisModem().streamSkipUntil('\n'); // VPA
|
||||
|
||||
// Set pointers
|
||||
if (lat != NULL) *lat = ilat;
|
||||
if (lon != NULL) *lon = ilon;
|
||||
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<int>(secondWithSS);
|
||||
|
||||
thisModem().waitResponse();
|
||||
return true;
|
||||
}
|
||||
|
||||
thisModem().streamSkipUntil('\n'); // toss the row of commas
|
||||
thisModem().waitResponse();
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Time functions
|
||||
*/
|
||||
// Can follow CCLK as per template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
protected:
|
||||
// Follows all battery functions per template
|
||||
|
||||
/*
|
||||
* Client related functions
|
||||
*/
|
||||
// should implement in sub-classes
|
||||
|
||||
/*
|
||||
* 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_
|
@@ -24,6 +24,8 @@
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmTemperature.tpp"
|
||||
#include "TinyGsmTime.tpp"
|
||||
#include "TinyGsmNTP.tpp"
|
||||
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
@@ -50,6 +52,7 @@ class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
|
||||
public TinyGsmGSMLocation<TinyGsmSim7600>,
|
||||
public TinyGsmGPS<TinyGsmSim7600>,
|
||||
public TinyGsmTime<TinyGsmSim7600>,
|
||||
public TinyGsmNTP<TinyGsmSim7600>,
|
||||
public TinyGsmBattery<TinyGsmSim7600>,
|
||||
public TinyGsmTemperature<TinyGsmSim7600> {
|
||||
friend class TinyGsmModem<TinyGsmSim7600>;
|
||||
@@ -59,6 +62,7 @@ class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
|
||||
friend class TinyGsmGPS<TinyGsmSim7600>;
|
||||
friend class TinyGsmGSMLocation<TinyGsmSim7600>;
|
||||
friend class TinyGsmTime<TinyGsmSim7600>;
|
||||
friend class TinyGsmNTP<TinyGsmSim7600>;
|
||||
friend class TinyGsmBattery<TinyGsmSim7600>;
|
||||
friend class TinyGsmTemperature<TinyGsmSim7600>;
|
||||
|
||||
@@ -271,12 +275,17 @@ class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
|
||||
return res;
|
||||
}
|
||||
|
||||
String setNetworkMode(uint8_t mode) {
|
||||
sendAT(GF("+CNMP="), mode);
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; }
|
||||
String res = stream.readStringUntil('\n');
|
||||
int16_t getNetworkMode() {
|
||||
sendAT(GF("+CNMP?"));
|
||||
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; }
|
||||
int16_t mode = streamGetIntBefore('\n');
|
||||
waitResponse();
|
||||
return res;
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool setNetworkMode(uint8_t mode) {
|
||||
sendAT(GF("+CNMP="), mode);
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getLocalIPImpl() {
|
||||
@@ -558,6 +567,11 @@ class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
|
||||
protected:
|
||||
// Can follow the standard CCLK function in the template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "TinyGsmSSL.tpp"
|
||||
#include "TinyGsmTCP.tpp"
|
||||
#include "TinyGsmTime.tpp"
|
||||
#include "TinyGsmNTP.tpp"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
@@ -51,6 +52,7 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
public TinyGsmSMS<TinyGsmSim800>,
|
||||
public TinyGsmGSMLocation<TinyGsmSim800>,
|
||||
public TinyGsmTime<TinyGsmSim800>,
|
||||
public TinyGsmNTP<TinyGsmSim800>,
|
||||
public TinyGsmBattery<TinyGsmSim800> {
|
||||
friend class TinyGsmModem<TinyGsmSim800>;
|
||||
friend class TinyGsmGPRS<TinyGsmSim800>;
|
||||
@@ -60,6 +62,7 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
friend class TinyGsmSMS<TinyGsmSim800>;
|
||||
friend class TinyGsmGSMLocation<TinyGsmSim800>;
|
||||
friend class TinyGsmTime<TinyGsmSim800>;
|
||||
friend class TinyGsmNTP<TinyGsmSim800>;
|
||||
friend class TinyGsmBattery<TinyGsmSim800>;
|
||||
|
||||
/*
|
||||
@@ -315,20 +318,23 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
const char* pwd = NULL) {
|
||||
gprsDisconnect();
|
||||
|
||||
// Set the Bearer for the IP
|
||||
sendAT(GF(
|
||||
"+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS
|
||||
// Bearer settings for applications based on IP
|
||||
// Set the connection type to GPRS
|
||||
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); // Set the APN
|
||||
// Set the APN
|
||||
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
// Set the user name
|
||||
if (user && strlen(user) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"'); // Set the user name
|
||||
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
|
||||
waitResponse();
|
||||
}
|
||||
// Set the password
|
||||
if (pwd && strlen(pwd) > 0) {
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"'); // Set the password
|
||||
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
@@ -351,8 +357,6 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
sendAT(GF("+CGATT=1"));
|
||||
if (waitResponse(60000L) != 1) { return false; }
|
||||
|
||||
// TODO(?): wait AT+CGATT?
|
||||
|
||||
// Set to multi-IP
|
||||
sendAT(GF("+CIPMUX=1"));
|
||||
if (waitResponse() != 1) { return false; }
|
||||
@@ -447,6 +451,11 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
protected:
|
||||
// Can follow the standard CCLK function in the template
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
@@ -456,50 +465,7 @@ class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
public:
|
||||
boolean isValidNumber(String str) {
|
||||
if (!(str.charAt(0) == '+' || str.charAt(0) == '-' ||
|
||||
isDigit(str.charAt(0))))
|
||||
return false;
|
||||
|
||||
for (byte i = 1; i < str.length(); i++) {
|
||||
if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String ShowNTPError(byte error) {
|
||||
switch (error) {
|
||||
case 1: return "Network time synchronization is successful";
|
||||
case 61: return "Network error";
|
||||
case 62: return "DNS resolution error";
|
||||
case 63: return "Connection error";
|
||||
case 64: return "Service response error";
|
||||
case 65: return "Service response timeout";
|
||||
default: return "Unknown error: " + String(error);
|
||||
}
|
||||
}
|
||||
|
||||
byte NTPServerSync(String server = "pool.ntp.org", byte TimeZone = 3) {
|
||||
// Set GPRS bearer profile to associate with NTP sync
|
||||
sendAT(GF("+CNTPCID=1"));
|
||||
if (waitResponse(10000L) != 1) { return -1; }
|
||||
|
||||
// Set NTP server and timezone
|
||||
sendAT(GF("+CNTP="), server, ',', String(TimeZone));
|
||||
if (waitResponse(10000L) != 1) { return -1; }
|
||||
|
||||
// Request network synchronization
|
||||
sendAT(GF("+CNTP"));
|
||||
if (waitResponse(10000L, GF(GSM_NL "+CNTP:"))) {
|
||||
String result = stream.readStringUntil('\n');
|
||||
result.trim();
|
||||
if (isValidNumber(result)) { return result.toInt(); }
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// Can sync with server using CNTP as per template
|
||||
|
||||
/*
|
||||
* Client related functions
|
||||
|
92
src/TinyGsmNTP.tpp
Normal file
92
src/TinyGsmNTP.tpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @file TinyGsmNTP.tpp
|
||||
* @author Volodymyr Shymanskyy
|
||||
* @license LGPL-3.0
|
||||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* @date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef SRC_TINYGSMNTP_H_
|
||||
#define SRC_TINYGSMNTP_H_
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define TINY_GSM_MODEM_HAS_NTP
|
||||
|
||||
template <class modemType>
|
||||
class TinyGsmNTP {
|
||||
public:
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
|
||||
public:
|
||||
boolean TinyGsmIsValidNumber(String str) {
|
||||
if (!(str.charAt(0) == '+' || str.charAt(0) == '-' ||
|
||||
isDigit(str.charAt(0))))
|
||||
return false;
|
||||
|
||||
for (byte i = 1; i < str.length(); i++) {
|
||||
if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
byte NTPServerSync(String server = "pool.ntp.org", byte TimeZone = 3) {
|
||||
return thisModem().NTPServerSyncImpl(server, TimeZone);
|
||||
}
|
||||
String ShowNTPError(byte error) {
|
||||
return thisModem().ShowNTPErrorImpl(error);
|
||||
}
|
||||
|
||||
/*
|
||||
* CRTP Helper
|
||||
*/
|
||||
protected:
|
||||
inline const modemType& thisModem() const {
|
||||
return static_cast<const modemType&>(*this);
|
||||
}
|
||||
inline modemType& thisModem() {
|
||||
return static_cast<modemType&>(*this);
|
||||
}
|
||||
|
||||
/*
|
||||
* NTP server functions
|
||||
*/
|
||||
protected:
|
||||
byte NTPServerSyncImpl(String server = "pool.ntp.org", byte TimeZone = 3) {
|
||||
// Set GPRS bearer profile to associate with NTP sync
|
||||
// this may fail, it's not supported by all modules
|
||||
thisModem().sendAT(GF("+CNTPCID=1"));
|
||||
thisModem().waitResponse(10000L);
|
||||
|
||||
// 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:"))) {
|
||||
String result = thisModem().stream.readStringUntil('\n');
|
||||
result.trim();
|
||||
if (TinyGsmIsValidNumber(result)) { return result.toInt(); }
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
String ShowNTPErrorImpl(byte error) {
|
||||
switch (error) {
|
||||
case 1: return "Network time synchronization is successful";
|
||||
case 61: return "Network error";
|
||||
case 62: return "DNS resolution error";
|
||||
case 63: return "Connection error";
|
||||
case 64: return "Service response error";
|
||||
case 65: return "Service response timeout";
|
||||
default: return "Unknown error: " + String(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SRC_TINYGSMNTP_H_
|
Reference in New Issue
Block a user