Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of MTS-Cellular by
Diff: Cellular/UIP.cpp
- Revision:
- 3:04046eebaef5
- Parent:
- 2:10e72dce251d
- Child:
- 4:1f63354b8d1b
diff -r 10e72dce251d -r 04046eebaef5 Cellular/UIP.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Cellular/UIP.cpp Tue May 20 15:21:06 2014 -0500 @@ -0,0 +1,816 @@ +#include "mbed.h" +#include "UIP.h" +#include "MTSText.h" +#include "CellUtils.h" + +using namespace mts; + +UIP::UIP() +: dcd(NULL), + dtr(NULL), + resetLine(NULL) +{ + io = NULL; + echoMode = true; + pppConnected = false; + mode = TCP; + socketOpened = false; + socketCloseable = true; + local_port = 0; + local_address = ""; + host_port = 0; +} + +UIP::~UIP() +{ + if (dtr != NULL) { + dtr->write(1); + } + + delete dcd; + delete dtr; + delete resetLine; +} + +bool UIP::init(MTSBufferedIO* io) +{ + if (io == NULL) { + return false; + } + this->io = io; + + test(); + // Reset radio to make sure it's in a good state and wait for it to come back + reset(); + test(); + + return SUCCESS; +} + +bool UIP::configureSignals(PinName DCD, PinName DTR, PinName RESET) +{ + //Set DCD - The radio will raise and lower this line + if (DCD != NC) { + dcd = new DigitalIn(DCD); + } + /* Set DTR - This line should be lowered when we want to talk to the radio and raised when we're done + * for now we will lower it in the constructor and raise it in the destructor. + */ + if (DTR != NC) { + dtr = new DigitalOut(DTR); + dtr->write(0); + } + //Set RESET - Set the hardware reset line to the radio + if (RESET != NC) { + resetLine = new DigitalOut(RESET); + } + return true; +} + +bool UIP::connect() +{ + //Check if socket is open + if(socketOpened) { + return true; + } + + //Check if already connected + if(isConnected()) { + return true; + } + + Timer tmr; + + //Check Registration: AT+CREG? == 0,1 + tmr.start(); + do { + Registration registration = getRegistration(); + if(registration != REGISTERED) { + printf("[WARNING] Not Registered [%d] ... waiting\r\n", (int)registration); + wait(1); + } else { + break; + } + } while(tmr.read() < 30); + + //Check RSSI: AT+CSQ + tmr.reset(); + do { + int rssi = getSignalStrength(); + printf("[DEBUG] Signal strength: %d\r\n", rssi); + if(rssi == 99) { + printf("[WARNING] No Signal ... waiting\r\n"); + wait(1); + } else { + break; + } + } while(tmr.read() < 30); + + //AT#CONNECTIONSTART: Make a PPP connection + printf("[DEBUG] Making PPP Connection Attempt. APN[%s]\r\n", apn.c_str()); + std::string pppResult = sendCommand("AT#CONNECTIONSTART", 120000); + std::vector<std::string> parts = Text::split(pppResult, "\r\n"); + + if(pppResult.find("Ok_Info_GprsActivation") != std::string::npos) { + if(parts.size() >= 2) { + local_address = parts[1]; + } + printf("[INFO] PPP Connection Established: IP[%s]\r\n", local_address.c_str()); + pppConnected = true; + + } else { + pppConnected = false; + } + + return pppConnected; +} + +void UIP::disconnect() +{ + //AT#CONNECTIONSTOP: Close a PPP connection + printf("[DEBUG] Closing PPP Connection\r\n"); + + if(socketOpened) { + close(); + } + + Code code = sendBasicCommand("AT#CONNECTIONSTOP", 10000); + if(code == SUCCESS) { + printf("[DEBUG] Successfully closed PPP Connection\r\n"); + } else { + printf("[ERROR] Closing PPP Connection [%d]. Continuing ...\r\n", (int)code); + } + + pppConnected = false; +} + +bool UIP::isConnected() +{ + //1) Check if APN was set + if(apn.size() == 0) { + printf("[DEBUG] APN is not set\r\n"); + return false; + } + + //1) Check that we do not have a live connection up + if(socketOpened) { + printf("[DEBUG] Socket is opened\r\n"); + return true; + } + //2) Query the radio + std::string result = sendCommand("AT#VSTATE", 3000); + if(result.find("CONNECTED") != std::string::npos) { + if(pppConnected == false) { + printf("[WARNING] Internal PPP state tracking differs from radio (DISCONNECTED:CONNECTED)\r\n"); + } + pppConnected = true; + } else { + if(pppConnected == true) { + //Find out what state is + size_t pos = result.find("STATE:"); + if(pos != std::string::npos) { + result = Text::getLine(result, pos + sizeof("STATE:"), pos); + printf("[WARNING] Internal PPP state tracking differs from radio (CONNECTED:%s)\r\n", result.c_str()); + } else { + printf("[ERROR] Unable to parse radio state: [%s]\r\n", result.c_str()); + } + + } + pppConnected = false; + } + + return pppConnected; +} + +bool UIP::bind(unsigned int port) +{ + if(socketOpened) { + printf("[ERROR] socket is open. Can not set local port\r\n"); + return false; + } + if(port > 65535) { + printf("[ERROR] port out of range (0-65535)\r\n"); + return false; + } + local_port = port; + return true; +} + +bool UIP::open(const std::string& address, unsigned int port, Mode mode) +{ + char buffer[256] = {0}; + Code portCode, addressCode; + + //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 || this->mode != mode) { + if(this->mode == TCP) { + printf("[ERROR] TCP socket already opened [%s:%d]\r\n", host_address.c_str(), host_port); + } else { + printf("[ERROR] UDP socket already opened [%s:%d]\r\n", host_address.c_str(), host_port); + } + return false; + } + + printf("[DEBUG] Socket already opened\r\n"); + return true; + } + + //2) Check Parameters + if(port > 65535) { + printf("[ERROR] port out of range (0-65535)\r\n"); + return false; + } + + //3) Check PPP connection + if(!isConnected()) { + printf("[ERROR] PPP not established. Attempting to connect\r\n"); + if(!connect()) { + printf("[ERROR] PPP connection failed\r\n"); + return false; + } else { + printf("[DEBUG] PPP connection established\r\n"); + } + } + + //Set Local Port + if(local_port != 0) { + //Attempt to set local port + sprintf(buffer, "AT#OUTPORT=%d", local_port); + Code code = sendBasicCommand(buffer, 1000); + if(code != SUCCESS) { + printf("[WARNING] Unable to set local port (%d) [%d]\r\n", local_port, (int) code); + } + } + + //Set TCP/UDP parameters + if(mode == TCP) { + if(socketCloseable) { + Code code = sendBasicCommand("AT#DLEMODE=1,1", 1000); + if(code != SUCCESS) { + printf("[WARNING] Unable to set socket closeable [%d]\r\n", (int) code); + } + } + sprintf(buffer, "AT#TCPPORT=1,%d", port); + portCode = sendBasicCommand(buffer, 1000); + addressCode = sendBasicCommand("AT#TCPSERV=1,\"" + address + "\"", 1000); + } else { + if(socketCloseable) { + Code code = sendBasicCommand("AT#UDPDLEMODE=1", 1000); + if(code != SUCCESS) { + printf("[WARNING] Unable to set socket closeable [%d]\r\n", (int) code); + } + } + sprintf(buffer, "AT#UDPPORT=%d", port); + portCode = sendBasicCommand(buffer, 1000); + addressCode = sendBasicCommand("AT#UDPSERV=\"" + address + "\"", 1000); + } + + if(portCode == SUCCESS) { + host_port = port; + } else { + printf("[ERROR] Host port could not be set\r\n"); + } + + if(addressCode == SUCCESS) { + host_address = address; + } else { + printf("[ERROR] Host address could not be set\r\n"); + } + + // Try and Connect + std::string sMode; + std::string sOpenSocketCmd; + if(mode == TCP) { + sOpenSocketCmd = "AT#OTCP=1"; + sMode = "TCP"; + } else { + sOpenSocketCmd = "AT#OUDP"; + sMode = "UDP"; + } + + string response = sendCommand(sOpenSocketCmd, 30000); + if (response.find("Ok_Info_WaitingForData") != string::npos) { + printf("[INFO] Opened %s Socket [%s:%d]\r\n", sMode.c_str(), address.c_str(), port); + socketOpened = true; + } else { + printf("[WARNING] Unable to open %s Socket [%s:%d]\r\n", sMode.c_str(), address.c_str(), port); + socketOpened = false; + } + + return socketOpened; +} + +bool UIP::isOpen() +{ + if(io->readable()) { + printf("[DEBUG] Assuming open, data available to read.\n\r"); + return true; + } + return socketOpened; +} + +bool UIP::close() +{ + if(io == NULL) { + printf("[ERROR] MTSBufferedIO not set\r\n"); + return false; + } + + if(!socketOpened) { + printf("[WARNING] Socket close() called, but socket was not open\r\n"); + return true; + } + + if(!socketCloseable) { + printf("[ERROR] Socket is not closeable\r\n"); + return false; + } + + + + if(io->write(ETX, 1000) != 1) { + printf("[ERROR] Timed out attempting to close socket\r\n"); + return false; + } + + 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(); + + socketOpened = false; + return true; +} + +int UIP::read(char* data, int max, int timeout) +{ + if(io == NULL) { + printf("[ERROR] MTSBufferedIO not set\r\n"); + return -1; + } + + //Check that nothing is in the rx buffer + if(!socketOpened && !io->readable()) { + printf("[ERROR] Socket is not open\r\n"); + return -1; + } + + int bytesRead = 0; + + if(timeout >= 0) { + bytesRead = io->read(data, max, static_cast<unsigned int>(timeout)); + } else { + bytesRead = io->read(data, max); + } + + if(bytesRead > 0 && socketCloseable) { + //Remove escape characters + int index = 0; + bool escapeFlag = false; + for(int i = 0; i < bytesRead; i++) { + if(data[i] == DLE || data[i] == ETX) { + if(escapeFlag == true) { + //This character has been escaped + escapeFlag = false; + } else if(data[bytesRead] == DLE) { + //Found escape character + escapeFlag = true; + continue; + } else { + //ETX sent without escape -> Socket closed + printf("[INFO] Read ETX character without DLE escape. Socket closed\r\n"); + socketOpened = false; + continue; + } + } + + if(index != i) { + data[index] = data[i]; + } + index++; + } + bytesRead = index; + } + + //Scan for socket closed message + for(size_t i = 0; i < bytesRead; i++) { + if(data[i] == 'O') { + if(strstr(&data[i], "Ok_Info_SocketClosed")) { + printf("[INFO] Found socket closed message. Socket closed\r\n"); + //Close socket and Cut Off End of Message + socketOpened = false; + data[i] = '\0'; + bytesRead = i; + break; + } + } + } + return bytesRead; +} + +int UIP::write(const char* data, int length, int timeout) +{ + if(io == NULL) { + printf("[ERROR] MTSBufferedIO not set\r\n"); + return -1; + } + + if(!socketOpened) { + printf("[ERROR] Socket is not open\r\n"); + 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 UIP::readable() +{ + if(io == NULL) { + printf("[WARNING] MTSBufferedIO not set\r\n"); + return 0; + } + if(!socketOpened && !io->readable()) { + printf("[WARNING] Socket is not open\r\n"); + return 0; + } + return io->readable(); +} + +unsigned int UIP::writeable() +{ + if(io == NULL) { + printf("[WARNING] MTSBufferedIO not set\r\n"); + return 0; + } + if(!socketOpened) { + printf("[WARNING] Socket is not open\r\n"); + return 0; + } + + return io->writeable(); +} + +bool UIP::setDeviceIP(std::string address) +{ + if (address.compare("DHCP") == 0) { + return true; + } else { + printf("[WARNING] Radio does not support static IPs, using DHCP.\n\r"); + return false; + } +} + +void UIP::reset() +{ + disconnect(); + Code code = sendBasicCommand("AT#RESET=0", 10000); + if(code != SUCCESS) { + printf("[ERROR] Socket Modem did not accept RESET command\n\r"); + } else { + printf("[WARNING] Socket Modem is resetting, allow 30 seconds for it to come back\n\r"); + } +} + +std::string UIP::getDeviceIP() +{ + return local_address; +} + +Code UIP::echo(bool state) +{ + Code code; + if (state) { + code = sendBasicCommand("ATE0", 1000); + echoMode = (code == SUCCESS) ? false : echoMode; + } else { + code = sendBasicCommand("ATE1", 1000); + echoMode = (code == SUCCESS) ? true : echoMode; + } + return code; +} + +Code UIP::setApn(const std::string& apn) +{ + Code code = sendBasicCommand("AT#APNSERV=\"" + apn + "\"", 1000); + if (code != SUCCESS) { + return code; + } + this->apn = apn; + return code; +} + + +Code UIP::setDns(const std::string& primary, const std::string& secondary) +{ + return sendBasicCommand("AT#DNS=1," + primary + "," + secondary, 1000); +} + +bool UIP::ping(const std::string& address) +{ + char buffer[256] = {0}; + Code code; + + code = sendBasicCommand("AT#PINGREMOTE=\"" + address + "\"", 1000); + if (code != SUCCESS) { + return false; + } + + sprintf(buffer, "AT#PINGNUM=%d", 1); + code = sendBasicCommand(buffer , 1000); + if (code != SUCCESS) { + return false; + } + + sprintf(buffer, "AT#PINGDELAY=%d", PINGDELAY); + code = sendBasicCommand(buffer , 1000); + if (code != SUCCESS) { + return false; + } + + std::string response; + for (int i = 0; i < PINGNUM; i++) { + response = sendCommand("AT#PING", PINGDELAY * 1000); + if (response.find("alive") != std::string::npos) { + return true; + } + } + return false; +} + +Code UIP::setSocketCloseable(bool enabled) +{ + if(socketCloseable == enabled) { + return SUCCESS; + } + + if(socketOpened) { + printf("[ERROR] socket is already opened. Can not set closeable\r\n"); + return ERROR; + } + + socketCloseable = enabled; + + return SUCCESS; +} + +Code UIP::sendBasicCommand(const std::string& command, unsigned int timeoutMillis, char esc) +{ + if(socketOpened) { + printf("[ERROR] socket is open. Can not send AT commands\r\n"); + return ERROR; + } + + string response = sendCommand(command, timeoutMillis, esc); + if (response.size() == 0) { + return NO_RESPONSE; + } else if (response.find("OK") != string::npos) { + return SUCCESS; + } else if (response.find("ERROR") != string::npos) { + return ERROR; + } else { + return FAILURE; + } +} + +string UIP::sendCommand(const std::string& command, unsigned int timeoutMillis, char esc) +{ + if(io == NULL) { + printf("[ERROR] MTSBufferedIO not set\r\n"); + return ""; + } + if(socketOpened) { + printf("[ERROR] socket is open. Can not send AT commands\r\n"); + return ""; + } + + io->rxClear(); + io->txClear(); + std::string result; + + //Attempt to write command + if(io->write(command.data(), command.size(), timeoutMillis) != command.size()) { + //Failed to write command + if (command != "AT" && command != "at") { + printf("[ERROR] failed to send command to radio within %d milliseconds\r\n", timeoutMillis); + } + return ""; + } + + //Send Escape Character + if (esc != 0x00) { + if(io->write(esc, timeoutMillis) != 1) { + if (command != "AT" && command != "at") { + printf("[ERROR] failed to send character '%c' (0x%02X) to radio within %d milliseconds\r\n", esc, esc, timeoutMillis); + } + return ""; + } + } + + int timer = 0; + size_t previous = 0; + char tmp[256]; + tmp[255] = 0; + bool started = !echoMode; + bool done = false; + do { + wait(0.1); + timer += 100; + + previous = result.size(); + //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(!started) { + //In Echo Mode (Command will have echo'd + 2 characters for \r\n) + if(result.size() > command.size() + 2) { + started = true; + } + } else { + done = (result.size() == previous); + } + if(timer >= timeoutMillis) { + if (command != "AT" && command != "at") { + printf("[WARNING] sendCommand [%s] timed out after %d milliseconds\r\n", command.c_str(), timeoutMillis); + } + done = true; + } + } while (!done); + + return result; +} + +Code UIP::sendSMS(const Sms& sms) +{ + return sendSMS(sms.phoneNumber, sms.message); +} + +Code UIP::sendSMS(const std::string& phoneNumber, const std::string& message) +{ + Code code = sendBasicCommand("AT+CMGF=1", 1000); + if (code != SUCCESS) { + return code; + } + string cmd = "AT+CMGS=\"+"; + cmd.append(phoneNumber); + cmd.append("\""); + string response1 = sendCommand(cmd, 1000); + if (response1.find('>') == string::npos) { + return NO_RESPONSE; + } + wait(.2); + string response2 = sendCommand(message, 4000, CTRL_Z); + printf("SMS Response: %s\r\n", response2.c_str()); + if (response2.find("+CMGS:") == string::npos) { + return FAILURE; + } + return SUCCESS; +} + +std::vector<Cellular::Sms> UIP::getReceivedSms() +{ + int smsNumber = 0; + std::vector<Sms> vSms; + std::string received = sendCommand("AT+CMGL=\"ALL\"", 4000); + size_t pos = received.find("+CMGL: "); + + while (pos != std::string::npos) { + Cellular::Sms sms; + std::string line(Text::getLine(received, pos, pos)); + //printf("[DEBUG] Top of SMS Parse Loop. LINE[%s]\r\n", line.c_str()); + if(line.find("+CMGL: ") == std::string::npos) { + continue; + } + + //Start of SMS message + std::vector<std::string> vSmsParts = Text::split(line, ','); + if(vSmsParts.size() != 6) { + printf("[WARNING] Expected 6 commas. SMS[%d] DATA[%s]. Continuing ...\r\n", smsNumber, line.c_str()); + continue; + } + + sms.phoneNumber = vSmsParts[2]; + sms.timestamp = vSmsParts[4] + ", " + vSmsParts[5]; + + if(pos == std::string::npos) { + printf("[WARNING] Expected SMS body. SMS[%d]. Leaving ...\r\n", smsNumber); + break; + } + //Check for the start of the next SMS message + size_t bodyEnd = received.find("\r\n+CMGL: ", pos); + if(bodyEnd == std::string::npos) { + //printf("[DEBUG] Parsing Last SMS. SMS[%d]\r\n", smsNumber); + //This must be the last SMS message + bodyEnd = received.find("\r\n\r\nOK", pos); + } + + //Safety check that we found the boundary of this current SMS message + if(bodyEnd != std::string::npos) { + sms.message = received.substr(pos, bodyEnd - pos); + } else { + sms.message = received.substr(pos); + printf("[WARNING] Expected to find end of SMS list. SMS[%d] DATA[%s].\r\n", smsNumber, sms.message.c_str()); + } + vSms.push_back(sms); + pos = bodyEnd; + //printf("[DEBUG] Parsed SMS[%d]. Starting Next at position [%d]\r\n", smsNumber, pos); + smsNumber++; + } + printf("Received %d SMS\r\n", smsNumber); + return vSms; +} + +Code UIP::deleteOnlyReceivedReadSms() +{ + return sendBasicCommand("AT+CMGD=1,1", 1000); +} + +Code UIP::deleteAllReceivedSms() +{ + return sendBasicCommand("AT+CMGD=1,4", 1000); +}