Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Michael Krumpus
2019-03-30 13:47:49 -05:00
62 changed files with 4384 additions and 1042 deletions

View File

@@ -9,16 +9,9 @@
#ifndef TinyGsmClient_h
#define TinyGsmClient_h
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_UBLOX) || defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_SSL
#endif
#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7)
#define TINY_GSM_MODEM_HAS_GPS
#endif
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM900)
#if defined(TINY_GSM_MODEM_SIM800)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientSIM800.h>
typedef TinyGsmSim800 TinyGsm;
typedef TinyGsmSim800::GsmClient TinyGsmClient;
@@ -26,18 +19,41 @@
#elif defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_SSL
#define TINY_GSM_MODEM_HAS_GPS
#include <TinyGsmClientSIM808.h>
typedef TinyGsmSim808 TinyGsm;
typedef TinyGsmSim808::GsmClient TinyGsmClient;
typedef TinyGsmSim808::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM900)
#define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientSIM800.h>
typedef TinyGsmSim800 TinyGsm;
typedef TinyGsmSim800::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_SIM7000)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_GPS
#include <TinyGsmClientSIM7000.h>
typedef TinyGsmSim7000 TinyGsm;
typedef TinyGsmSim7000::GsmClient TinyGsmClient;
typedef TinyGsmSim7000::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_UBLOX)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientUBLOX.h>
typedef TinyGsmUBLOX TinyGsm;
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,8 +72,16 @@
typedef TinyGsmM590 TinyGsm;
typedef TinyGsmM590::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E)
#include <TinyGsmClientMC60.h>
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_GPS
typedef TinyGsmMC60 TinyGsm;
typedef TinyGsmMC60::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_WIFI
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientESP8266.h>
typedef TinyGsmESP8266 TinyGsm;
typedef TinyGsmESP8266::GsmClient TinyGsmClient;
@@ -66,6 +90,7 @@
#elif defined(TINY_GSM_MODEM_XBEE)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_WIFI
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientXBee.h>
typedef TinyGsmXBee TinyGsm;
typedef TinyGsmXBee::GsmClient TinyGsmClient;

View File

