|
|
@ -11,9 +11,6 @@ |
|
|
|
|
|
|
|
// #define TINY_GSM_DEBUG Serial |
|
|
|
|
|
|
|
#if !defined(TINY_GSM_RX_BUFFER) |
|
|
|
#define TINY_GSM_RX_BUFFER 64 |
|
|
|
#endif |
|
|
|
|
|
|
|
#define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode |
|
|
|
|
|
|
@ -29,24 +26,36 @@ enum SimStatus { |
|
|
|
SIM_LOCKED = 2, |
|
|
|
}; |
|
|
|
|
|
|
|
enum XBeeType { |
|
|
|
XBEE_CELL = 0, |
|
|
|
XBEE_WIFI = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
enum RegStatus { |
|
|
|
REG_UNREGISTERED = 0, |
|
|
|
REG_SEARCHING = 2, |
|
|
|
REG_DENIED = 3, |
|
|
|
REG_OK_HOME = 1, |
|
|
|
REG_OK_ROAMING = 5, |
|
|
|
REG_OK = 1, |
|
|
|
REG_UNKNOWN = 4, |
|
|
|
}; |
|
|
|
|
|
|
|
enum XBeeType { |
|
|
|
XBEE_CELL = 0, |
|
|
|
XBEE_WIFI = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
// Declaration of the TinyGsmXBee Class |
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
|
|
|
|
class TinyGsmXBee |
|
|
|
{ |
|
|
|
|
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
// The XBee Client Class |
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
class GsmClient : public Client |
|
|
@ -116,7 +125,6 @@ public: |
|
|
|
|
|
|
|
virtual size_t write(const uint8_t *buf, size_t size) { |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
//at->maintain(); |
|
|
|
return at->modemSend(buf, size, mux); |
|
|
|
} |
|
|
|
|
|
|
@ -162,6 +170,13 @@ private: |
|
|
|
bool sock_connected; |
|
|
|
}; |
|
|
|
|
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
// The Secure XBee Client Class |
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
|
|
|
|
|
|
|
|
class GsmClientSecure : public GsmClient |
|
|
|
{ |
|
|
|
public: |
|
|
@ -197,6 +212,13 @@ public: |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
// The XBee Modem Functions |
|
|
|
//============================================================================// |
|
|
|
//============================================================================// |
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
#ifdef GSM_DEFAULT_STREAM |
|
|
@ -205,9 +227,7 @@ public: |
|
|
|
TinyGsmXBee(Stream& stream) |
|
|
|
#endif |
|
|
|
: stream(stream) |
|
|
|
{ |
|
|
|
memset(sockets, 0, sizeof(sockets)); |
|
|
|
} |
|
|
|
{} |
|
|
|
|
|
|
|
/* |
|
|
|
* Basic functions |
|
|
@ -290,6 +310,17 @@ public: |
|
|
|
return ret_val; |
|
|
|
} |
|
|
|
|
|
|
|
String getModemInfo() { |
|
|
|
String modemInf = ""; |
|
|
|
if (!commandMode()) return modemInf; // Try up to 10 times for the init |
|
|
|
|
|
|
|
sendAT(GF("HS")); // Get the "Hardware Series" |
|
|
|
modemInf += readResponse(); |
|
|
|
|
|
|
|
exitCommand(); |
|
|
|
return modemInf; |
|
|
|
} |
|
|
|
|
|
|
|
bool hasSSL() { |
|
|
|
if (beeType == XBEE_WIFI) return false; |
|
|
|
else return true; |
|
|
@ -349,6 +380,12 @@ public: |
|
|
|
exitCommand(); |
|
|
|
} |
|
|
|
|
|
|
|
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED; |
|
|
|
|
|
|
|
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; |
|
|
|
|
|
|
|
bool sleepEnable() TINY_GSM_ATTR_NOT_IMPLEMENTED; |
|
|
|
|
|
|
|
/* |
|
|
|
* SIM card functions |
|
|
|
*/ |
|
|
@ -377,65 +414,83 @@ public: |
|
|
|
return SIM_READY; // unsupported |
|
|
|
} |
|
|
|
|
|
|
|
String getOperator() { |
|
|
|
if (!commandMode()) return ""; // Return immediately |
|
|
|
sendAT(GF("MN")); |
|
|
|
String res = readResponse(); |
|
|
|
exitCommand(); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Generic network functions |
|
|
|
*/ |
|
|
|
|
|
|
|
RegStatus getRegistrationStatus() { |
|
|
|
if (!commandMode()) return REG_UNKNOWN; // Return immediately |
|
|
|
|
|
|
|
sendAT(GF("AI")); |
|
|
|
String res = readResponse(); |
|
|
|
RegStatus stat; |
|
|
|
char buf[3] = {0,}; // Set up buffer for response |
|
|
|
res.toCharArray(buf, 3); |
|
|
|
int intRes = strtol(buf, 0, 16); |
|
|
|
RegStatus stat = REG_UNKNOWN; |
|
|
|
|
|
|
|
switch (beeType){ |
|
|
|
case XBEE_WIFI: { |
|
|
|
if(res == GF("0")) // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets |
|
|
|
stat = REG_OK_HOME; |
|
|
|
else if(res == GF("1")) // 0x01 Wi-Fi transceiver initialization in progress. |
|
|
|
if(intRes == 0x00) // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets |
|
|
|
stat = REG_OK; |
|
|
|
else if(intRes == 0x01) // 0x01 Wi-Fi transceiver initialization in progress. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("2")) // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point. |
|
|
|
else if(intRes == 0x02) // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("13")) { // 0x13 Disconnecting from access point. |
|
|
|
sendAT(GF("NR")); // Do a network reset; the S6B tends to get stuck "disconnecting" |
|
|
|
else if(intRes == 0x13) { // 0x13 Disconnecting from access point. |
|
|
|
sendAT(GF("NR0")); // Do a network reset; the S6B tends to get stuck "disconnecting" |
|
|
|
waitResponse(5000); |
|
|
|
writeChanges(); |
|
|
|
stat = REG_UNREGISTERED; |
|
|
|
} |
|
|
|
else if(res == GF("23")) // 0x23 SSID not configured. |
|
|
|
else if(intRes == 0x23) // 0x23 SSID not configured. |
|
|
|
stat = REG_UNREGISTERED; |
|
|
|
else if(res == GF("24")) // 0x24 Encryption key invalid (either NULL or invalid length for WEP). |
|
|
|
else if(intRes == 0x24) // 0x24 Encryption key invalid (either NULL or invalid length for WEP). |
|
|
|
stat = REG_DENIED; |
|
|
|
else if(res == GF("27")) // 0x27 SSID was found, but join failed. |
|
|
|
else if(intRes == 0x27) // 0x27 SSID was found, but join failed. |
|
|
|
stat = REG_DENIED; |
|
|
|
else if(res == GF("40")) // 0x40 Waiting for WPA or WPA2 Authentication. |
|
|
|
else if(intRes == 0x40) // 0x40 Waiting for WPA or WPA2 Authentication. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("41")) // 0x41 Device joined a network and is waiting for IP configuration to complete |
|
|
|
else if(intRes == 0x41) // 0x41 Device joined a network and is waiting for IP configuration to complete |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("42")) // 0x42 Device is joined, IP is configured, and listening sockets are being set up. |
|
|
|
else if(intRes == 0x42) // 0x42 Device is joined, IP is configured, and listening sockets are being set up. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("FF")) // 0xFF Device is currently scanning for the configured SSID. |
|
|
|
else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else stat = REG_UNKNOWN; |
|
|
|
break; |
|
|
|
} |
|
|
|
case XBEE_CELL: { |
|
|
|
if(res == GF("0")) // 0x00 Connected to the Internet. |
|
|
|
stat = REG_OK_HOME; |
|
|
|
else if(res == GF("22")) // 0x22 Registering to cellular network. |
|
|
|
if(intRes == 0x00) // 0x00 Connected to the Internet. |
|
|
|
stat = REG_OK; |
|
|
|
else if(intRes == 0x22) // 0x22 Registering to cellular network. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("23")) // 0x23 Connecting to the Internet. |
|
|
|
else if(intRes == 0x23) // 0x23 Connecting to the Internet. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else if(res == GF("24")) // 0x24 The cellular component is missing, corrupt, or otherwise in error. |
|
|
|
else if(intRes == 0x24) // 0x24 The cellular component is missing, corrupt, or otherwise in error. |
|
|
|
stat = REG_UNKNOWN; |
|
|
|
else if(res == GF("25")) // 0x25 Cellular network registration denied. |
|
|
|
else if(intRes == 0x25) // 0x25 Cellular network registration denied. |
|
|
|
stat = REG_DENIED; |
|
|
|
else if(res == GF("2A")) { // 0x2A Airplane mode. |
|
|
|
else if(intRes == 0x2A) { // 0x2A Airplane mode. |
|
|
|
sendAT(GF("AM0")); // Turn off airplane mode |
|
|
|
waitResponse(); |
|
|
|
writeChanges(); |
|
|
|
stat = REG_UNKNOWN; |
|
|
|
} |
|
|
|
else if(res == GF("2F")) { // 0x2F Bypass mode active. |
|
|
|
else if(intRes == 0x2F) { // 0x2F Bypass mode active. |
|
|
|
sendAT(GF("AP0")); // Set back to transparent mode |
|
|
|
waitResponse(); |
|
|
|
writeChanges(); |
|
|
|
stat = REG_UNKNOWN; |
|
|
|
} |
|
|
|
else if(res == GF("FF")) // 0xFF Device is currently scanning for the configured SSID. |
|
|
|
else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID. |
|
|
|
stat = REG_SEARCHING; |
|
|
|
else stat = REG_UNKNOWN; |
|
|
|
break; |
|
|
@ -446,18 +501,6 @@ public: |
|
|
|
return stat; |
|
|
|
} |
|
|
|
|
|
|
|
String getOperator() { |
|
|
|
if (!commandMode()) return ""; // Return immediately |
|
|
|
sendAT(GF("MN")); |
|
|
|
String res = readResponse(); |
|
|
|
exitCommand(); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Generic network functions |
|
|
|
*/ |
|
|
|
|
|
|
|
int getSignalQuality() { |
|
|
|
if (!commandMode()) return 0; // Return immediately |
|
|
|
if (beeType == XBEE_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity |
|
|
@ -473,19 +516,42 @@ public: |
|
|
|
|
|
|
|
bool isNetworkConnected() { |
|
|
|
RegStatus s = getRegistrationStatus(); |
|
|
|
return (s == REG_OK_HOME || s == REG_OK_ROAMING); |
|
|
|
return (s == REG_OK); |
|
|
|
} |
|
|
|
|
|
|
|
bool waitForNetwork(unsigned long timeout = 60000L) { |
|
|
|
commandMode(); |
|
|
|
for (unsigned long start = millis(); millis() - start < timeout; ) { |
|
|
|
if (isNetworkConnected()) { |
|
|
|
sendAT(GF("AI")); |
|
|
|
String res = readResponse(); |
|
|
|
char buf[3] = {0,}; // Set up buffer for response |
|
|
|
res.toCharArray(buf, 3); |
|
|
|
int intRes = strtol(buf, 0, 16); |
|
|
|
if (intRes == 0) { |
|
|
|
exitCommand(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
delay(250); |
|
|
|
} |
|
|
|
exitCommand(); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
String getLocalIP() { |
|
|
|
if (!commandMode()) return ""; // Return immediately |
|
|
|
sendAT(GF("MY")); |
|
|
|
String IPaddr; IPaddr.reserve(16); |
|
|
|
// wait for the response - this response can be very slow |
|
|
|
IPaddr = readResponse(30000); |
|
|
|
exitCommand(); |
|
|
|
IPaddr.trim(); |
|
|
|
return IPaddr; |
|
|
|
} |
|
|
|
|
|
|
|
IPAddress localIP() { |
|
|
|
return TinyGsmIpFromString(getLocalIP()); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* WiFi functions |
|
|
|
*/ |
|
|
@ -513,27 +579,18 @@ fail: |
|
|
|
} |
|
|
|
|
|
|
|
bool networkDisconnect() { |
|
|
|
return false; // Doesn't support disconnecting |
|
|
|
} |
|
|
|
|
|
|
|
String getLocalIP() { |
|
|
|
if (!commandMode()) return ""; // Return immediately |
|
|
|
sendAT(GF("MY")); |
|
|
|
String IPaddr; IPaddr.reserve(16); |
|
|
|
// wait for the response - this response can be very slow |
|
|
|
IPaddr = readResponse(30000); |
|
|
|
if (!commandMode()) return false; // return immediately |
|
|
|
sendAT(GF("NR0")); // Do a network reset in order to disconnect |
|
|
|
int res = (1 == waitResponse(5000)); |
|
|
|
writeChanges(); |
|
|
|
exitCommand(); |
|
|
|
return IPaddr; |
|
|
|
} |
|
|
|
|
|
|
|
IPAddress localIP() { |
|
|
|
return TinyGsmIpFromString(getLocalIP()); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* GPRS functions |
|
|
|
*/ |
|
|
|
bool gprsConnect(const char* apn, const char* user = "", const char* pw = "") { |
|
|
|
bool gprsConnect(const char* apn, const char* user = "", const char* pwd = "") { |
|
|
|
if (!commandMode()) return false; // Return immediately |
|
|
|
sendAT(GF("AN"), apn); // Set the APN |
|
|
|
waitResponse(); |
|
|
@ -542,19 +599,29 @@ fail: |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool gprsDisconnect() { // TODO |
|
|
|
return false; |
|
|
|
bool gprsDisconnect() { |
|
|
|
if (!commandMode()) return false; // return immediately |
|
|
|
sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode |
|
|
|
int res = (1 == waitResponse(5000)); |
|
|
|
writeChanges(); |
|
|
|
sendAT(GF("AM0")); // Airplane mode off |
|
|
|
waitResponse(5000); |
|
|
|
writeChanges(); |
|
|
|
exitCommand(); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
bool isGprsConnected() { |
|
|
|
return isNetworkConnected(); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Messaging functions |
|
|
|
*/ |
|
|
|
|
|
|
|
void sendUSSD() { |
|
|
|
} |
|
|
|
void sendUSSD() TINY_GSM_ATTR_NOT_AVAILABLE; |
|
|
|
|
|
|
|
void sendSMS() { |
|
|
|
} |
|
|
|
void sendSMS() TINY_GSM_ATTR_NOT_IMPLEMENTED; |
|
|
|
|
|
|
|
bool sendSMS(const String& number, const String& text) { |
|
|
|
if (!commandMode()) return false; // Return immediately |
|
|
@ -577,6 +644,21 @@ fail: |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* 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: |
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
@ -590,8 +672,9 @@ private: |
|
|
|
{ |
|
|
|
sendAT(GF("LA"), host); |
|
|
|
while (stream.available() < 4) {}; // wait for any response |
|
|
|
strIP = streamReadUntil('\r'); // read result |
|
|
|
// DBG("<<< ", strIP); |
|
|
|
strIP = stream.readStringUntil('\r'); // read result |
|
|
|
strIP.trim(); |
|
|
|
DBG("<<< ", strIP); |
|
|
|
if (!strIP.endsWith(GF("ERROR"))) gotIP = true; |
|
|
|
delay(100); // short wait before trying again |
|
|
|
} |
|
|
@ -644,29 +727,9 @@ 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...); |
|
|
|
} |
|
|
|
|
|
|
|
int streamRead() { return stream.read(); } |
|
|
|
|
|
|
|
String streamReadUntil(char c) { |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
String return_string = stream.readStringUntil(c); |
|
|
|
return_string.trim(); |
|
|
|
return return_string; |
|
|
|
} |
|
|
|
|
|
|
|
void streamClear(void) { |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
while (stream.available()) { streamRead(); } |
|
|
|
while (stream.available()) { stream.read(); } |
|
|
|
} |
|
|
|
|
|
|
|
bool commandMode(int retries = 2) { |
|
|
@ -677,8 +740,8 @@ public: |
|
|
|
// Cannot send anything for 1 "guard time" before entering command mode |
|
|
|
// Default guard time is 1s, but the init fxn decreases it to 250 ms |
|
|
|
delay(guardTime); |
|
|
|
streamWrite(GF("+++")); // enter command mode |
|
|
|
// DBG("\r\n+++"); |
|
|
|
stream.print(GF("+++")); // enter command mode |
|
|
|
DBG("\r\n+++"); |
|
|
|
success = (1 == waitResponse(guardTime*2)); |
|
|
|
triesMade ++; |
|
|
|
} |
|
|
@ -699,16 +762,18 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
String readResponse(uint32_t timeout = 1000) { |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
unsigned long startMillis = millis(); |
|
|
|
while (!stream.available() && millis() - startMillis < timeout) {}; |
|
|
|
String res = streamReadUntil('\r'); // lines end with carriage returns |
|
|
|
// DBG("<<< ", res); |
|
|
|
String res = stream.readStringUntil('\r'); // lines end with carriage returns |
|
|
|
res.trim(); |
|
|
|
DBG("<<< ", res); |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename... Args> |
|
|
|
void sendAT(Args... cmd) { |
|
|
|
streamWrite("AT", cmd..., GSM_NL); |
|
|
|
stream.print("AT", cmd..., GSM_NL); |
|
|
|
stream.flush(); |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
// DBG("### AT:", cmd...); |
|
|
@ -731,7 +796,7 @@ public: |
|
|
|
do { |
|
|
|
TINY_GSM_YIELD(); |
|
|
|
while (stream.available() > 0) { |
|
|
|
int a = streamRead(); |
|
|
|
int a = stream.read(); |
|
|
|
if (a <= 0) continue; // Skip 0x00 bytes, just in case |
|
|
|
data += (char)a; |
|
|
|
if (r1 && data.endsWith(r1)) { |
|
|
@ -767,7 +832,7 @@ finish: |
|
|
|
data.replace(GSM_NL GSM_NL, GSM_NL); |
|
|
|
data.replace(GSM_NL, "\r\n "); |
|
|
|
if (data.length()) { |
|
|
|
// DBG("<<< ", data); |
|
|
|
DBG("<<< ", data); |
|
|
|
} |
|
|
|
} |
|
|
|
return index; |
|
|
@ -787,7 +852,7 @@ finish: |
|
|
|
return waitResponse(1000, r1, r2, r3, r4, r5); |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
protected: |
|
|
|
int guardTime; |
|
|
|
XBeeType beeType; |
|
|
|
Stream& stream; |
|
|
|