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

@@ -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