@@ -8,13 +8,10 @@
#ifndef TinyGsmClientA6_h
#define TinyGsmClientA6_h
//#pragma message("TinyGSM: TinyGsmClientA6")
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 8
#include <TinyGsmCommon.h>
@@ -38,22 +35,10 @@ enum RegStatus {
REG_UNKNOWN = 4,
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmA6 Class
//============================================================================//
//============================================================================//
class TinyGsmA6
class TinyGsmA6 : public TinyGsmModem
{
//============================================================================//
//============================================================================//
// The Internal A6 Client Class
//============================================================================//
//============================================================================//
public:
class GsmClient : public Client
@@ -127,7 +112,7 @@ public:
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
if (!rx.size()) {
at->maintain();
}
return rx.size();
@@ -136,7 +121,8 @@ public:
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
while (cnt < size) {
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -147,7 +133,6 @@ public:
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
at->maintain();
//break;
}
}
return cnt;
@@ -185,28 +170,11 @@ private:
RxFifo rx;
};
//============================================================================//
//============================================================================//
// The A6 does not have a secure client!
//============================================================================//
//============================================================================//
//============================================================================//
//============================================================================//
// The A6 Modem Functions
//============================================================================//
//============================================================================//
public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmA6(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmA6(Stream& stream)
#endif
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -214,11 +182,9 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -226,16 +192,24 @@ public:
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CMEE=0"));
sendAT(GF("+CMEE=0")); // Turn off verbose errors
waitResponse();
sendAT(GF("+CMER=3,0,0,2"));
sendAT(GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination
waitResponse();
DBG(GF("### Modem:"), getModemName());
getSimStatus();
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);
}
@@ -243,10 +217,7 @@ public:
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -279,6 +250,14 @@ public:
return false;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -380,7 +359,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
@@ -395,20 +374,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
*/
/*
* GPRS functions
*/
@@ -467,6 +432,10 @@ public:
return (res == 1);
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIFSR"));
String res;
@@ -479,10 +448,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -621,7 +586,7 @@ public:
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() {
int8_t getBattPercent() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
@@ -632,6 +597,10 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux) {
@@ -654,7 +623,7 @@ protected:
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', len);
if (waitResponse(2000L, GF(GSM_NL ">")) != 1) {
return 0;
@@ -676,31 +645,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

@@ -8,14 +8,11 @@
#ifndef TinyGsmClientBG96_h
#define TinyGsmClientBG96_h
//#pragma message("TinyGSM: TinyGsmClientBG96")
//#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 12
#include <TinyGsmCommon.h>
@@ -39,22 +36,10 @@ enum RegStatus {
REG_UNKNOWN = 4,
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmBG96 Class
//============================================================================//
//============================================================================//
class TinyGsmBG96
class TinyGsmBG96 : public TinyGsmModem
{
//============================================================================//
//============================================================================//
// The Internal BG96 Client Class
//============================================================================//
//============================================================================//
public:
class GsmClient : public Client
@@ -104,10 +89,21 @@ public:
virtual void stop() {
TINY_GSM_YIELD();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
@@ -190,46 +186,31 @@ private:
RxFifo rx;
};
//============================================================================//
//============================================================================//
// The BG96 Secure Client
//============================================================================//
//============================================================================//
// 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;
}
};
//============================================================================//
//============================================================================//
// The BG96 Modem Functions
//============================================================================//
//============================================================================//
public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmBG96(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmBG96(Stream& stream)
#endif
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -237,11 +218,9 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -249,10 +228,15 @@ public:
if (waitResponse() != 1) {
return false;
}
DBG(GF("### Modem:"), getModemName());
getSimStatus();
return true;
}
String getModemName() {
return "Quectel BG96";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -260,10 +244,7 @@ public:
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -307,6 +288,14 @@ public:
return false; // TODO: For now
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -324,8 +313,9 @@ public:
}
bool poweroff() {
sendAT(GF("+QPOWD"));
return waitResponse(GF("POWERED DOWN")) == 1; // TODO
sendAT(GF("+QPOWD=1"));
waitResponse(300); // returns OK first
return waitResponse(300, GF("POWERED DOWN")) == 1;
}
bool radioOff() {
@@ -413,7 +403,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
@@ -428,42 +418,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
*/
/*
* 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;
}
@@ -485,15 +463,16 @@ public:
if (res != 1)
return false;
return localIP() != 0;
return localIP() != IPAddress(0,0,0,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 "";
@@ -501,10 +480,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -560,6 +535,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();
@@ -592,14 +569,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;
}
int8_t 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();
@@ -616,7 +621,7 @@ protected:
return (0 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+QISEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
@@ -685,31 +690,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

@@ -8,13 +8,10 @@
#ifndef TinyGsmClientESP8266_h
#define TinyGsmClientESP8266_h
//#pragma message("TinyGSM: TinyGsmClientESP8266")
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512
#endif
#define TINY_GSM_MUX_COUNT 5
#include <TinyGsmCommon.h>
@@ -38,22 +35,10 @@ enum RegStatus {
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmESP8266 Class
//============================================================================//
//============================================================================//
class TinyGsmESP8266
class TinyGsmESP8266 : public TinyGsmModem
{
//============================================================================//
//============================================================================//
// The ESP8266 Internal Client Class
//============================================================================//
//============================================================================//
public:
class GsmClient : public Client
@@ -124,7 +109,7 @@ public:
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
if (!rx.size()) {
at->maintain();
}
return rx.size();
@@ -133,7 +118,8 @@ public:
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
while (cnt < size) {
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -144,7 +130,6 @@ public:
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
at->maintain();
//break;
}
}
return cnt;
@@ -182,12 +167,6 @@ private:
RxFifo rx;
};
//============================================================================//
//============================================================================//
// The Secure ESP8266 Client Class
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient
{
@@ -209,20 +188,10 @@ public:
};
//============================================================================//
//============================================================================//
// The ESP8266 Modem Functions
//============================================================================//
//============================================================================//
public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmESP8266(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmESP8266(Stream& stream)
#endif
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -230,11 +199,9 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -250,20 +217,22 @@ public:
if (waitResponse() != 1) {
return false;
}
DBG(GF("### Modem:"), getModemName());
return true;
}
String getModemName() {
return "ESP8266";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
sendAT(GF("+UART_CUR="), baud, "8,1,0,0");
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -294,6 +263,14 @@ public:
return true;
}
bool hasWifi() {
return true;
}
bool hasGPRS() {
return false;
}
/*
* Power functions
*/
@@ -313,7 +290,10 @@ public:
return init();
}
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool poweroff() {
sendAT(GF("+GSLP=0")); // Power down indefinitely - until manually reset!
return waitResponse() == 1;
}
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
@@ -335,7 +315,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CWJAP_CUR?"));
int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
if (res1 != 2) {
@@ -390,6 +370,10 @@ public:
return retVal;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIPSTA_CUR??"));
int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
@@ -401,32 +385,10 @@ public:
return res2;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* GPRS functions
* Client related functions
*/
/*
* 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:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
@@ -444,7 +406,7 @@ protected:
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
@@ -464,31 +426,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

@@ -8,13 +8,10 @@
#ifndef TinyGsmClientM590_h
#define TinyGsmClientM590_h
//#pragma message("TinyGSM: TinyGsmClientM590")
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 2
#include <TinyGsmCommon.h>
@@ -39,22 +36,9 @@ enum RegStatus {
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmM590 Class
//============================================================================//
//============================================================================//
class TinyGsmM590
class TinyGsmM590 : public TinyGsmModem
{
//============================================================================//
//============================================================================//
// The M590 Internal Client Class
//============================================================================//
//============================================================================//
public:
class GsmClient : public Client
@@ -134,7 +118,8 @@ public:
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
while (cnt < size) {
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -143,9 +128,8 @@ public:
continue;
}
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
if (!rx.size()) {
at->maintain();
//break;
}
}
return cnt;
@@ -183,28 +167,11 @@ private:
RxFifo rx;
};
//============================================================================//
//============================================================================//
// The M590 Has no Secure client!
//============================================================================//
//============================================================================//
//============================================================================//
//============================================================================//
// The M590 Modem Functions
//============================================================================//
//============================================================================//
public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmM590(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmM590(Stream& stream)
#endif
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -212,11 +179,9 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -228,11 +193,15 @@ public:
sendAT(GF("+CMEE=2"));
waitResponse();
#endif
DBG(GF("### Modem:"), getModemName());
getSimStatus();
return true;
}
String getModemName() {
return "Neoway M590";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
@@ -240,10 +209,7 @@ public:
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -284,6 +250,14 @@ public:
return false;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -389,7 +363,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
@@ -404,20 +378,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
*/
/*
* GPRS functions
*/
@@ -474,6 +434,10 @@ public:
return res == 1;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
@@ -486,10 +450,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@@ -563,7 +523,11 @@ public:
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
@@ -587,7 +551,7 @@ protected:
return false;
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+TCPSEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
@@ -622,31 +586,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) {

848
src/TinyGsmClientM95.h Normal file
View File

@@ -0,0 +1,848 @@
/**
* @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
//#pragma message("TinyGSM: TinyGsmClientM95")
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#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();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
}
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()) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
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) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
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
DBG(GF("### Modem:"), getModemName());
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) 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=1"));
return waitResponse(300, GF("NORMAL POWER DOWN")) == 1;
}
bool radioOff() {
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return true;
}
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* 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
*/
int16_t 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() != IPAddress(0,0,0,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;
}
int8_t 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="), mux, 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));
return (1 == rsp);
}
int16_t 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

880
src/TinyGsmClientMC60.h Normal file
View File

@@ -0,0 +1,880 @@
/**
* @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
//#pragma message("TinyGSM: TinyGsmClientMC60")
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#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();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
}
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()) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
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) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
sendAT(GF("&FZ")); // Factory + Reset
waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
DBG(GF("### Modem:"), getModemName());
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) 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;
}
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* 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
*/
int16_t 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() != IPAddress(0,0,0,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;
}
int8_t 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="), mux, 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));
return (1 == rsp);
}
int16_t 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

1099
src/TinyGsmClientSIM7000.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,14 +8,11 @@
#ifndef TinyGsmClientSIM800_h
#define TinyGsmClientSIM800_h
//#pragma message("TinyGSM: TinyGsmClientSIM800")
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 5
#include <TinyGsmCommon.h>
@@ -45,7 +42,7 @@ enum TinyGSMDateTimeFormat {
DATE_DATE = 2
};
class TinyGsmSim800
class TinyGsmSim800 : public TinyGsmModem
{
public:
@@ -98,10 +95,21 @@ public:
virtual void stop() {
TINY_GSM_YIELD();
at->sendAT(GF("+CIPCLOSE="), mux);
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->sendAT(GF("+CIPCLOSE="), mux, GF(",1")); // Quick close
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
@@ -121,11 +129,11 @@ public:
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
if (!rx.size()) {
// Workaround: sometimes SIM800 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 500) {
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
@@ -138,7 +146,7 @@ public:
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size && sock_connected) {
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
@@ -146,10 +154,17 @@ public:
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
// Workaround: sometimes SIM800 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
// TODO: Read directly into user buffer?
if (sock_available > 0) {
at->modemRead(rx.free(), mux);
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
@@ -192,6 +207,7 @@ private:
RxFifo rx;
};
class GsmClientSecure : public GsmClient
{
public:
@@ -211,10 +227,11 @@ public:
}
};
public:
TinyGsmSim800(Stream& stream)
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@@ -222,11 +239,9 @@ public:
/*
* Basic functions
*/
bool begin() {
return init();
}
bool init() {
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -236,10 +251,24 @@ public:
if (waitResponse() != 1) {
return false;
}
DBG(GF("### Modem:"), getModemName());
getSimStatus();
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);
}
@@ -248,10 +277,7 @@ public:
//streamWrite(GF("AAAAA" GSM_NL)); // TODO: extra A's to help detect the baud rate
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -309,6 +335,14 @@ public:
#endif
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -402,10 +436,10 @@ public:
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
@@ -437,7 +471,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
@@ -452,16 +486,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;
}
/*
* GPRS functions
*/
@@ -582,6 +606,10 @@ public:
return true;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CIFSR;E0"));
String res;
@@ -594,9 +622,6 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
@@ -780,7 +805,7 @@ public:
return res;
}
int getBattPercent() {
int8_t getBattPercent() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
@@ -791,6 +816,10 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
@@ -813,7 +842,7 @@ protected:
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
@@ -885,30 +914,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) {
@@ -984,6 +992,7 @@ finish:
}
data = "";
}
//DBG('<', index, '>');
return index;
}

