From bd14270277badc0f4d503a6be470fc3c98d4b81f Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 28 Aug 2019 16:29:08 -0400 Subject: [PATCH] Doing a better job of detecting XBee socket state --- src/TinyGsmClientXBee.h | 200 +++++++++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 35 deletions(-) diff --git a/src/TinyGsmClientXBee.h b/src/TinyGsmClientXBee.h index f18a66f..5d14c96 100644 --- a/src/TinyGsmClientXBee.h +++ b/src/TinyGsmClientXBee.h @@ -120,6 +120,8 @@ public: virtual void stop(uint32_t maxWaitMs) { at->streamClear(); // Empty anything in the buffer + // empty the saved currently-in-use destination address + at->savedOperatingIP = IPAddress(0, 0, 0, 0); at->commandMode(); // For WiFi models, there's no direct way to close the socket. This is a // hack to shut the socket by setting the timeout to zero. @@ -216,6 +218,10 @@ public: return true; } return sock_connected; + // NOTE: We dont't check or return + // modemGetConnected() because we don't + // want to go into command mode. + // return at->modemGetConnected(); } virtual operator bool() { return connected(); } @@ -832,7 +838,31 @@ public: * Client related functions */ -protected: + protected: + + int16_t getConnectionIndicator() { + XBEE_COMMAND_START_DECORATOR(5, false) + sendAT(GF("CI")); + int16_t intRes = readResponseInt(); + XBEE_COMMAND_END_DECORATOR + return intRes; + } + + IPAddress getOperatingIP() { + String strIP; + strIP.reserve(16); + + XBEE_COMMAND_START_DECORATOR(5, IPAddress(0, 0, 0, 0)) + sendAT(GF("OD")); + strIP = stream.readStringUntil('\r'); // read result + strIP.trim(); + XBEE_COMMAND_END_DECORATOR + + if (strIP != "" && strIP != GF("ERROR")) { + return TinyGsmIpFromString(strIP); + } else + return IPAddress(0, 0, 0, 0); + } IPAddress lookupHostIP(const char* host, int timeout_s = 45) { String strIP; @@ -867,8 +897,6 @@ protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) { - unsigned long startMillis = millis(); - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; bool retVal = false; XBEE_COMMAND_START_DECORATOR(5, false) @@ -885,7 +913,7 @@ protected: // If we now have a valid IP address, use it to connect if (savedHostIP != IPAddress(0,0,0,0)) { // Only re-set connection information if we have an IP address - retVal = modemConnect(savedHostIP, port, mux, ssl, timeout_ms - (millis() - startMillis)); + retVal = modemConnect(savedHostIP, port, mux, ssl); } XBEE_COMMAND_END_DECORATOR @@ -893,13 +921,19 @@ protected: return retVal; } - bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) { - + bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, + bool ssl = false, int timeout_s = 75) { bool success = true; - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; + + if (timeout_s != 75) DBG("Timeout doesn't apply here."); + + // empty the saved currelty-in-use destination address + savedOperatingIP = IPAddress(0, 0, 0, 0); + XBEE_COMMAND_START_DECORATOR(5, false) - if (ip != savedIP) { // Can skip almost everything if there's no change in the IP address + if (ip != savedIP) { // Can skip almost everything if there's no + // change in the IP address savedIP = ip; // Set the newly requested IP address String host; host.reserve(16); host += ip[0]; @@ -926,11 +960,12 @@ protected: success &= writeChanges(); } - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { - if (modemGetConnected()) { - sockets[mux]->sock_connected = true; - break; - } + // we'll accept either unknown or connected + uint16_t ci = getConnectionIndicator(); + success &= (ci == 0x00 || ci == 0xFF || ci == 0x28); + + if (success) { + sockets[mux]->sock_connected = true; } XBEE_COMMAND_END_DECORATOR @@ -942,6 +977,17 @@ protected: if (mux != 0) DBG("XBee only supports 1 IP channel in transparent mode!"); stream.write((uint8_t*)buff, len); stream.flush(); + + // After a send, verify the outgoing ip if it isn't set + if (savedOperatingIP == IPAddress(0, 0, 0, 0)) { + modemGetConnected(); + } + // After sending several characters, also re-check + // NOTE: I'm intentionally not checking after every single character! + else if (len > 5) { + modemGetConnected(); + } + return len; } @@ -955,17 +1001,11 @@ protected: XBEE_COMMAND_START_DECORATOR(5, false) - // Verify that we're connected to the *right* IP address - // We might be connected - but to the wrong thing - // NOTE: In transparent mode, there is only one connection possible - no multiplex - // String strIP; strIP.reserve(16); - // sendAT(GF("DL")); - // strIP = stream.readStringUntil('\r'); // read result - // if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); - if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response - switch (beeType){ // The wifi be can only say if it's connected to the netowrk + switch (beeType){ + + // The wifi be can only say if it's connected to the netowrk case XBEE_S6B_WIFI: { RegStatus s = getRegistrationStatus(); XBEE_COMMAND_END_DECORATOR @@ -974,22 +1014,111 @@ protected: } return (s == REG_OK); // if it's connected, we hope the sockets are too } - default: { // Cellular XBee's - sendAT(GF("CI")); - int16_t intRes = readResponseInt(); + + // Cellular XBee's + default: { + int16_t ci = getConnectionIndicator(); + // Get the operating destination address + IPAddress od = getOperatingIP(); XBEE_COMMAND_END_DECORATOR - switch(intRes) { - case 0x00: // 0x00 = The socket is definitely open - case 0x28: // 0x28 = "Unknown." - case 0xFF: // 0xFF = No known status - this is always returned prior to sending data + + switch(ci) { + + // 0x00 = The socket is definitely open + case 0x00: { + savedOperatingIP = od; + // but it's possible the socket is set to the wrong place + if (od != IPAddress(0, 0, 0, 0) && od != savedIP) { + sockets[0]->stop(); + return false; + } return true; - case 0x02: // 0x02 = Invalid parameters (bad IP/host) - case 0x12: // 0x12 = DNS query lookup failure - case 0x25: // 0x25 = Unknown server - DNS lookup failed (0x22 for UDP socket!) - savedIP = IPAddress(0,0,0,0); // force a lookup next time! - default: // If it's anything else (inc 0x02, 0x12, and 0x25)... - sockets[0]->sock_connected = false; // ...it's definitely NOT connected + } + + // 0x28 = "Unknown." + // 0xFF = No known status - always returned prior to sending data + case 0x28: + case 0xFF: { + // If we previously had an operating destination and we no longer do, + // the socket must have closed + if (od == IPAddress(0, 0, 0, 0) && savedOperatingIP != IPAddress(0, 0, 0, 0)) { + savedOperatingIP = od; + sockets[0]->sock_connected = false; + return false; + } + // else if the operating destination exists, but is wrong + // we need to close and re-open + else if (od != IPAddress(0, 0, 0, 0) && od != savedIP) { + sockets[0]->stop(); + return false; + } + // else if the operating destination exists and matches, we're + // good to go + else if (od != IPAddress(0, 0, 0, 0) && od == savedIP) { + savedOperatingIP = od; + return true; + } + // If we never had an operating destination, then sock may be open + // but data never sent - this is the dreaded "we don't know" + else { + savedOperatingIP = od; + return true; + } + + // // Ask for information about any open sockets + // sendAT(GF("SI")); + // String open_socks = stream.readStringUntil('\r'); + // open_socks.replace(GSM_NL, ""); + // open_socks.trim(); + // if (open_socks != "") { + // // In transparent mode, only socket 0 should be possible + // sendAT(GF("SI0")); + // // read socket it + // String sock_id = stream.readStringUntil('\r'); + // // read socket state + // String sock_state = stream.readStringUntil('\r'); + // // read socket protocol (TCP/UDP) + // String sock_protocol = stream.readStringUntil('\r'); + // // read local port number + // String local_port = stream.readStringUntil('\r'); + // // read remote port number + // String remote_port = stream.readStringUntil('\r'); + // // read remote ip address + // String remoted_address = + // stream.readStringUntil('\r'); // read result + // stream.readStringUntil('\r'); // final carriage return + // } + } + + // 0x21 = User closed + // 0x27 = Connection lost + // If the connection is lost or timed out on our side, + // we force close so it can reopen + case 0x21 : + case 0x27 : { + Serial.println("Here!"); + sendAT(GF("TM")); // Get socket timeout + String timeoutUsed = readResponseString(5000L); + sendAT(GF("TM"), timeoutUsed); // Re-set socket timeout + waitResponse(5000L); // This response can be slow + } + + // 0x02 = Invalid parameters (bad IP/host) + // 0x12 = DNS query lookup failure + // 0x25 = Unknown server - DNS lookup failed (0x22 for UDP socket!) + case 0x02: + case 0x12: + case 0x25: { + savedIP = IPAddress(0, 0, 0, 0); // force a lookup next time! + } + + // If it's anything else (inc 0x02, 0x12, and 0x25)... + // it's definitely NOT connected + default: { + sockets[0]->sock_connected = false; + savedOperatingIP = od; return false; + } } } } @@ -1193,6 +1322,7 @@ protected: IPAddress savedIP; String savedHost; IPAddress savedHostIP; + IPAddress savedOperatingIP; bool inCommandMode; uint32_t lastCommandModeMillis; GsmClient* sockets[TINY_GSM_MUX_COUNT];