Cellular library for MTS Socket Modem Arduino Shield devices from Multi-Tech Systems
Dependents: mtsas mtsas mtsas mtsas
Revision 30:1326b623919a, committed 2014-07-14
- Comitter:
- Vanger
- Date:
- Mon Jul 14 17:33:24 2014 +0000
- Parent:
- 29:edc613ed3f2e
- Child:
- 31:529db15abda7
- Commit message:
- For EasyIP.cpp:; Made sendEscapeCommand() and socketCheck() socket closed verification; connect(), disconnect(), and isConnected() were tweaked; IMPL. bind(),open(),isOpen(),close(),read(),write(),readable(),writeable(),reset(); Fixed bug in CellUtils.h;
Changed in this revision
--- a/Cellular/CellularFactory.cpp Wed Jul 02 15:06:03 2014 +0000 +++ b/Cellular/CellularFactory.cpp Mon Jul 14 17:33:24 2014 +0000 @@ -11,7 +11,7 @@ std::string reply; Cellular::Radio type; Cellular* cell; - + /* wait for radio to get into a good state */ while (true) { if (sendCommand(io, "AT", 1000).find("OK") != string::npos) { @@ -20,14 +20,13 @@ } else { logTrace("waiting on radio..."); } - wait(1); } - + /* "ATI4" gets us the model (HE910, DE910, etc) */ for (int i = 0; i < 5; i++) { model = sendCommand(io, "ATI4", 2000); - if (model.find("error") == string::npos && model.find("ERROR") == string::npos) { + if (model.find("error") == string::npos && model.find("ERROR") == string::npos && !model.empty()) { /* didn't get an error - keep going */ break; }
--- a/Cellular/EasyIP.cpp Wed Jul 02 15:06:03 2014 +0000 +++ b/Cellular/EasyIP.cpp Mon Jul 14 17:33:24 2014 +0000 @@ -12,6 +12,112 @@ using namespace mts; +bool EasyIP::sendEscapeCommand() +{ + //string Cellular::sendCommand(const std::string& command, unsigned int timeoutMillis, char esc) + if(io == NULL) { + logError("MTSBufferedIO not set"); + return false; + } + if(!socketOpened) { + logError("Socket is not open. Can not send AT escape sequence (+++)"); + return false; + } + + io->rxClear(); + io->txClear(); + + std::string result; + unsigned int timeoutMillis = 2000; + const int size_cmd = 3; + //Attempt to write command + wait(1); //Format for +++ command is 1 second wait, send +++, then another second wait + //1s wait after command is implemented as a polling function for 2 seconds + //Option: Could change wait periods to be longer/shorter (0-255)*50ms + if(io->write("+++", size_cmd, timeoutMillis) != size_cmd) { + //Failed to write command + logError("failed to send command to radio within %d milliseconds", timeoutMillis); + return false; + } + + int timer = 0; + char tmp[256]; + tmp[255] = 0; + bool done = false; + io->read(tmp,255,0); + bool exitmode = false; + + do { + wait(0.1); + timer += 100; + //Make a non-blocking read call by passing timeout of zero + int size = io->read(tmp,255,0); //1 less than allocated (timeout is instant) + if(size > 0) { + result.append(tmp, size); + } + if(result.find("OK") != std::string::npos) { + exitmode = true; + done = true; + } else if(result.find("NO CARRIER") != std::string::npos) { + exitmode = true; + done = true; + } else if(result.find("ERROR") != std::string::npos) { + exitmode = false; + done = true; + } + if(timer >= timeoutMillis) { + logDebug("Escape sequence [+++] timed out after %d milliseconds", timeoutMillis); + exitmode = true; + done = true; + } + } while (!done); + + return exitmode; +} + +bool EasyIP::socketCheck() { + bool status = false; + std::string socketInfo = "9"; //9 is unrecognized + std::vector<std::string> params; + + if(sendEscapeCommand()) { + socketOpened = false; + if(sendBasicCommand("AT", 1000) == MTS_SUCCESS) { + socketInfo = sendCommand("AT#SS=1", 2000); + if(socketInfo.find("OK") != std::string::npos) { + //Found valid response + params = Text::split(socketInfo, "\r\n"); + params = Text::split(params[1], ","); + socketInfo = params[1]; + //Check comparison of params[1] to response codes + } else { + logError("Could not determine socket status[%s]",socketInfo.c_str()); + socketInfo == "9"; //9 is unrecognized + } + } + } else { + status = false; //Return value of socketOpened when checking + } + //Check socket status query + if(socketInfo == "2" || socketInfo == "3" || socketInfo == "1" || socketInfo == "4") { + status = true; //2 and 3 are suspended connections + } else if(socketInfo == "0" || socketInfo == "5") { + status = false; //0 is closed socket, probably won't occur + } else { + logError("Could not determine socket status"); + status = false; //anything else is unknown status + } + + if(status) { + std::string reconnect = sendCommand("AT#SO=1", 2000); + if(reconnect.find("CONNECT") != std::string::npos || reconnect.find("OK") != std::string::npos) { + } else { + logError("Failed to resume socket connection"); + } + } + return status; +} + EasyIP::EasyIP(Radio type) { //Not sure how the construction process is done, @@ -78,7 +184,6 @@ } //Create an mbed timer object Timer tmr; - //Check Registration: AT+CREG? == 0,1 //(Does the AT command inside Cellular class) tmr.start(); @@ -90,15 +195,14 @@ } else { break; } - } while(tmr.read() < 30); - + } while(tmr.read() < 30); //Check RSSI: AT+CSQ //Does the command inside Cellular tmr.reset(); do { int rssi = getSignalStrength(); logDebug("Signal strength: %d", rssi); - if(rssi == 99) { + if((rssi == 99) || (rssi == -1)) { logTrace("No Signal ... waiting"); wait(1); } else { @@ -113,11 +217,11 @@ logDebug("Making PPP Connection Attempt"); } //The main thing going on; Sends the AT command to start a connection - //Assuming context is already stored in the modem...If not, will need to set context from classes/data - std::string pppResult = sendCommand("AT#SGACT=1,1", 120000); - + //Assuming context is already stored in the modem + std::string pppResult = sendCommand("AT#SGACT=1,1", 2000); + std::vector<std::string> parts; if(pppResult.find("OK") != std::string::npos) { - std::vector<std::string> parts = Text::split(pppResult, "\r\n"); + parts = Text::split(pppResult, "\r\n"); if(parts.size() >= 2) { parts = Text::split(parts[1], " "); local_address = parts[1]; @@ -126,7 +230,16 @@ pppConnected = true; } else { - pppConnected = false; + pppResult = sendCommand("AT#SGACT?", 2000); + if(pppResult.empty() || (pppResult.find("ERROR") != std::string::npos)) { + pppConnected = false; + } else { + if(pppResult.find("1,1") != std::string::npos) { + pppConnected = true; + } else { + pppConnected = false; + } + } } return pppConnected; @@ -134,32 +247,30 @@ void EasyIP::disconnect() { - bool complete = false; - Timer dctmr; //AT#SGACT=1,0: Close a PPP connection logDebug("Closing PPP Connection"); if(socketOpened) { - close(); //Calls another EasyIP - //function to close socket before disconnect + if(!close()) { //Calls another EasyIP function to close socket before disconnect + logDebug("Failed to close socket for disconnect"); + return; //Can't close connection without AT commands + //(and thus need socket closed) + } } //Sends AT#SGACT=1,0 command - dctmr.start(); - do { - if(sendBasicCommand("AT#SGACT=1,0", 10000) == MTS_SUCCESS) { - complete = true; - } else { - wait(0.050); - } - } while((!complete) && (dctmr.read() < 5)); - logDebug("Successfully closed PPP Connection"); + if(sendBasicCommand("AT#SGACT=1,0", 1000) == MTS_SUCCESS) { + pppConnected = false; + logDebug("Successfully closed PPP Connection"); + } pppConnected = false; //Cell will drop connection if we go silent + return; } bool EasyIP::isConnected() { std::string stateString; std::vector<std::string> pieces; - bool signal = false, regist = false, active = false, ping = false; + //state flags for various connection checks + bool signal = false, regist = false, active = false; //1) Check if APN was set if we're on an HSPA radio if (type == MTSMC_H5_IP || type == MTSMC_H5 || type == MTSMC_G3) { @@ -175,9 +286,10 @@ return true; } + //3) Query the radio //Check antenna signal - std::string reply = sendCommand("AT+CSQ", 1000); + std::string reply = sendCommand("AT+CSQ", 500); if(reply.empty() || (reply.find("ERROR") != std::string::npos)) { signal = false; } else { @@ -195,7 +307,7 @@ } //Check cell tower registration - reply = sendCommand("AT+CREG?", 1000); + reply = sendCommand("AT+CREG?", 500); if(reply.empty() || (reply.find("ERROR") != std::string::npos)) { regist = false; } else { @@ -212,11 +324,8 @@ } } - //Check internet connection through ping, admittedly uses data over cell network - ping = EasyIP::ping("www.google.com"); - //Check active mode (SGACT = 1,1) - reply = sendCommand("AT#SGACT?", 1000); + reply = sendCommand("AT#SGACT?", 500); if(reply.empty() || (reply.find("ERROR") != std::string::npos)) { active = false; } else { @@ -232,80 +341,350 @@ } } } - - if(ping) { - if(!pppConnected) { - logWarning("Internal PPP state tracking differs from radio (DISCONNECTED:CONNECTED)"); - } - pppConnected = true; - } else { - std::string stateStr; + //4) Determine radio state + if(regist && signal) { if(pppConnected) { - //New code for state from boolean values - if(regist && signal) { - if(active) { - stateString = "AUTHENTICATING"; - } else { - stateString = "IDLE"; - } - } else if(regist != signal) { - stateString = "CHECKING"; + if(active) { + if(ping()) { + stateString = "CONNECTED"; + pppConnected = true; } else { - stateString = "DISCONNECTED"; + stateString = "AUTHENTICATING"; + pppConnected = true; + return false; //Return false instead of changing pppConnected due to the fact + //that it is connected to ppp, it just can't ping successfully } - logWarning("Internal PPP state tracking differs from radio (CONNECTED:%s)", stateString.c_str()); + } else { + stateString = "DISCONNECTING"; + pppConnected = false; + } + } else { + if(active) { + if(ping()) { + pppConnected = true; + logWarning("Internal PPP state tracking differs from radio (DISCONNECTED:CONNECTED)"); + stateString = "CONNECTED"; + } else { + stateString = "CONNECTING"; + } + } else { + stateString = "IDLE"; + } } + } else if(regist != signal) { + stateString = "CHECKING"; pppConnected = false; + } else if(!regist && !signal) { + stateString = "DISCONNECTED"; + pppConnected = false; + } + //Log results if necessary + if(stateString != "CONNECTED") { + logWarning("Internal PPP state tracking differs from radio (CONNECTED:%s)",stateString.c_str()); } return pppConnected; } + //Binds the socket to a specific port if able bool EasyIP::bind(unsigned int port) { - - + if(socketOpened) { + logError("socket is open. Can not set local port"); + return false; + } + if(port > 65535) { + logError("port out of range (0-65535)"); + return false; + } + local_port = port; return true; } bool EasyIP::open(const std::string& address, unsigned int port, Mode mode) { - + char sOpenSocketCmd[256] = {0}; //String for AT command + std::string sMode = ""; + int typeSocket; + + //1) Check that we do not have a live connection up + if(socketOpened) { + //Check that the address, port, and mode match + if(host_address != address || host_port != port || socketMode != mode) { + if(socketMode == TCP) { + logError("TCP socket already opened [%s:%d]", host_address.c_str(), host_port); + } else { + logError("UDP socket already opened [%s:%d]", host_address.c_str(), host_port); + } + return false; + } + + logDebug("Socket already opened"); + return true; + } + + //2) Check Parameters + if(port > 65535) { + logError("port out of range (0-65535)"); + return false; + } + + //3) Check PPP connection + if(!isConnected()) { + logError("PPP not established. Attempting to connect"); + if(!connect()) { + logError("PPP connection failed"); + return false; + } else { + logDebug("PPP connection established"); + } + } + //No way to "set" port except on socket call; + //Going to need to warn if local_port was not set. + if(!local_port) { + logDebug("No local port was set: 0"); + } + + //4) Set escape sequence to not be transmitted + if(sendBasicCommand("AT#SKIPESC=1", 2000) != MTS_SUCCESS) { + logWarning("Failed to disable escape sequence transmission on data mode suspension"); + } + + if(mode == TCP) { + typeSocket = 0; + sMode = "TCP"; + } else { + typeSocket = 1; + sMode = "UDP"; + } + //4) Close Socket + sprintf(sOpenSocketCmd, "AT#SD=1,%d,%d,%s,0,%d,0", typeSocket, port, address.c_str(), local_port); + std::string response = sendCommand(sOpenSocketCmd, 5000); + + if(response.find("CONNECT") != std::string::npos) { + host_address = address; + host_port = port; + + logInfo("Opened %s Socket [%s:%d]", sMode.c_str(), address.c_str(), port); + socketOpened = true; + socketMode = mode; + } else { + logWarning("Unable to open %s Socket [%s:%d]", sMode.c_str(), address.c_str(), port); + socketOpened = false; + } + return socketOpened; } bool EasyIP::isOpen() { - + if(io->readable()) { + logDebug("Assuming open, data available to read.\n\r"); + return true; + } return socketOpened; } bool EasyIP::close() { - return true; + if(io == NULL) { + logError("MTSBufferedIO not set"); + return false; + } + + if(!socketOpened) { + logWarning("Socket close() called, but socket was not open"); + return true; + } + + if(!socketCloseable) { + logError("Socket is not closeable"); + return false; + } + + if(!sendEscapeCommand()) { + logError("Failed to exit online mode"); + return false; + } else { + socketOpened = false; + } + + if(sendBasicCommand("AT#SH=1", 2000) != MTS_SUCCESS) { + logDebug("Failed to close socket connection"); + } + + Timer tmr; + int counter = 0; + char tmp[256]; + tmr.start(); + do { + if(socketOpened == false) { + break; + } + read(tmp, 256, 1000); + } while(counter++ < 10); + + io->rxClear(); + io->txClear(); + + return !socketOpened; } int EasyIP::read(char* data, int max, int timeout) { - - return 1; + if(io == NULL) { + logError("MTSBufferedIO not set"); + return -1; + } + + //Check that nothing is in the rx buffer + if(!socketOpened && !io->readable()) { + logError("Socket is not open"); + return -1; + } + + int bytesRead = 0; + + + if(timeout >= 0) { + bytesRead = io->read(data, max, static_cast<unsigned int>(timeout)); + } else { + bytesRead = io->read(data, max); + } + + //Scan for socket closed message + for(size_t i = 0; i < bytesRead; i++) { + if(data[i] == 'N') { + if(strstr(&data[i], "NO CARRIER")) { + logTrace("Found socket closed message. Checking validity"); + //Close socket and Cut Off End of Message + socketOpened = socketCheck(); //Verifies legitimacy of socket disconnect + if(socketOpened) { + logDebug("Socket still open"); + continue; + } else { + logDebug("Socket closed"); + data[i] = '\0'; + bytesRead = i; + break; + } + } + } + } + return bytesRead; } int EasyIP::write(const char* data, int length, int timeout) { - - return 1; + if(io == NULL) { + logError("MTSBufferedIO not set"); + return -1; + } + + if(!socketOpened) { + logError("Socket is not open"); + return -1; + } + + //In order to avoid allocating another buffer, capture indices of + //characters to escape during write + int specialWritten = 0; + std::vector<int> vSpecial; + if(socketCloseable) { + for(int i = 0; i < length; i++) { + if(data[i] == ETX || data[i] == DLE) { + //Push back index of special characters + vSpecial.push_back(i); + } + } + } + + int bytesWritten = 0; + if(timeout >= 0) { + Timer tmr; + tmr.start(); + do { + int available = io->writeable(); + if (available > 0) { + if(specialWritten < vSpecial.size()) { + //Check if current index is at a special character + if(bytesWritten == vSpecial[specialWritten]) { + if(available < 2) { + //Requires at least two bytes of space + wait(0.05); + continue; + } + //Ready to write special character + if(io->write(DLE)) { + specialWritten++; + if(io->write(data[bytesWritten])) { + bytesWritten++; + } + } else { + //Unable to write escape character, try again next round + wait(0.05); + } + } else { + //We want to write all the way up to the next special character + int relativeIndex = vSpecial[specialWritten] - bytesWritten; + int size = MIN(available, relativeIndex); + bytesWritten += io->write(&data[bytesWritten], size); + } + } else { + int size = MIN(available, length - bytesWritten); + bytesWritten += io->write(&data[bytesWritten], size); + } + } else { + wait(0.05); + } + } while (tmr.read_ms() <= timeout && bytesWritten < length); + } else { + for(int i = 0; i < vSpecial.size(); i++) { + //Write up to the special character, then write the special character + int size = vSpecial[i] - bytesWritten; + int currentWritten = io->write(&data[bytesWritten], size); + bytesWritten += currentWritten; + if(currentWritten != size) { + //Failed to write up to the special character. + return bytesWritten; + } + if(io->write(DLE) && io->write(data[bytesWritten])) { + bytesWritten++; + } else { + //Failed to write the special character. + return bytesWritten; + } + } + + bytesWritten = io->write(&data[bytesWritten], length - bytesWritten); + } + + return bytesWritten; } unsigned int EasyIP::readable() { - + if(io == NULL) { + logWarning("MTSBufferedIO not set"); + return 0; + } + if(!socketOpened && !io->readable()) { + logWarning("Socket is not open"); + return 0; + } return io->readable(); } unsigned int EasyIP::writeable() { - + if(io == NULL) { + logWarning("MTSBufferedIO not set"); + return 0; + } + if(!socketOpened) { + logWarning("Socket is not open"); + return 0; + } return io->writeable(); } @@ -338,6 +717,13 @@ void EasyIP::reset() { + disconnect(); + if(sendBasicCommand("AT#REBOOT", 10000) != MTS_SUCCESS) { + logError("Socket Modem did not accept RESET command\n\r"); + } else { + logWarning("Socket Modem is resetting, allow 30 seconds for it to come back\n\r"); + return; + } } std::string EasyIP::getDeviceIP() @@ -368,27 +754,22 @@ int Timeout=0; //Format parameters for sending to radio - sprintf(buffer, "AT#PING=%s,1,32,%d", address.c_str(), (5*PINGDELAY)); + sprintf(buffer, "AT#PING=%s,1,32,%d", address.c_str(), (PINGDELAY*10)); for(int pngs=0; pngs<PINGNUM; pngs++) { - std::string response = sendCommand(buffer, (PINGDELAY*1000)); //Send 1 ping - //printf("Response [%s]\n", response.c_str()); //remove + std::string response = sendCommand(buffer, (PINGDELAY*1010)); //Send 1 ping if(response.empty()) { - //printf("Response empty!\n"); //remove continue; //Skip current loop if send command fails } if(response.find("ERROR") != std::string::npos) { - //printf("ERROR found\n"); //remove continue; //Skip current loop if send command fails } parts = Text::split(response, "\r\n"); if(parts.size() < 2) { - //printf("Response newline-split size %d\n", parts.size()); //remove continue; } parts = Text::split(parts[1], ","); if(parts.size() < 4) { - //printf("Response comma-split size %d\n", parts.size()); //remove continue; } //Parse TTL and Timeout values @@ -409,6 +790,6 @@ //Pass 0 to disable socket closeable Code EasyIP::setSocketCloseable(bool enabled) { - + return MTS_SUCCESS; }
--- a/Cellular/EasyIP.h Wed Jul 02 15:06:03 2014 +0000 +++ b/Cellular/EasyIP.h Mon Jul 14 17:33:24 2014 +0000 @@ -25,6 +25,15 @@ */ class EasyIP : public Cellular //Inherits from Cellular. { +private: + /*Function that sends +++ to radio to exit data mode + returns true if successful exit from online mode, else false + */ + virtual bool sendEscapeCommand(); + /*Switches to command mode, queries socket connection status, + then returns true if there is an active socket connection, + else it returns false.*/ + virtual bool socketCheck(); public: /** This static function is used to create or get a reference to a * Cellular object. Cellular uses the singleton pattern, which means @@ -41,7 +50,6 @@ * @returns a reference to the single Cellular obect that has been created. */ EasyIP(Radio type); - /** Destructs a Cellular object and frees all related resources. */ ~EasyIP(); @@ -64,7 +72,7 @@ virtual int write(const char* data, int length, int timeout = -1); virtual unsigned int readable(); virtual unsigned int writeable(); - virtual bool ping(const std::string& address = "8.8.8.8"); //Can this default address be different? + virtual bool ping(const std::string& address = "8.8.8.8"); //Google DNS server virtual std::string getDeviceIP(); virtual bool setDeviceIP(std::string address = "DHCP");//What does this do? Run DHCP to configure the IP?
--- a/Utils/CellUtils.h Wed Jul 02 15:06:03 2014 +0000 +++ b/Utils/CellUtils.h Mon Jul 14 17:33:24 2014 +0000 @@ -84,7 +84,7 @@ if(size > 0) { result.append(tmp, size); } - done = (result.size() == previous); + done = (result.size() == previous && previous > 0); if(timer >= timeoutMillis) { if (command != "AT" && command != "at") { logWarning("sendCommand [%s] timed out after %d milliseconds", command.c_str(), timeoutMillis);