View File

@@ -8,16 +8,11 @@
#ifndef TinyGsmClientSIM808_h
#define TinyGsmClientSIM808_h
//#pragma message("TinyGSM: TinyGsmClientSIM808")
#include <TinyGsmClientSIM800.h>
//============================================================================//
//============================================================================//
// Declaration and Definitio of the TinyGsmSim808 Class
//============================================================================//
//============================================================================//
class TinyGsmSim808: public TinyGsmSim800
{
@@ -33,7 +28,7 @@ public:
// enable GPS
bool enableGPS() {
uint16_t state;
// uint16_t state;
sendAT(GF("+CGNSPWR=1"));
if (waitResponse() != 1) {
@@ -44,7 +39,7 @@ public:
}
bool disableGPS() {
uint16_t state;
// uint16_t state;
sendAT(GF("+CGNSPWR=0"));
if (waitResponse() != 1) {
@@ -71,7 +66,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

@@ -8,14 +8,11 @@
#ifndef TinyGsmClientUBLOX_h
#define TinyGsmClientUBLOX_h
//#pragma message("TinyGSM: TinyGsmClientUBLOX")
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 5
#define TINY_GSM_MUX_COUNT 7
#include <TinyGsmCommon.h>
@@ -39,22 +36,10 @@ enum RegStatus {
REG_UNKNOWN = 4,
};
//============================================================================//
//============================================================================//
// Declaration of the TinyGsmUBLOX Class
//============================================================================//
//============================================================================//
class TinyGsmUBLOX
class TinyGsmUBLOX : public TinyGsmModem
{
//============================================================================//
//============================================================================//
// The UBLOX Internal Client Class
//============================================================================//
//============================================================================//
public:
class GsmClient : public Client
@@ -65,14 +50,15 @@ class GsmClient : public Client
public:
GsmClient() {}
GsmClient(TinyGsmUBLOX& modem, uint8_t mux = 1) {
GsmClient(TinyGsmUBLOX& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmUBLOX* modem, uint8_t mux = 1) {
bool init(TinyGsmUBLOX* modem, uint8_t mux = 0) {
this->at = modem;
this->mux = mux;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
return true;
@@ -80,11 +66,24 @@ public:
public:
virtual int connect(const char *host, uint16_t port) {
stop();
if (sock_connected) {
stop();
// If we're creating a new connection on the same client, we need to wait
// until the async close has finished on Cat-M modems.
// After close has completed, the +UUSOCL should appear.
if (at->isCatM) {
DBG("Waiting for +UUSOCL URC on", mux);
for (unsigned long start = millis(); millis() - start < 120000L; ) {
at->maintain();
if (!sock_connected) break;
}
}
}
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, &mux);
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
@@ -102,10 +101,22 @@ public:
virtual void stop() {
TINY_GSM_YIELD();
at->sendAT(GF("+USOCL="), mux);
sock_connected = false;
at->waitResponse();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->modemDisconnect(mux);
// We don't actually know if the CatM modem has finished closing because
// we're using an "asynchronous" close
if (!at->isCatM) sock_connected = false;
}
virtual size_t write(const uint8_t *buf, size_t size) {
@@ -125,7 +136,14 @@ public:
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
if (!rx.size()) {
// Workaround: sometimes SARA R410 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
}
return rx.size() + sock_available;
@@ -143,8 +161,15 @@ public:
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
// Workaround: sometimes SARA R410 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
// TODO: Read directly into user buffer?
if (sock_available > 0) {
sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
@@ -183,17 +208,12 @@ private:
TinyGsmUBLOX* at;
uint8_t mux;
uint16_t sock_available;
uint32_t prev_check;
bool sock_connected;
bool got_data;
RxFifo rx;
};
//============================================================================//
//============================================================================//
// The Secure UBLOX Client Class
//============================================================================//
//============================================================================//
class GsmClientSecure : public GsmClient
{
@@ -211,36 +231,27 @@ public:
rx.clear();
sock_connected = at->modemConnect(host, port, &mux, true);
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
};
//============================================================================//
//============================================================================//
// The UBLOX Modem Functions
//============================================================================//
//============================================================================//
public:
#ifdef GSM_DEFAULT_STREAM
TinyGsmUBLOX(Stream& stream = GSM_DEFAULT_STREAM)
#else
TinyGsmUBLOX(Stream& stream)
#endif
: stream(stream)
: TinyGsmModem(stream), stream(stream)
{
memset(sockets, 0, sizeof(sockets));
isCatM = false; // For SARA R4 and N4 series
}
/*
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
@@ -248,11 +259,52 @@ public:
if (waitResponse() != 1) {
return false;
}
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
String name = getModemName();
DBG(GF("### Modem:"), name);
if (name.startsWith("u-blox SARA-R4") or name.startsWith("u-blox SARA-N4")) {
isCatM = true;
}
else if (name.startsWith("u-blox SARA-N2")) {
DBG(GF("### SARA N2 NB-IoT modems not supported!"), name);
}
int ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
}
return (getSimStatus() == SIM_READY);
// if the sim is ready, or it's locked but no pin has been provided, return true
else {
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
String getModemName() {
sendAT(GF("+CGMI"));
String res1;
if (waitResponse(1000L, res1) != 1) {
return "u-blox Cellular Modem";
}
res1.replace(GSM_NL "OK" GSM_NL, "");
res1.trim();
sendAT(GF("+GMM"));
String res2;
if (waitResponse(1000L, res2) != 1) {
return "u-blox Cellular Modem";
}
res2.replace(GSM_NL "OK" GSM_NL, "");
res2.trim();
return res1 + String(' ') + res2;
}
void setBaud(unsigned long baud) {
@@ -262,10 +314,7 @@ public:
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
@@ -280,15 +329,21 @@ public:
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
waitResponse(15, NULL, NULL);
}
}
bool factoryDefault() {
sendAT(GF("+UFACTORY=0,1")); // Factory + Reset + Echo Off
waitResponse();
sendAT(GF("+CFUN=16")); // Auto-baud
return waitResponse() == 1;
if (!isCatM) {
sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
waitResponse();
sendAT(GF("+CFUN=16")); // Reset
return waitResponse() == 1;
}
else {
sendAT(GF("&F")); // Resets the current profile, other NVM not affected
return waitResponse() == 1;
}
}
String getModemInfo() {
@@ -307,6 +362,14 @@ public:
return true;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
@@ -315,15 +378,25 @@ public:
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=16"));
if (!isCatM)
{
sendAT(GF("+CFUN=16"));
}
else
{
sendAT(GF("+CFUN=15"));
}
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
delay(3000); // TODO: Verify delay timing here
return init();
}
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool poweroff() {
sendAT(GF("+CPWROFF"));
return waitResponse(40000L) == 1;
}
bool radioOff() {
sendAT(GF("+CFUN=0"));
@@ -387,9 +460,17 @@ public:
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CGREG?"));
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
if (isCatM) { // Check EPS registration for LTE modules
sendAT(GF("+CEREG?"));
if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) {
return REG_UNKNOWN;
}
}
else {
sendAT(GF("+CGREG?")); // Check GPRS registration for others
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
@@ -412,7 +493,7 @@ public:
* Generic network functions
*/
int getSignalQuality() {
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
@@ -424,70 +505,118 @@ public:
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
if (s == REG_OK_HOME || s == REG_OK_ROAMING)
return true;
else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown..
return isGprsConnected();
else return false;
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
bool setURAT( uint8_t urat ) {
// AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]]
sendAT(GF("+COPS=2")); // Deregister from network
if (waitResponse() != 1) {
return false;
}
return false;
sendAT(GF("+URAT="), urat); // Radio Access Technology (RAT) selection
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+COPS=0")); // Auto-register to the network
if (waitResponse() != 1) {
return false;
}
return restart();
}
/*
* WiFi functions
*/
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) {
sendAT(GF("+CGATT=1")); // attach to GPRS
if (waitResponse(360000L) != 1) {
return false;
}
sendAT(GF("+UPSD=0,1,\""), apn, '"');
waitResponse();
// Using CGDCONT sets up an "external" PCP context, i.e. a data connection
// using the external IP stack (e.g. Windows dial up) and PPP link over the
// serial interface. This is the only command set supported by the LTE-M
// and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx)
if (user && strlen(user) > 0) {
sendAT(GF("+UPSD=0,2,\""), user, '"');
// Setting up the PSD profile/PDP context with the UPSD commands sets up an
// "internal" PDP context, i.e. a data connection using the internal IP
// stack and related AT commands for sockets. This is what we're using for
// all of the other modules.
if (isCatM) {
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); // Set the authentication
waitResponse();
}
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); // Define PDP context 1
waitResponse();
sendAT(GF("+CGACT=1,1")); // activate PDP profile/context 1
if (waitResponse(150000L) != 1) {
return false;
}
return true;
}
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+UPSD=0,3,\""), pwd, '"');
else {
sendAT(GF("+UPSD=0,1,\""), apn, '"'); // Set APN for PSD profile 0
waitResponse();
}
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP
waitResponse();
if (user && strlen(user) > 0) {
sendAT(GF("+UPSD=0,2,\""), user, '"'); // Set user for PSD profile 0
waitResponse();
}
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+UPSD=0,3,\""), pwd, '"'); // Set password for PSD profile 0
waitResponse();
}
sendAT(GF("+UPSDA=0,3"));
if (waitResponse(60000L) != 1) {
return false;
}
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0
waitResponse();
// Open a GPRS context
sendAT(GF("+UPSND=0,8"));
if (waitResponse(GF(",8,1")) != 1) {
return false;
sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) {
return false;
}
sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0
if (waitResponse(GF(",8,1")) != 1) {
return false;
}
waitResponse();
return true;
}
waitResponse();
return true;
}
bool gprsDisconnect() {
sendAT(GF("+UPSDA=0,4"));
if (waitResponse(60000L) != 1)
return false;
sendAT(GF("+CGATT=0"));
if (waitResponse(60000L) != 1)
// LTE-M and NB-IoT modules do not support UPSx commands
if (isCatM) {
sendAT(GF("+CGACT=1,0")); // Deactivate PDP context 1
if (waitResponse(40000L) != 1) {
return false;
}
}
else {
sendAT(GF("+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
if (waitResponse(360000L) != 1)
return false;
}
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1)
return false;
return true;
@@ -503,25 +632,40 @@ public:
if (res != 1)
return false;
return localIP() != 0;
return localIP() != IPAddress(0,0,0,0);
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) {
return "";
// LTE-M and NB-IoT modules do not support UPSx commands
if (isCatM) {
sendAT(GF("+CGPADDR"));
if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) {
return "";
}
streamSkipUntil(','); // Skip context id
String res = stream.readStringUntil('\r');
if (waitResponse() != 1) {
return "";
}
return res;
}
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) {
return "";
else {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) {
return "";
}
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) {
return "";
}
return res;
}
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
@@ -580,7 +724,7 @@ public:
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() {
int8_t getBattPercent() {
sendAT(GF("+CIND?"));
if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
return 0;
@@ -591,11 +735,15 @@ public:
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) {
sendAT(GF("+USOCR=6"));
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {
sendAT(GF("+USOCR=6")); // create a socket
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { // reply is +USOCR: ## of socket created
return false;
}
*mux = stream.readStringUntil('\n').toInt();
@@ -614,12 +762,28 @@ protected:
//sendAT(GF("+USOSO="), *mux, GF(",6,2,30000"));
//waitResponse();
// connect on the allocated socket
// TODO: Use faster "asynchronous" connection?
// We would have to wait for the +UUSOCO URC to verify connection
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
int rsp = waitResponse(75000L);
int rsp = waitResponse(120000L);
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
bool modemDisconnect(uint8_t mux) {
TINY_GSM_YIELD();
if (isCatM) { // These modems allow a faster "asynchronous" close
sendAT(GF("+USOCL="), mux, GF(",1"));
int rsp = waitResponse(120000L);
return (1 == rsp); // but it still can take up to 120s to get a response
}
else { // no async close
sendAT(GF("+USOCL="), mux);
return (1 == waitResponse());
}
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+USOWR="), mux, ',', len);
if (waitResponse(GF("@")) != 1) {
return 0;
@@ -634,6 +798,7 @@ protected:
streamSkipUntil(','); // Skip mux
int sent = stream.readStringUntil('\n').toInt();
waitResponse();
maintain(); // look for a very quick response
return sent;
}
@@ -659,12 +824,15 @@ protected:
size_t modemGetAvailable(uint8_t mux) {
sendAT(GF("+USORD="), mux, ",0");
size_t result = 0;
if (waitResponse(GF(GSM_NL "+USORD:")) == 1) {
uint8_t res = waitResponse(GF(GSM_NL "+USORD:"));
// Will give error "operation not allowed" when attempting to read a socket
// that you have already told to close
if (res == 1) {
streamSkipUntil(','); // Skip mux
result = stream.readStringUntil('\n').toInt();
waitResponse();
}
if (!result) {
if (!result && res != 2 && res != 3) { // Don't check modemGetConnected after an error
sockets[mux]->sock_connected = modemGetConnected(mux);
}
return result;
@@ -672,43 +840,34 @@ protected:
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+USOCTL="), mux, ",10");
if (waitResponse(GF(GSM_NL "+USOCTL:")) != 1)
uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:"));
if (res != 1)
return false;
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int result = stream.readStringUntil('\n').toInt();
// 0: the socket is in INACTIVE status (it corresponds to CLOSED status
// defined in RFC793 "TCP Protocol Specification" [112])
// 1: the socket is in LISTEN status
// 2: the socket is in SYN_SENT status
// 3: the socket is in SYN_RCVD status
// 4: the socket is in ESTABILISHED status
// 5: the socket is in FIN_WAIT_1 status
// 6: the socket is in FIN_WAIT_2 status
// 7: the sokcet is in CLOSE_WAIT status
// 8: the socket is in CLOSING status
// 9: the socket is in LAST_ACK status
// 10: the socket is in TIME_WAIT status
waitResponse();
return result != 0;
return (result != 0);
}
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) {
@@ -802,6 +961,7 @@ public:
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
bool isCatM;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,9 @@
#ifndef TinyGsmCommon_h
#define TinyGsmCommon_h
// The current library version number
#define TINYGSM_VERSION "0.6.1"
#if defined(SPARK) || defined(PARTICLE)
#include "Particle.h"
#elif defined(ARDUINO)
@@ -31,6 +34,10 @@
#define TINY_GSM_YIELD() { delay(0); }
#endif
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type")))
#define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented")))
@@ -69,6 +76,7 @@ namespace {
}
}
#else
#define DBG_PLAIN(...)
#define DBG(...)
#endif
@@ -194,4 +202,137 @@ 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;
virtual bool poweroff() = 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 int16_t 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; }
virtual bool isGprsConnected() { 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