Creating a super-class for modems

This commit is contained in:
Sara Damiano
2018-09-13 18:13:18 -04:00
46 changed files with 2218 additions and 429 deletions

View File

@@ -35,29 +35,29 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope
## Features
Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | M590 | ESP8266 | XBee
--- | --- | --- | --- | --- | --- | ---
Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | M590 | ESP8266 | XBee | Quectel M95 |
--- | --- | --- | --- | --- | --- | --- | ----------- |
**Data connections**
TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔
UDP | ◌ | ◌ | | | | ◌
SSL/TLS (HTTPS) | ✔¹ | ✔ | 🅧 | 🅧 | ✔¹ | ✔¹
TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
UDP | ◌ | ◌ | | | | ◌ | ◌ |
SSL/TLS (HTTPS) | ✔¹ | ✔ | 🅧 | 🅧 | ✔¹ | ✔¹ | |
**USSD**
Sending USSD requests | ✔ | | ✔ | ✔ | 🅧 |
Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | 🅧 |
Sending USSD requests | ✔ | | ✔ | ✔ | 🅧 | | |
Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | 🅧 | | |
**SMS**
Sending | ✔ | ✔ | ✔ | ✔ | 🅧 | ✔
Sending Unicode | ✔ | | ◌ | 🅧 | 🅧 |
Reading | | | | | 🅧 |
Incoming message event | | | | ? | 🅧 |
Sending | ✔ | ✔ | ✔ | ✔ | 🅧 | ✔ | ✔ |
Sending Unicode | ✔ | | ◌ | 🅧 | 🅧 | | ? |
Reading | | | | | 🅧 | | ? |
Incoming message event | | | | ? | 🅧 | | |
**Calls**
Dial, hangup | ✔ | | ✔ | 🅧 | 🅧 | 🅧
Receiving calls | ✔ | | ✔ | 🅧 | 🅧 | 🅧
Incoming event (RING) | ◌ | | ◌ | 🅧 | 🅧 | 🅧
DTMF sending | ✔ | | ✔ | 🅧 | 🅧 | 🅧
DTMF decoding | ◌ | | 🅧 | 🅧 | 🅧 | 🅧
Dial, hangup | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? |
Receiving calls | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? |
Incoming event (RING) | ◌ | | ◌ | 🅧 | 🅧 | 🅧 | ? |
DTMF sending | ✔ | | ✔ | 🅧 | 🅧 | 🅧 | ? |
DTMF decoding | ◌ | | 🅧 | 🅧 | 🅧 | 🅧 | ? |
**Location**
GSM location service | ✔ | ✔ | 🅧 | 🅧 | 🅧 | ✔
GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | 🅧 | 🅧
GSM location service | ✔ | ✔ | 🅧 | 🅧 | 🅧 | ✔ | 🅧 |
GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 | 🅧 | 🅧 | 🅧 |
✔ - implemented◌ - planned🅧 - not available on this modem
¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.)
@@ -72,6 +72,8 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 |
- Neoway M590
- u-blox Cellular Modems (LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx)
- Quectel BG96 ***(alpha)***
- Quectel M95 ***(alpha)***
- Quectel MC60 ***(alpha)***
### Supported boards/modules
- Arduino MKR GSM 1400
@@ -84,8 +86,8 @@ GPS/GNSS | ✔¹ | 🅧 | ◌¹ | 🅧 |
- ... other modules, based on supported modems. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration).
More modems may be supported later:
- [ ] Quectel M10, M35, M95, UG95, EC21
- [ ] Sequans Monarch LTE Cat M1/NB1
- [ ] Quectel M10, UG95
- [ ] SIMCom SIM5320, SIM5360, SIM5216, SIM7xxx
- [ ] Telit GL865
- [ ] ZTE MG2639

View File

@@ -18,6 +18,8 @@
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon Serial
@@ -34,6 +36,22 @@
//#define DUMP_AT_COMMANDS
#define TINY_GSM_DEBUG SerialMon
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 38400
/*
* Test enabled
*/
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_CALL true
#define TINY_GSM_USE_SMS true
#define TINY_GSM_USE_USSD true
// powerdown modem after tests
#define TINY_GSM_POWERDOWN false
// set GSM PIN, if any
#define GSM_PIN ""
// Set phone numbers, if you want to test SMS and Calls
//#define SMS_TARGET "+380xxxxxxxxx"
//#define CALL_TARGET "+380xxxxxxxxx"
@@ -64,7 +82,7 @@ void setup() {
delay(3000);
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT);
TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX);
}
void loop() {
@@ -73,6 +91,10 @@ void loop() {
// To skip it, call init() instead of restart()
DBG("Initializing modem...");
if (!modem.restart()) {
DBG("Failed to restart modem, delayin 10s and retring");
delay(3000);
// restart autobaud in case GSM just rebooted
TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX);
delay(10000);
return;
}
@@ -80,8 +102,10 @@ void loop() {
String modemInfo = modem.getModemInfo();
DBG("Modem:", modemInfo);
// Unlock your SIM card with a PIN
//modem.simUnlock("1234");
// Unlock your SIM card with a PIN if needed
if ( GSM_PIN && modem.getSimStatus() != 3 ) {
modem.simUnlock(GSM_PIN);
}
DBG("Waiting for network...");
if (!modem.waitForNetwork()) {
@@ -93,11 +117,13 @@ void loop() {
DBG("Network connected");
}
#if TINY_GSM_USE_GPRS
DBG("Connecting to", apn);
if (!modem.gprsConnect(apn, user, pass)) {
delay(10000);
return;
}
#endif
bool res = modem.isGprsConnected();
DBG("GPRS status:", res ? "connected" : "not connected");
@@ -113,6 +139,7 @@ void loop() {
IPAddress local = modem.localIP();
DBG("Local IP:", local);
#endif
int csq = modem.getSignalQuality();
DBG("Signal quality:", csq);
@@ -140,15 +167,16 @@ void loop() {
String ussd_phone_num = modem.sendUSSD("*161#");
DBG("Phone number (USSD):", ussd_phone_num);
#endif
#if defined(TINY_GSM_MODEM_SIM808)
#if defined(TINY_GSM_MODEM_HAS_GPS)
modem.enableGPS();
String gps_raw = modem.getGPSraw();
modem.disableGPS();
DBG("GPS raw data:", gps_raw);
#endif
#if defined(SMS_TARGET)
#if TINY_GSM_USE_SMS && defined(SMS_TARGET)
res = modem.sendSMS(SMS_TARGET, String("Hello from ") + imei);
DBG("SMS:", res ? "OK" : "fail");
@@ -157,7 +185,7 @@ void loop() {
DBG("UTF16 SMS:", res ? "OK" : "fail");
#endif
#if defined(CALL_TARGET)
#if TINY_GSM_USE_CALL && defined(CALL_TARGET)
DBG("Calling:", CALL_TARGET);
// This is NOT supported on M590
@@ -182,21 +210,24 @@ void loop() {
}
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
if (!modem.isGprsConnected()) {
DBG("GPRS disconnected");
} else {
DBG("GPRS disconnect: Failed.");
}
#endif
#if TINY_GSM_POWERDOWN
// Try to power-off (modem may decide to restart automatically)
// To turn off modem completely, please use Reset/Enable pins
modem.poweroff();
DBG("Poweroff.");
#endif
// Do nothing forevermore
while (true) {
modem.maintain();
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "TinyGSM",
"version": "0.3.5",
"version": "0.3.6",
"description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports GSM modules with AT command interface: SIM800, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900, SIM900A, SIM900D, SIM908, SIM968",
"keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968",
"authors":

View File

@@ -1,9 +1,9 @@
name=TinyGSM
version=0.3.5
version=0.3.6
author=Volodymyr Shymanskyy
maintainer=Volodymyr Shymanskyy
sentence=A small Arduino library for GPRS modules, that just works.
paragraph=Includes examples for Blynk, MQTT, File Download, and Web Client. Supports GSM modules with AT command interface: SIM800, SIM900, A6, A7, M590, ESP8266, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968
paragraph=Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM and wifi modules with AT command interfaces.
category=Communication
url=https://github.com/vshymanskyy/TinyGSM
architectures=*

View File

@@ -13,7 +13,7 @@
#define TINY_GSM_MODEM_HAS_SSL
#endif
#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7)
#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7) || defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E)
#define TINY_GSM_MODEM_HAS_GPS
#endif
@@ -38,6 +38,12 @@
typedef TinyGsmUBLOX::GsmClient TinyGsmClient;
typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_M95)
#define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientM95.h>
typedef TinyGsmM95 TinyGsm;
typedef TinyGsmM95::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_BG96)
#define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientBG96.h>
@@ -56,6 +62,11 @@
typedef TinyGsmM590 TinyGsm;
typedef TinyGsmM590::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E)
#include <TinyGsmClientMC60.h>
typedef TinyGsmMC60 TinyGsm;
typedef TinyGsmMC60::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_WIFI
#include <TinyGsmClientESP8266.h>

View File

@@ -39,7 +39,7 @@ enum RegStatus {
};
class TinyGsmA6
class TinyGsmA6 : public TinyGsmModem
{
public:
@@ -178,7 +178,7 @@ private:
public:
TinyGsmA6(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -186,11 +186,8 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
@@ -208,6 +205,15 @@ public:
return true;
}
String getModemName() {
#if defined(TINY_GSM_MODEM_A6)
return "AI-Thinker A6";
#elif defined(TINY_GSM_MODEM_A7)
return "AI-Thinker A7";
#endif
return "AI-Thinker A6";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -251,6 +257,14 @@ public:
return false;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -367,22 +381,6 @@ public:
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE;
bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GPRS functions
*/
@@ -441,6 +439,10 @@ public:
return (res == 1);
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIFSR"));
String res;
@@ -453,10 +455,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -606,6 +604,10 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux) {
@@ -650,30 +652,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

View File

@@ -40,7 +40,7 @@ enum RegStatus {
};
class TinyGsmBG96
class TinyGsmBG96 : public TinyGsmModem
{
public:
@@ -115,7 +115,7 @@ public:
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size() + sock_available;
@@ -125,7 +125,7 @@ public:
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
while (cnt < size && sock_connected) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -179,30 +179,30 @@ private:
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
public:
virtual int connect(const char *host, uint16_t port) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true);
return sock_connected;
}
};
// class GsmClientSecure : public GsmClient
// {
// public:
// GsmClientSecure() {}
//
// GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1)
// : GsmClient(modem, mux)
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// return sock_connected;
// }
// };
public:
TinyGsmBG96(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -210,11 +210,8 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
@@ -226,6 +223,10 @@ public:
return true;
}
String getModemName() {
return "Quectel BG96";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -280,6 +281,14 @@ public:
return false; // TODO: For now
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -401,44 +410,30 @@ public:
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE;
bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
//Configure the TCPIP Context
sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse() != 1) {
return false;
}
//Activate GPRS/CSD Context
sendAT(GF("+QIACT=1"));
if (waitResponse(150000L) != 1) {
return false;
}
//Attach to Packet Domain service - is this necessary?
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) {
return false;
}
return true;
}
@@ -463,12 +458,13 @@ public:
return localIP() != 0;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CGPADDR=1"));
if (waitResponse(10000L, GF(GSM_NL "+CGPADDR:")) != 1) {
return "";
}
streamSkipUntil(',');
sendAT(GF("+QILOCIP"));
stream.readStringUntil('\n');
String res = stream.readStringUntil('\n');
if (waitResponse() != 1) {
return "";
@@ -476,10 +472,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -535,6 +527,8 @@ public:
bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CSMP=17,167,0,8"));
waitResponse();
@@ -567,14 +561,42 @@ public:
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_IMPLEMENTED;
int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED;
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
uint16_t res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
int getBattPercent() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
int rsp;
// <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE",
// "<IP_address>/<domain_name>",<remote_port>,<local_port>,<access_mode>(0-2 0=buffer)
sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0"));
rsp = waitResponse();
@@ -660,30 +682,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

View File

@@ -39,7 +39,7 @@ enum RegStatus {
class TinyGsmESP8266
class TinyGsmESP8266 : public TinyGsmModem
{
public:
@@ -191,10 +191,11 @@ public:
}
};
public:
TinyGsmESP8266(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -202,11 +203,8 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
@@ -225,6 +223,10 @@ public:
return true;
}
String getModemName() {
return "ESP8266";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -266,6 +268,14 @@ public:
return true;
}
bool hasWifi() {
return true;
}
bool hasGPRS() {
return false;
}
/*
* Power functions
*/
@@ -362,6 +372,10 @@ public:
return retVal;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIPSTA_CUR??"));
int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
@@ -373,33 +387,9 @@ public:
return res2;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* GPRS functions
* Client related functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) TINY_GSM_ATTR_NOT_AVAILABLE;
bool gprsDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Messaging functions
*/
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
protected:
@@ -438,30 +428,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

View File

@@ -39,7 +39,7 @@ enum RegStatus {
};
class TinyGsmM590
class TinyGsmM590 : public TinyGsmModem
{
public:
@@ -175,7 +175,7 @@ private:
public:
TinyGsmM590(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -183,11 +183,8 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
@@ -204,6 +201,10 @@ public:
return true;
}
String getModemName() {
return "Neoway M590";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -255,6 +256,14 @@ public:
return false;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -375,22 +384,6 @@ public:
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE;
bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GPRS functions
*/
@@ -447,6 +440,10 @@ public:
return res == 1;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
@@ -459,10 +456,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -538,6 +531,10 @@ public:
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux) {
@@ -595,30 +592,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

843
src/TinyGsmClientM95.h Normal file
View File

@@ -0,0 +1,843 @@
/**
* @file TinyGsmClientM95.h
* @author Volodymyr Shymanskyy, Pacman Pereira, and Replicade Ltd.
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy, (c)2017 Replicade Ltd. <http://www.replicade.com>
* @date Nov 2016
*/
#ifndef TinyGsmClientM95_h
#define TinyGsmClientM95_h
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 6
#include <TinyGsmCommon.h>
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmM95 : public TinyGsmModem
{
public:
class GsmClient : public Client
{
friend class TinyGsmM95;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsmM95& modem, uint8_t mux = 1) {
init(&modem, mux);
}
bool init(TinyGsmM95* modem, uint8_t mux = 1) {
this->at = modem;
this->mux = mux;
sock_available = 0;
sock_connected = false;
got_data = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop() {
TINY_GSM_YIELD();
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size && sock_connected) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(rx.free(), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmM95* at;
uint8_t mux;
uint16_t sock_available;
bool sock_connected;
bool got_data;
RxFifo rx;
};
// class GsmClientSecure : public GsmClient
// {
// public:
// GsmClientSecure() {}
//
// GsmClientSecure(TinyGsmm95& modem, uint8_t mux = 1)
// : GsmClient(modem, mux)
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// return sock_connected;
// }
// };
public:
TinyGsmM95(Stream& stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
if (waitResponse() != 1) {
return false;
}
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2"));
waitResponse();
#endif
getSimStatus();
return true;
}
String getModemName() {
return "Quectel M95";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("+IPR=0")); // Auto-baud
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
bool hasSSL() {
return false; // TODO: For now
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
bool restart() {
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) == 3) {
return false;
}
sendAT(GF("+CFUN=1"));
if (waitResponse(10000L, GF("Call Ready"), GF("OK"), GF("FAIL")) == 3) {
return false;
}
return init();
}
bool poweroff() {
sendAT(GF("+QPOWD"));
return waitResponse(GF("POWERED DOWN")) == 1; // TODO
}
bool radioOff() {
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return true;
}
/*
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
void setHostFormat( bool useDottedQuad ) {
if ( useDottedQuad ) {
sendAT(GF("+QIDNSIP=0"));
} else {
sendAT(GF("+QIDNSIP=1"));
}
waitResponse();
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
// select foreground context 0 = VIRTUAL_UART_1
sendAT(GF("+QIFGCNT=0"));
if (waitResponse() != 1) {
return false;
}
//Select GPRS (=1) as the Bearer
sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse() != 1) {
return false;
}
//Start TCPIP Task and Set APN, User Name and Password
sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" );
if (waitResponse() != 1) {
return false;
}
//Activate GPRS/CSD Context
sendAT(GF("+QIACT"));
if (waitResponse(10000) != 1) {
return false;
}
//Enable multiple TCP/IP connections
sendAT(GF("+QIMUX=1"));
if (waitResponse() != 1) {
return false;
}
//Request an IP header for received data ("IPD(data length):")
sendAT(GF("+QIHEAD=1"));
if (waitResponse() != 1) {
return false;
}
//Set Method to Handle Received TCP/IP Data - Retrieve Data by Command
sendAT(GF("+QINDI=1"));
if (waitResponse() != 1) {
return false;
}
return true;
}
bool gprsDisconnect() {
sendAT(GF("+QIDEACT"));
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return localIP() != 0;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+QILOCIP"));
stream.readStringUntil('\n');
String res = stream.readStringUntil('\n');
res.trim();
return res;
}
/*
* Messaging functions
*/
String sendUSSD(const String& code) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CUSD=1,\""), code, GF("\""));
if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
return "";
}
stream.readStringUntil('"');
String hex = stream.readStringUntil('"');
stream.readStringUntil(',');
int dcs = stream.readStringUntil('\n').toInt();
if (waitResponse() != 1) {
return "";
}
if (dcs == 15) {
return TinyGsmDecodeHex8bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMS(const String& number, const String& text) {
sendAT(GF("+CMGF=1"));
waitResponse();
//Set GSM 7 bit default alphabet (3GPP TS 23.038)
sendAT(GF("+CSCS=\"GSM\""));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text);
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CSMP=17,167,0,8"));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
uint16_t* t = (uint16_t*)text;
for (size_t i=0; i<len; i++) {
uint8_t c = t[i] >> 8;
if (c < 0x10) { stream.print('0'); }
stream.print(c, HEX);
c = t[i] & 0xFF;
if (c < 0x10) { stream.print('0'); }
stream.print(c, HEX);
}
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
/** Delete all SMS */
bool deleteAllSMS() {
sendAT(GF("+QMGDA=6"));
if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) {
return true;
}
return false;
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
uint16_t res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
int getBattPercent() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
sendAT(GF("+QIOPEN="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
int rsp = waitResponse(75000L,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
if ( rsp != 1 ) {
return false;
}
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+QISEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
}
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(GF(GSM_NL "SEND OK")) != 1) {
return 0;
}
bool allAcknowledged = false;
// bool failed = false;
while ( !allAcknowledged ) {
sendAT( GF("+QISACK"));
if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) {
return -1;
} else {
streamSkipUntil(','); /** Skip total */
streamSkipUntil(','); /** Skip acknowledged data size */
if ( stream.readStringUntil('\n').toInt() == 0 ) {
allAcknowledged = true;
}
}
}
waitResponse(5000L);
// streamSkipUntil(','); // Skip mux
// return stream.readStringUntil('\n').toInt();
return 1;
}
size_t modemRead(size_t size, uint8_t mux) {
sendAT(GF("+QIRD="), mux, ',', size);
if (waitResponse(GF("+QIRD:")) != 1) {
return 0;
}
size_t len = stream.readStringUntil('\n').toInt();
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
}
waitResponse();
DBG("### READ:", mux, ",", len);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
sendAT(GF("+QIRD="), mux, GF(",0"));
size_t result = 0;
if (waitResponse(GF("+QIRD:")) == 1) {
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = stream.readStringUntil('\n').toInt();
DBG("### STILL:", mux, "has", result);
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
return result;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+QISTATE=1,"), mux);
//+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QISTATE:")))
return false;
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip socket type
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int res = stream.readStringUntil(',').toInt(); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
}
public:
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (data.endsWith(GF(GSM_NL "+QIRD:"))) {
streamSkipUntil(','); // Skip the context
streamSkipUntil(','); // Skip the role
int mux = stream.readStringUntil('\n').toInt();
DBG("### Got Data:", mux);
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
} else if (data.endsWith(GF("CLOSED" GSM_NL))) {
int nl = data.lastIndexOf(GSM_NL, data.length()-8);
int coma = data.indexOf(',', nl+2);
int 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);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
//DBG('<', index, '>');
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

874
src/TinyGsmClientMC60.h Normal file
View File

@@ -0,0 +1,874 @@
/**
* @file TinyGsmClientMC60.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*
* @MC60 support added by Tamas Dajka 2017.10.15 - with fixes by Sara Damiano
*
*/
#ifndef TinyGsmClientMC60_h
#define TinyGsmClientMC60_h
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 6
#include <TinyGsmCommon.h>
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
SIM_ANTITHEFT_LOCKED = 3,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmMC60 : public TinyGsmModem
{
public:
class GsmClient : public Client
{
friend class TinyGsmMC60;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsmMC60& modem, uint8_t mux = 1) {
init(&modem, mux);
}
bool init(TinyGsmMC60* modem, uint8_t mux = 1) {
this->at = modem;
this->mux = mux;
sock_available = 0;
sock_connected = false;
got_data = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop() {
TINY_GSM_YIELD();
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size && sock_connected) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(rx.free(), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmMC60* at;
uint8_t mux;
uint16_t sock_available;
bool sock_connected;
bool got_data;
RxFifo rx;
};
// class GsmClientSecure : public GsmClient
// {
// public:
// GsmClientSecure() {}
//
// GsmClientSecure(TinyGsmMC60& modem, uint8_t mux = 1)
// : GsmClient(modem, mux)
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// return sock_connected;
// }
// };
public:
TinyGsmMC60(Stream& stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
sendAT(GF("&FZ")); // Factory + Reset
waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
getSimStatus();
return true;
}
String getModemName() {
#if defined(TINY_GSM_MODEM_MC60)
return "Quectel MC60";
#elif defined(TINY_GSM_MODEM_MC60E)
return "Quectel MC60E";
#endif
return "Quectel MC60";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("+IPR=0")); // Auto-baud
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
/*
* under development
*/
// bool hasSSL() {
// sendAT(GF("+QIPSSL=?"));
// if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) {
// return false;
// }
// return waitResponse() == 1;
// }
bool hasSSL() {
return false; // TODO: For now
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
bool restart() {
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
sendAT(GF("+CFUN=1,1"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return init();
}
bool poweroff() {
sendAT(GF("+QPOWD=1"));
return waitResponse(GF("NORMAL POWER DOWN")) == 1;
}
bool radioOff() {
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return true;
}
/*
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"), GF("PH_SIM PIN"), GF("PH_SIM PUK"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 5:
case 6: return SIM_ANTITHEFT_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
// select foreground context 0 = VIRTUAL_UART_1
sendAT(GF("+QIFGCNT=0"));
if (waitResponse() != 1) {
return false;
}
//Select GPRS (=1) as the Bearer
sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse() != 1) {
return false;
}
//Define PDP context - is this necessary?
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Activate PDP context - is this necessary?
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
//Start TCPIP Task and Set APN, User Name and Password
sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" );
if (waitResponse() != 1) {
return false;
}
//Activate GPRS/CSD Context
sendAT(GF("+QIACT"));
if (waitResponse(60000L) != 1) {
return false;
}
//Enable multiple TCP/IP connections
sendAT(GF("+QIMUX=1"));
if (waitResponse() != 1) {
return false;
}
//Request an IP header for received data ("IPD(data length):")
sendAT(GF("+QIHEAD=1"));
if (waitResponse() != 1) {
return false;
}
//Set Method to Handle Received TCP/IP Data - Retrieve Data by Command
sendAT(GF("+QINDI=1"));
if (waitResponse() != 1) {
return false;
}
// Check that we have a local IP address
if (localIP() != 0) {
return true;
}
return false;
}
bool gprsDisconnect() {
sendAT(GF("+QIDEACT"));
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return localIP() != 0;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+QILOCIP"));
stream.readStringUntil('\n');
String res = stream.readStringUntil('\n');
res.trim();
return res;
}
/*
* Messaging functions
*/
String sendUSSD(const String& code) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CUSD=1,\""), code, GF("\""));
if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
return "";
}
stream.readStringUntil('"');
String hex = stream.readStringUntil('"');
stream.readStringUntil(',');
int dcs = stream.readStringUntil('\n').toInt();
if (waitResponse() != 1) {
return "";
}
if (dcs == 15) {
return TinyGsmDecodeHex8bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMS(const String& number, const String& text) {
sendAT(GF("+CMGF=1"));
waitResponse();
//Set GSM 7 bit default alphabet (3GPP TS 23.038)
sendAT(GF("+CSCS=\"GSM\""));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text);
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CSMP=17,167,0,8"));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
uint16_t* t = (uint16_t*)text;
for (size_t i=0; i<len; i++) {
uint8_t c = t[i] >> 8;
if (c < 0x10) { stream.print('0'); }
stream.print(c, HEX);
c = t[i] & 0xFF;
if (c < 0x10) { stream.print('0'); }
stream.print(c, HEX);
}
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
/** Delete all SMS */
bool deleteAllSMS() {
sendAT(GF("+QMGDA=6"));
if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) {
return true;
}
return false;
}
/*
* Location functions
*/
String getGsmLocation() {
sendAT(GF("+CIPGSMLOC=1,1"));
if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Battery functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
uint16_t res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
int getBattPercent() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
sendAT(GF("+QIOPEN="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
int rsp = waitResponse(75000L,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
if ( rsp != 1 ) {
return false;
}
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+QISEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
}
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(GF(GSM_NL "SEND OK")) != 1) {
return 0;
}
bool allAcknowledged = false;
// bool failed = false;
while ( !allAcknowledged ) {
sendAT( GF("+QISACK"));
if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) {
return -1;
} else {
streamSkipUntil(','); /** Skip total */
streamSkipUntil(','); /** Skip acknowledged data size */
if ( stream.readStringUntil('\n').toInt() == 0 ) {
allAcknowledged = true;
}
}
}
waitResponse(5000L);
// streamSkipUntil(','); // Skip mux
// return stream.readStringUntil('\n').toInt();
return 1;
}
size_t modemRead(size_t size, uint8_t mux) {
sendAT(GF("+QIRD="), mux, ',', size);
if (waitResponse(GF("+QIRD:")) != 1) {
return 0;
}
size_t len = stream.readStringUntil('\n').toInt();
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
}
waitResponse();
DBG("### READ:", mux, ",", len);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
sendAT(GF("+QIRD="), mux, GF(",0"));
size_t result = 0;
if (waitResponse(GF("+QIRD:")) == 1) {
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = stream.readStringUntil('\n').toInt();
DBG("### STILL:", mux, "has", result);
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
return result;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+QISTATE=1,"), mux);
//+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QISTATE:")))
return false;
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip socket type
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int res = stream.readStringUntil(',').toInt(); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
}
public:
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=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();
String r6s(r6); r6s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s, ",", r6s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (r6 && data.endsWith(r6)) {
index = 6;
goto finish;
} else if (data.endsWith(GF(GSM_NL "+QIRD:"))) {
streamSkipUntil(','); // Skip the context
streamSkipUntil(','); // Skip the role
int mux = stream.readStringUntil('\n').toInt();
DBG("### Got Data:", mux);
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
} else if (data.endsWith(GF("CLOSED" GSM_NL))) {
int nl = data.lastIndexOf(GSM_NL, data.length()-8);
int coma = data.indexOf(',', nl+2);
int 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);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
//DBG('<', index, '>');
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5, r6);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5, r6);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

View File

@@ -45,8 +45,7 @@ enum TinyGSMDateTimeFormat {
DATE_DATE = 2
};
class TinyGsmSim800
class TinyGsmSim800 : public TinyGsmModem
{
public:
@@ -217,7 +216,7 @@ public:
public:
TinyGsmSim800(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -225,11 +224,8 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
if (!testAT()) {
return false;
}
@@ -243,6 +239,19 @@ public:
return true;
}
String getModemName() {
#if defined(TINY_GSM_MODEM_SIM800)
return "SIMCom SIM800";
#elif defined(TINY_GSM_MODEM_SIM808)
return "SIMCom SIM808";
#elif defined(TINY_GSM_MODEM_SIM868)
return "SIMCom SIM868";
#elif defined(TINY_GSM_MODEM_SIM900)
return "SIMCom SIM900";
#endif
return "SIMCom SIM800";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -312,6 +321,14 @@ public:
#endif
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -455,22 +472,6 @@ public:
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE;
bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GPRS functions
*/
@@ -591,6 +592,10 @@ public:
return true;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
@@ -603,9 +608,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
@@ -800,6 +802,10 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
@@ -894,30 +900,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(const char c, const unsigned long timeout = 3000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

View File

@@ -27,7 +27,7 @@ public:
// enable GPS
bool enableGPS() {
uint16_t state;
// uint16_t state;
sendAT(GF("+CGNSPWR=1"));
if (waitResponse() != 1) {
@@ -38,7 +38,7 @@ public:
}
bool disableGPS() {
uint16_t state;
// uint16_t state;
sendAT(GF("+CGNSPWR=0"));
if (waitResponse() != 1) {
@@ -65,7 +65,7 @@ public:
// works only with ans SIM808 V2
bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) {
//String buffer = "";
char chr_buffer[12];
// char chr_buffer[12];
bool fix = false;
sendAT(GF("+CGNSINF"));

View File

@@ -40,7 +40,7 @@ enum RegStatus {
};
class TinyGsmUBLOX
class TinyGsmUBLOX : public TinyGsmModem
{
public:
@@ -123,7 +123,7 @@ public:
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
while (cnt < size && sock_connected) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -201,7 +201,7 @@ public:
public:
TinyGsmUBLOX(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -209,9 +209,6 @@ public:
/*
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
if (!testAT()) {
@@ -228,6 +225,10 @@ public:
return (getSimStatus() == SIM_READY);
}
String getModemName() {
return "u-blox Cellular Modem";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -280,6 +281,14 @@ public:
return true;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -400,22 +409,6 @@ public:
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) TINY_GSM_ATTR_NOT_AVAILABLE;
bool networkDisconnect() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GPRS functions
*/
@@ -481,6 +474,10 @@ public:
return localIP() != 0;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) {
@@ -495,10 +492,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -566,6 +559,10 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) {
@@ -659,30 +656,9 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
/*
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {

View File

@@ -45,7 +45,7 @@ enum XBeeType {
};
class TinyGsmXBee
class TinyGsmXBee : public TinyGsmModem
{
public:
@@ -207,17 +207,14 @@ public:
public:
TinyGsmXBee(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{}
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
guardTime = 1100; // Start with a default guard time of 1 second
if (!commandMode(10)) return false; // Try up to 10 times for the init
@@ -242,6 +239,10 @@ public:
return ret_val;
}
String getModemName() {
return getBeeName();
}
void setBaud(unsigned long baud) {
if (!commandMode()) return;
switch(baud)
@@ -309,6 +310,16 @@ public:
else return true;
}
bool hasWifi() {
if (beeType == XBEE_S6B_WIFI) return true;
else return false;
}
bool hasGPRS() {
if (beeType == XBEE_S6B_WIFI) return false;
else return true;
}
XBeeType getBeeType() {
return beeType;
}
@@ -321,6 +332,7 @@ public:
case XBEE3_LTE1_ATT: return "Digi XBee3™ Cellular LTE CAT 1";
case XBEE3_LTEM_ATT: return "Digi XBee3™ Cellular LTE-M";
case XBEE3_LTENB: return "Digi XBee3™ Cellular NB-IoT";
default: return "Digi XBee®";
}
}
@@ -555,6 +567,10 @@ fail:
return res;
}
/*
* IP Address functions
*/
String getLocalIP() {
if (!commandMode()) return ""; // Return immediately
sendAT(GF("MY"));
@@ -566,10 +582,6 @@ fail:
return IPaddr;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* GPRS functions
*/
@@ -639,6 +651,10 @@ fail:
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) {
@@ -704,63 +720,15 @@ protected:
public:
/* Utilities */
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
/*
Utilities
*/
void streamClear(void) {
TINY_GSM_YIELD();
while (stream.available()) { stream.read(); }
}
bool commandMode(int retries = 2) {
int triesMade = 0;
bool success = false;
streamClear(); // Empty everything in the buffer before starting
while (!success and triesMade < retries) {
// Cannot send anything for 1 "guard time" before entering command mode
// Default guard time is 1s, but the init fxn decreases it to 250 ms
delay(guardTime);
streamWrite(GF("+++")); // enter command mode
DBG("+++");
success = (1 == waitResponse(guardTime*2));
triesMade ++;
}
return success;
}
bool writeChanges(void) {
sendAT(GF("WR")); // Write changes to flash
if (1 != waitResponse()) return false;
sendAT(GF("AC")); // Apply changes
if (1 != waitResponse()) return false;
return true;
}
void exitCommand(void) {
sendAT(GF("CN")); // Exit command mode
waitResponse();
}
String readResponse(uint32_t timeout = 1000) {
TINY_GSM_YIELD();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < timeout) {};
String res = stream.readStringUntil('\r'); // lines end with carriage returns
res.trim();
DBG("<<< ", res);
return res;
}
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
@@ -825,6 +793,7 @@ finish:
// DBG("<<< ", data);
}
}
//DBG('<', index, '>');
return index;
}
@@ -842,6 +811,45 @@ finish:
return waitResponse(1000, r1, r2, r3, r4, r5);
}
bool commandMode(int retries = 2) {
int triesMade = 0;
bool success = false;
streamClear(); // Empty everything in the buffer before starting
while (!success and triesMade < retries) {
// Cannot send anything for 1 "guard time" before entering command mode
// Default guard time is 1s, but the init fxn decreases it to 250 ms
delay(guardTime);
streamWrite(GF("+++")); // enter command mode
DBG("+++");
success = (1 == waitResponse(guardTime*2));
triesMade ++;
}
return success;
}
bool writeChanges(void) {
sendAT(GF("WR")); // Write changes to flash
if (1 != waitResponse()) return false;
sendAT(GF("AC")); // Apply changes
if (1 != waitResponse()) return false;
return true;
}
void exitCommand(void) {
sendAT(GF("CN")); // Exit command mode
waitResponse();
}
String readResponse(uint32_t timeout = 1000) {
TINY_GSM_YIELD();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < timeout) {};
String res = stream.readStringUntil('\r'); // lines end with carriage returns
res.trim();
DBG("<<< ", res);
return res;
}
public:
Stream& stream;

View File

@@ -69,6 +69,7 @@ namespace {
}
}
#else
#define DBG_PLAIN(...)
#define DBG(...)
#endif
@@ -194,4 +195,135 @@ String TinyGsmDecodeHex16bit(String &instr) {
return result;
}
class TinyGsmModem
{
public:
TinyGsmModem(Stream& stream)
: stream(stream)
{}
/*
* Basic functions
*/
// Prepare the modem for further functionality
virtual bool init(const char* pin = NULL) = 0;
// Begin is redundant with init
virtual bool begin(const char* pin = NULL) {
return init(pin);
}
// Returns a string with the chip name
virtual String getModemName() = 0;
// Sets the serial communication baud rate
virtual void setBaud(unsigned long baud) = 0;
// Checks that the modem is responding to standard AT commands
virtual bool testAT(unsigned long timeout = 10000L) = 0;
// Holds open communication with the modem waiting for data to come in
virtual void maintain() = 0;
// Resets all modem chip settings to factor defaults
virtual bool factoryDefault() = 0;
// Returns the response to a get info request. The format varies by modem.
virtual String getModemInfo() = 0;
// Answers whether types of communication are available on this modem
virtual bool hasSSL() = 0;
virtual bool hasWifi() = 0;
virtual bool hasGPRS() = 0;
/*
* Power functions
*/
virtual bool restart() = 0;
/*
* SIM card functions - only apply to cellular modems
*/
virtual bool simUnlock(const char *pin) { return false; }
virtual String getSimCCID() { return ""; }
virtual String getIMEI() { return ""; }
virtual String getOperator() { return ""; }
/*
* Generic network functions
*/
virtual int getSignalQuality() = 0;
// NOTE: this returns whether the modem is registered on the cellular or WiFi
// network NOT whether GPRS or other internet connections are available
virtual bool isNetworkConnected() = 0;
virtual bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions - only apply to WiFi modems
*/
virtual bool networkConnect(const char* ssid, const char* pwd) { return false; }
virtual bool networkDisconnect() { return false; }
/*
* GPRS functions - only apply to cellular modems
*/
virtual bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
return false;
}
virtual bool gprsDisconnect() { return false; }
/*
* IP Address functions
*/
virtual String getLocalIP() = 0;
virtual IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
Utilities
*/
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(const char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
public:
Stream& stream;
};
#endif

View File

@@ -3,6 +3,7 @@
* DO NOT USE THIS - this is just a compilation test!
*
**************************************************************/
#define TINY_GSM_MODEM_SIM800
#include <TinyGsmClient.h>
@@ -77,4 +78,3 @@ void loop() {
modem.networkDisconnect();
#endif
}