modified to get more signal info
Fork of WncControllerLibrary by
Diff: WncController.cpp
- Revision:
- 36:d1a98d5f2bbd
- Parent:
- 35:7c9d0f29ff7a
- Child:
- 37:92acf8c20e6d
- Child:
- 38:9a1a6f211eb4
--- a/WncController.cpp Thu Mar 09 00:58:50 2017 +0000 +++ b/WncController.cpp Thu Apr 06 21:42:30 2017 -0400 @@ -1,2127 +1,2128 @@ -/* - Copyright (c) 2016 Fred Kellerman - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - - @file WncController.cpp - @purpose Controls WNC 14A2A Cellular Modem - @version 1.0 - @date July 2016 - @author Fred Kellerman - - - An Example of usage: - - WncControllerK64F mdm(&wncPinList, &mdmUart, &debugUart); - - mdm.enableDebug(true, true); - - if (false == mdm.powerWncOn("m2m.com.attz", 60)) { - while(1); - } - - // ICCID and MSISDN - string iccid; string msisdn; - if (mdm.getICCID(&iccid) == true) { - if (mdm.convertICCIDtoMSISDN(iccid, &msisdn) == true) { - // Send an SMS message (must use 15-digit MISDN number!) - mdm.sendSMSText(msisdn.c_str(), "Hello from WNC Kit -> from WNC"); - } - } - - // Get an IP address setup for the socket #1 (0 indexed)) - if (true == mdm.resolveUrl(0, "www.att.com")) - { - // Report server IP - if (true == mdm.getIpAddr(0, ipAddrStr)) { - debugUart.puts("Server IP: "); - debugUart.puts(ipAddrStr); - debugUart.puts("\r\n"); - } - - // Open Socket #1, TCP=true resolved IP on port 80: - if (true == mdm.openSocket(0, 80, true)) { - // Write some data - const uint8_t * dataStr = "GET /index.html HTTP/1.0\r\nFrom: someuser@someuser.com\r\nUser-Agent: HTTPTool/1.0\r\n\r\n"; - if (true == mdm.write(0, dataStr, strlen((const char *)dataStr))) - { - const uint8_t * myBuf; - mdm.setReadRetries(0, 20); - uint32_t n = mdm.read(0, &myBuf); - if (n > 0) - debugUart.printf("Read %d chars: %s\r\n", n, myBuf); - else - debugUart.puts("No read data!\r\n"); - } - } - } - -*/ - - -#include <cstdlib> -#include <cctype> -#include <string.h> -#include "WncController.h" - -namespace WncController_fk { - -///////////////////////////////////////////////////// -// Static initializers -///////////////////////////////////////////////////// -WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS]; -const WncController::WncSocketInfo_s WncController::defaultSockStruct = { 0, false, "192.168.0.1", 80, 0, 25, true, 30 }; - -WncController::WncState_e WncController::m_sState = WNC_OFF; -uint16_t WncController::m_sCmdTimeoutMs = WNC_CMD_TIMEOUT_MS; -string WncController::m_sApnStr = "NULL"; -string WncController::m_sWncStr; -uint8_t WncController::m_sPowerUpTimeoutSecs = MAX_POWERUP_TIMEOUT; -bool WncController::m_sDebugEnabled = false; -bool WncController::m_sMoreDebugEnabled = false; -bool WncController::m_sCheckNetStatus = false; // Turn on internet status check between every command -const char * const WncController::INVALID_IP_STR = ""; -bool WncController::m_sReadyForSMS = false; - - -/** - * C++ version 0.4 char* style "itoa": - * Written by Lukás Chmela - * Released under GPLv3. - */ -static char* itoa(int64_t value, char* result, int base) -{ - // check that the base is valid - if ( base < 2 || base > 36 ) { - *result = '\0'; - return result; - } - - char* ptr = result, *ptr1 = result, tmp_char; - int64_t tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - if ( tmp_value < 0 ) - *ptr++ = '-'; - - *ptr-- = '\0'; - - while ( ptr1 < ptr ) { - tmp_char = *ptr; - *ptr-- = *ptr1; - *ptr1++ = tmp_char; - } - - return result; -} - -const char * WncController::_to_string(int64_t value) -{ - static char str[21]; // room for signed 64-bit + null - itoa(value, str, 10); - return (str); -} - -const char * WncController::_to_hex_string(uint8_t value) -{ - static char str[3]; // room for 8-bit + null - itoa(value, str, 16); - return (str); -} - -WncController::WncController(void) -{ - for(unsigned i; i<MAX_NUM_WNC_SOCKETS; i++) - m_sSock[i] = defaultSockStruct; -} - -void WncController::enableDebug(bool on, bool moreDebugOn) -{ - m_sDebugEnabled = on; - m_sMoreDebugEnabled = moreDebugOn; -} - -WncController::WncState_e WncController::getWncStatus(void) -{ - return (m_sState); -} - -int16_t WncController::getDbmRssi(void) -{ - int16_t rssi, ber; - if (at_getrssiber_wnc(&rssi, &ber) == true) - return (rssi); - else - return (99); -} - -int16_t WncController::get3gBer(void) -{ - int16_t rssi, ber; - if (at_getrssiber_wnc(&rssi, &ber) == true) - return (ber); - else - return (99); -} - -bool WncController::powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs) -{ - dbgPuts("Waiting for WNC to Initialize..."); - m_sPowerUpTimeoutSecs = powerUpTimeoutSecs; - m_sState = WNC_ON_NO_CELL_LINK; // Turn soft on to allow "AT" for init to be sent! - if (initWncModem(powerUpTimeoutSecs) == true) { - // Set the Apn - setApnName(apn); - if (false == softwareInitMdm()) { - dbgPuts("Software init failed!"); - m_sState = WNC_OFF; - } - } - else { - dbgPuts("Power up failed!"); - m_sState = WNC_OFF; - } - - return ((m_sState == WNC_ON) || (m_sState == WNC_ON_NO_CELL_LINK)); -} - -size_t WncController::sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout) -{ - string * respStr; - - if (sizeRespBuf > 0) { - AtCmdErr_e r = at_send_wnc_cmd(cmd, &respStr, ms_timeout); - strncpy(resp, respStr->c_str(), sizeRespBuf); - if (respStr->size() > sizeRespBuf) - dbgPuts("sendCustomCmd truncated!"); - - return (respStr->size()); - } - - dbgPuts("sendCustomCmd: would have overrun!"); - - return (0); -} - -bool WncController::pingUrl(const char * url) -{ - string ipAddr; - - if (true == at_dnsresolve_wnc(url, &ipAddr)) - return (pingIp(ipAddr.c_str())); - else - dbgPuts("pingUrl DNS resolve: failed!"); - - return (false); -} - -bool WncController::pingIp(const char * ip) -{ - if (true == at_ping_wnc(ip)) - return (true); - else - dbgPuts("pingIp: failed!"); - - return (false); -} - -bool WncController::getWncNetworkingStats(WncIpStats * s) -{ - return (at_get_wnc_net_stats(s)); -} - -bool WncController::getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR]) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) { - strncpy(myIpAddr, m_sSock[numSock].myIpAddressStr.c_str(), MAX_LEN_IP_STR); - myIpAddr[MAX_LEN_IP_STR - 1] = '\0'; - return (true); - } - else { - myIpAddr[0] = '\0'; - return (false); - } -} - -bool WncController::setApnName(const char * const apnStr) -{ - if (at_setapn_wnc(apnStr) == true) - { - m_sApnStr = apnStr; - return (true); - } - else - return (false); -} - -bool WncController::resolveUrl(uint16_t numSock, const char * url) -{ - bool cmdRes; - - if (numSock < MAX_NUM_WNC_SOCKETS) { - if (strlen(url) > 0) { - cmdRes = at_dnsresolve_wnc(url, &m_sSock[numSock].myIpAddressStr); - if (cmdRes == false) - dbgPuts("Cannot resolve URL!"); - return (cmdRes); - } - else - dbgPuts("Invalid URL"); - } - else - dbgPuts("Invalid Sock num!"); - - return (false); -} - -bool WncController::setIpAddr(uint16_t numSock, const char * ipStr) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) { - m_sSock[numSock].myIpAddressStr = ipStr; - return (true); - } - else { - dbgPuts("Bad socket num!"); - return (false); - } -} - -void WncController::setWncCmdTimeout(uint16_t toMs) -{ - m_sCmdTimeoutMs = toMs; -} - -bool WncController::openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec) -{ - if (resolveUrl(numSock, url) == true) - return (openSocket(numSock, port, tcp, timeOutSec)); - - return (false); -} - -bool WncController::openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec) -{ - if (setIpAddr(numSock, ipAddr) == true) - return (openSocket(numSock, port, tcp, timeOutSec)); - - return (false); -} - -bool WncController::openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) { - // IPV4 ip addr sanity check! - size_t lenIpStr = m_sSock[numSock].myIpAddressStr.size(); - if (lenIpStr < 7 || lenIpStr > 15) { - dbgPuts("Invalid IP Address!"); - return (false); - } - - // Already open ? Must close if want to re-open with new settings. - if (m_sSock[numSock].open == true) { - dbgPuts("Socket already open, close then re-open!"); - if (true == at_sockclose_wnc(m_sSock[numSock].numWncSock)) - m_sSock[numSock].open = false; - else - return (false); - } - - m_sSock[numSock].myPort = port; - m_sSock[numSock].isTcp = tcp; - m_sSock[numSock].timeOutSec = timeOutSec; - - int16_t numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), port, numSock, tcp, timeOutSec); - m_sSock[numSock].numWncSock = numWncSock; - if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS) - m_sSock[numSock].open = true; - else { - m_sSock[numSock].open = false; - dbgPuts("Socket open fail!!!!"); - - // If the modem is not responding don't bother it. - if (WNC_NO_RESPONSE != getWncStatus()) { - // Work-around. If the sock open fails it needs to be told - // to close. If 6 sock opens happen with a fail, it further - // crashes the WNC. Not sure why the sock won't open. - at_sockclose_wnc(m_sSock[numSock].numWncSock); - } - } - } - else { - dbgPuts("Bad socket num or IP!"); - return (false); - } - - return (m_sSock[numSock].open); -} - -bool WncController::sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp) -{ - bool result = true; - - AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, m_sSock[numSock].numWncSock, isTcp); - if (cmdRes != WNC_AT_CMD_OK) { - if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) - { - // This may throw away any data that hasn't been written out of the WNC - // but at this point with the way the WNC currently works we have - // no choice. - closeOpenSocket(numSock); - } - result = false; - } - - return (result); -} - -bool WncController::write(uint16_t numSock, const uint8_t * s, uint32_t n) -{ - bool result; - - if (numSock < MAX_NUM_WNC_SOCKETS) { - if (m_sSock[numSock].open == true) { - if (n <= MAX_WNC_WRITE_BYTES) { - result = sockWrite(s, n, numSock, m_sSock[numSock].isTcp); - } - else { - uint16_t rem = n % MAX_WNC_WRITE_BYTES; - while (n >= MAX_WNC_WRITE_BYTES) { - n -= MAX_WNC_WRITE_BYTES; - result = sockWrite(s, MAX_WNC_WRITE_BYTES, numSock, m_sSock[numSock].isTcp); - if (result == false) { - n = 0; - rem = 0; - dbgPuts("Sock write fail!"); - } - else - s += MAX_WNC_WRITE_BYTES; - } - if (rem > 0) - result = sockWrite(s, rem, numSock, m_sSock[numSock].isTcp); - } - } - else { - dbgPuts("Socket is closed for write!"); - result = false; - } - } - else { - dbgPuts("Bad socket num!"); - result = false; - } - - return (result); -} - -size_t WncController::read(uint16_t numSock, const uint8_t ** readBuf) -{ - static string theBuf; - string readStr; - - theBuf.erase(); // Clean-up from last time - - if (numSock < MAX_NUM_WNC_SOCKETS) { - if (m_sSock[numSock].open == true) { - uint8_t i = m_sSock[numSock].readRetries; - uint16_t to = m_sSock[numSock].readRetryWaitMs; - bool foundData = false; - do { - AtCmdErr_e cmdRes; - cmdRes = at_sockread_wnc(&readStr, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); - if (WNC_AT_CMD_OK == cmdRes) { - // This will let this loop read until the socket data is - // empty. If no data, then wait the retry amount of time. - if (readStr.size() > 0) { - theBuf += readStr; - foundData = true; - i = 1; - } - else { - // Once data is found start returning it asap - if (foundData == false) - waitMs(to); - } - } - else { - theBuf += readStr; // Append what if any we got before it errored. - dbgPuts("Sockread failed!"); - if (WNC_NO_RESPONSE == getWncStatus()) { - i = 0; - } - else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) - { - // This may throw away any data that hasn't been read out of the WNC - // but at this point with the way the WNC currently works we have - // no choice. - closeOpenSocket(numSock); - i = 0; - } - else - waitMs(to); - } - } while (i-- > 0); - } - else { - dbgPuts("Socket is closed for read"); - } - } - else { - dbgPuts("Bad socket num!"); - } - - *readBuf = (const uint8_t *)theBuf.c_str(); - - return (theBuf.size()); -} - -size_t WncController::read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen) -{ - uint32_t numCopied = 0; - - if (numSock < MAX_NUM_WNC_SOCKETS) { - if (m_sSock[numSock].open == true) { - uint8_t i = m_sSock[numSock].readRetries; - uint16_t to = m_sSock[numSock].readRetryWaitMs; - bool foundData = false; - uint16_t numRead; - do { - AtCmdErr_e cmdRes; - if (maxReadBufLen < MAX_WNC_READ_BYTES) - cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); - else - cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); - - if (WNC_AT_CMD_OK == cmdRes) { - // This will let this loop read until the socket data is - // empty. If no data, then wait the retry amount of time. - if (numRead > 0) { - foundData = true; - i = 1; - if (numRead <= maxReadBufLen) { - maxReadBufLen -= numRead; - numCopied += numRead; - readBuf += numRead; - } - else { - i = 0; // No more room for data! - dbgPutsNoTime("No more room for read data!"); - } - } - else { - // Once data is found start returning it asap - if (foundData == false) - waitMs(to); - } - } - else { - dbgPuts("Sockread failed!"); - if (WNC_NO_RESPONSE == getWncStatus()) { - i = 0; - } - else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) - { - // This may throw away any data that hasn't been read out of the WNC - // but at this point with the way the WNC currently works we have - // no choice. - closeOpenSocket(numSock); - i = 0; - } - else - waitMs(to); - } - } while ((i-- > 0) && (maxReadBufLen > 0)); - } - else { - dbgPuts("Socket is closed for read"); - } - } - else { - dbgPuts("Bad socket num!"); - } - - return (numCopied); -} - -void WncController::setReadRetries(uint16_t numSock, uint16_t retries) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) - m_sSock[numSock].readRetries = retries; - else - dbgPuts("Bad socket num!"); -} - -void WncController::setReadRetryWait(uint16_t numSock, uint16_t readRetryWaitMs) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) - m_sSock[numSock].readRetryWaitMs = readRetryWaitMs; - else - dbgPuts("Bad socket num!"); -} - -bool WncController::closeSocket(uint16_t numSock) -{ - if (numSock < MAX_NUM_WNC_SOCKETS) { - - if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) - dbgPuts("Sock close may not have closed!"); - - // Even with an error the socket could have closed, - // can't tell for sure so just soft close it for now. - m_sSock[numSock].open = false; - } - else { - dbgPuts("Bad socket num!"); - } - - return (m_sSock[numSock].open == false); -} - -size_t WncController::mdmGetline(string * buff, int timeout_ms) -{ - char chin = '\0'; - char chin_last; - size_t len = 0; - - startTimerB(); - while ((len <= MAX_LEN_WNC_CMD_RESPONSE) && (getTimerTicksB_mS() < timeout_ms)) { - if (charReady()) { - chin_last = chin; - chin = getc(); - if (isprint(chin)) { - *buff += chin; - len++; // Bound the copy length to something reaonable just in case - continue; - } - else if ((('\r' == chin_last) && ('\n' == chin)) || (('\n' == chin_last) && ('\r' == chin))) { - break; - } - } - } - stopTimerB(); - - if (len > MAX_LEN_WNC_CMD_RESPONSE) - dbgPuts("Max cmd length reply exceeded!"); - - return (len); -} - -bool WncController::softwareInitMdm(void) -{ - static bool reportStatus = true; - unsigned i; - - if (checkCellLink() == true) { - if (reportStatus == false) { - dbgPuts("Re-connected to cellular network!"); - reportStatus = true; - } - - // WNC has SIM and registered on network so - // soft initialize the WNC. - for (i = 0; i < WNC_SOFT_INIT_RETRY_COUNT; i++) - if (at_init_wnc() == true) - break; - - // If it did not respond try a hardware init - if (i == WNC_SOFT_INIT_RETRY_COUNT) - { - at_reinitialize_mdm(); - return (at_init_wnc(true)); // Hard reset occurred so make it go through the software init(); - } - else - return (true); - } - else - { - if (reportStatus == true) { - dbgPuts("Not connected to cellular network!"); - reportStatus = false; - } - return (false); - } -} - -WncController::AtCmdErr_e WncController::sendWncCmd(const char * const s, string ** r, int ms_timeout) -{ - if (checkCellLink() == false) { - static string noRespStr; - - // Save some run-time! - if (m_sDebugEnabled) - { - dbgPuts("FAIL send cmd: ", false); - if (m_sMoreDebugEnabled && m_sDebugEnabled) { - dbgPutsNoTime(s); - } - else { - size_t n = strlen(s); - if (n <= WNC_TRUNC_DEBUG_LENGTH) { - dbgPutsNoTime(s); - } - else { - string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); - truncStr += ".."; - truncStr += &s[n-(WNC_TRUNC_DEBUG_LENGTH/2)]; - dbgPutsNoTime(truncStr.c_str()); - } - } - } - - noRespStr.erase(); - *r = &noRespStr; - - return (WNC_AT_CMD_NO_CELL_LINK); - } - - if (m_sCheckNetStatus) - { - if (m_sMoreDebugEnabled) - dbgPuts("[---------- Network Status -------------"); - string * pRespStr; - at_send_wnc_cmd("AT@SOCKDIAL?", &pRespStr, m_sCmdTimeoutMs); - if (m_sMoreDebugEnabled) - dbgPuts("---------------------------------------]"); - } - - // If WNC ready, send user command - return (at_send_wnc_cmd(s, r, ms_timeout)); -} - -WncController::AtCmdErr_e WncController::at_send_wnc_cmd(const char * s, string ** r, int ms_timeout) -{ - // Save some run-time! - if (m_sDebugEnabled) - { - if (m_sMoreDebugEnabled) { - dbgPuts("TX: ", false); dbgPutsNoTime(s); - } - else { - if (m_sDebugEnabled) { // Save some run-time! - size_t n = strlen(s); - if (n <= WNC_TRUNC_DEBUG_LENGTH) { - dbgPuts("TX: ", false); dbgPutsNoTime(s); - } - else { - string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); - truncStr += ".."; - truncStr += &s[n - (WNC_TRUNC_DEBUG_LENGTH/2)]; - dbgPuts("TX: ", false); dbgPutsNoTime(truncStr.c_str()); - } - } - } - } - - AtCmdErr_e atResult = mdmSendAtCmdRsp(s, ms_timeout, &m_sWncStr); - *r = &m_sWncStr; // Return a pointer to the static string - - if (atResult != WNC_AT_CMD_TIMEOUT) { - // If a prior command timed out but a new one works then - // change the state back to ON. We don't know here in this - // method if the Cell Link is good so assume it is. When a command - // that depends on the cell link is made it will update the state. - if (m_sState == WNC_NO_RESPONSE) - m_sState = WNC_ON; - - // Save some run-time! - if (m_sDebugEnabled) - { - dbgPuts("RX: ", false); - if (m_sMoreDebugEnabled) { - dbgPutsNoTime(m_sWncStr.c_str()); - } - else { - if (m_sWncStr.size() <= WNC_TRUNC_DEBUG_LENGTH) { - dbgPutsNoTime(m_sWncStr.c_str()); - } - else { - string truncStr = m_sWncStr.substr(0,WNC_TRUNC_DEBUG_LENGTH/2) + ".."; - truncStr += m_sWncStr.substr(m_sWncStr.size() - (WNC_TRUNC_DEBUG_LENGTH/2), WNC_TRUNC_DEBUG_LENGTH/2); - dbgPutsNoTime(truncStr.c_str()); - } - } - } - } - else { - m_sState = WNC_NO_RESPONSE; - dbgPuts("AT Cmd TIMEOUT!"); - dbgPuts("RX: ", false); dbgPutsNoTime(m_sWncStr.c_str()); - } - - return (atResult); -} - -void WncController::closeOpenSocket(uint16_t numSock) -{ - // Try to open and close the socket - do { - dbgPuts("Try to close and re-open socket"); - if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) { - if (WNC_NO_RESPONSE == getWncStatus()) { - dbgPuts("No response for closeOpenSocket1"); - return ; - } - } - - int numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec); - m_sSock[numSock].numWncSock = numWncSock; - if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS) - m_sSock[numSock].open = true; - else { - m_sSock[numSock].open = false; - dbgPuts("Failed to re-open socket!"); - } - - if (WNC_NO_RESPONSE == getWncStatus()) { - dbgPuts("No response for closeOpenSocket2"); - return ; - } - } while (m_sSock[numSock].open == false); -} - -bool WncController::getICCID(string * iccid) -{ - if (at_geticcid_wnc(iccid) == false) { - dbgPuts("getICCID error!"); - return (false); - } - - return (true); -} - -bool WncController::at_geticcid_wnc(string * iccid) -{ - string * respStr; - - iccid->erase(); - - AtCmdErr_e r = at_send_wnc_cmd("AT%CCID", &respStr, m_sCmdTimeoutMs); - - if (r != WNC_AT_CMD_OK || respStr->size() == 0) - return (false); - - size_t pos = respStr->find("AT%CCID"); - if (pos == string::npos) - return (false); - - size_t posOK = respStr->rfind("OK"); - if (posOK == string::npos) - return (false); - - pos += 7; // Advanced to the number - *iccid = respStr->substr(pos, posOK - pos); - - return (true); -} - -bool WncController::convertICCIDtoMSISDN(const string & iccid, string * msisdn) -{ - msisdn->erase(); - - if (iccid.size() != 20 && iccid.size() != 19) { - dbgPuts("Invalid ICCID length!"); - return (false); - } - - *msisdn = "882350"; - - if (iccid.size() == 20) - *msisdn += iccid.substr(10,iccid.size() - 11); - else - *msisdn += iccid.substr(10,iccid.size() - 10); - - return (true); -} - -bool WncController::sendSMSText(const char * const phoneNum, const char * const text) -{ - if (at_sendSMStext_wnc(phoneNum, text) == true) - return (true); - else { - dbgPuts("sendSMSText: Failed!"); - return (false); - } -} - -bool WncController::readSMSLog(struct WncSmsList * log) -{ - string * logStr; - uint16_t i; - - if (at_readSMSlog_wnc(&logStr) == false) { - dbgPuts("readSMSLog: Failed!"); - return (false); - } - - // Clean slate - log->msgCount = 0; - - if (logStr->size() == 0) - return (false); - - // Pick out the stuff from the string and convert to struct - string s; - size_t pos2; - size_t pos = logStr->find("+CMGL:"); - - for(i=0; i<MAX_WNC_SMS_MSG_SLOTS; i++) { - // Start with a clean slate, let parsing fill out later. - log->e[i].unread = false; - log->e[i].incoming = false; - log->e[i].unsent = false; - log->e[i].pduMode = false; - log->e[i].msgReceipt = false; - - log->e[i].idx = logStr->at(pos + 7); - if (pos == string::npos) - return (false); - pos2 = logStr->find(",\"", pos); - if (pos2 == string::npos) { - // If the WNC acts wrong and receives a PDU mode - // SMS there will not be any quotes in the response, - // just take the whole reply and make it the message body for - // now, mark it as an unread message, set the pdu flag! - log->e[log->msgCount].unread = true; - log->e[log->msgCount].pduMode = true; - log->msgCount++; - - pos2 = logStr->find("+CMGL", pos + 5); - if (pos2 == string::npos) { - pos2 = logStr->find("OK", pos + 5); - if (pos2 == string::npos) { - dbgPuts("Strange SMS Log Ending!"); - return (false); - } - i = MAX_WNC_SMS_MSG_SLOTS; - } - log->e[log->msgCount].msg = logStr->substr(0, pos2 - pos); - pos = pos2; // for loop starts off expecting pos to point to next log msg - continue; - } - pos += 2; // Advance to the text we want - pos2 = logStr->find("\",", pos); - if ((pos2 == string::npos) || (pos >= pos2)) - return (false); - - // Setup attributes - s = logStr->substr(pos, pos2 - pos); - if (s.find("REC READ") != string::npos) - log->e[i].incoming = true; - if (s.find("REC UNREAD") != string::npos) { - log->e[i].unread = true; - log->e[i].incoming = true; - } - if (s.find("STO UNSENT") != string::npos) - log->e[i].unsent = true; - if (logStr->find(",,") == string::npos) - log->e[i].msgReceipt = true; - - // Tele number - pos2 = logStr->find(",\"", pos2); - if (pos2 == string::npos) - return (false); - pos2 += 2; // Advance to next field - pos = logStr->find("\",", pos2); - if ((pos == string::npos) || (pos2 > pos)) - return (false); - if (pos == pos2) - log->e[i].number.erase(); - else - log->e[i].number = logStr->substr(pos2, pos - pos2); - - // Date - pos = logStr->find(",\"", pos); - if (pos == string::npos) - return (false); - pos += 2; // Beginning of date field - pos2 = logStr->find(",", pos); // End of timestamp field - if ((pos2 == string::npos) || (pos > pos2)) - return (false); - if (pos == pos2) - log->e[i].date.erase(); - else - log->e[i].date = logStr->substr(pos, pos2 - pos); - - // Timestamp - pos = logStr->find("\",", pos2); // End of timestamp - if (pos == string::npos) - return (false); - pos2 += 1; // Beginning of time field - if (pos < pos2) - return (false); - if (pos == pos2) - log->e[i].time.erase(); - else - log->e[i].time = logStr->substr(pos2, pos - pos2); - - // Message field - - // We don't know how many messages we have so the next search - // could end with +CMGL or OK. - pos += 2; // Advanced to message text - pos2 = logStr->find("+CMGL", pos); - if (pos2 == string::npos) { - pos2 = logStr->find("OK", pos); - if (pos2 == string::npos) { - dbgPuts("Strange SMS Log Ending!"); - return (false); - } - i = MAX_WNC_SMS_MSG_SLOTS; // break - } - if (pos > pos2) - return (false); - if (pos == pos2) - log->e[log->msgCount].msg.erase(); - else - log->e[log->msgCount].msg = logStr->substr(pos, pos2 - pos); - - log->msgCount++; // Message complete - } - - return (true); -} - -bool WncController::readUnreadSMSText(struct WncSmsList * w, bool deleteRead) -{ - struct WncController::WncSmsList tmp; - - if (readSMSLog(&tmp) == false) - return (false); - - w->msgCount = 0; - for(uint16_t i = 0; i < tmp.msgCount; i++) { - if (tmp.e[i].unread == true) { - w->e[w->msgCount] = tmp.e[i]; - w->msgCount++; - if (deleteRead == true) { - // Clean up message that was copied out and read - deleteSMSTextFromMem(w->e[i].idx); - } - } - } - - return (w->msgCount > 0); -} - -size_t WncController::getSignalQuality(const char ** log) -{ - size_t n; - - n = at_getSignalQuality_wnc(log); - if (n == 0) - dbgPuts("readSMSText: Failed!"); - - return (n); -} - -size_t WncController::at_getSignalQuality_wnc(const char ** log) -{ - string * pRespStr; - static string logStr; - - logStr.erase(); - - if (at_send_wnc_cmd("AT%MEAS=\"0\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr = *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=0: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"1\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=1: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"2\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=2: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"3\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=3: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"4\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=4: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"5\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=5: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=8: failed!"); - - if (at_send_wnc_cmd("AT%MEAS=\"98\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - logStr += *pRespStr; - logStr += "\r\n"; - } - else - dbgPuts("AT%MEAS=98: failed!"); - - *log = logStr.c_str(); - - return (logStr.size()); -} - -bool WncController::getTimeDate(struct WncDateTime * tod) -{ - if (at_gettimedate_wnc(tod) == true) - return (true); - else { - dbgPuts("Get time date failed!"); - return (false); - } -} - -bool WncController::at_ping_wnc(const char * ip) -{ - string * pRespStr; - string cmdStr = "AT@PINGREQ=\""; - cmdStr += ip; - cmdStr += "\""; - return (at_send_wnc_cmd(cmdStr.c_str(), &pRespStr, WNC_PING_CMD_TIMEOUT_MS) == WNC_AT_CMD_OK); -} - -bool WncController::at_gettimedate_wnc(struct WncDateTime * tod) -{ - string * pRespStr; - char * pEnd; - - if (at_send_wnc_cmd("AT+CCLK?", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { - if (pRespStr->size() > 0) { - size_t pos1 = pRespStr->find("+CCLK:"); - if (pos1 != string::npos) { - pEnd = (char *)pRespStr->c_str() + pos1 + 8; - tod->year = strtol(pEnd, &pEnd, 10); - tod->month = strtol(pEnd+1, &pEnd, 10); - tod->day = strtol(pEnd+1, &pEnd, 10); - tod->hour = strtol(pEnd+1, &pEnd, 10); - tod->min = strtol(pEnd+1, &pEnd, 10); - tod->sec = strtol(pEnd+1, &pEnd, 10); - return (true); - } - } - } - - return (false); -} - -bool WncController::at_get_wnc_net_stats(WncIpStats * s) -{ - string * pRespStr; - AtCmdErr_e cmdRes = at_send_wnc_cmd("AT+CGCONTRDP=1", &pRespStr, m_sCmdTimeoutMs); - - if (WNC_AT_CMD_OK == cmdRes) { - if (pRespStr->size() > 0) { - memset((void*)s, '\0', sizeof(*s)); // Clean-up - string ss; - size_t pe; - size_t ps = pRespStr->rfind("\""); - if (ps != string::npos) { - ps += 2; // Skip the , after the " - pe = ps; - - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - - ss = pRespStr->substr(ps, pe - 1 - ps); - strncpy(s->ip, ss.c_str(), MAX_LEN_IP_STR); - s->ip[MAX_LEN_IP_STR - 1] = '\0'; - ps = pe; - - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(",", pe); - - ss = pRespStr->substr(ps, pe - ps); - strncpy(s->mask, ss.c_str(), MAX_LEN_IP_STR); - s->mask[MAX_LEN_IP_STR - 1] = '\0'; - ps = pe + 1; - - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(",", pe); - - ss = pRespStr->substr(ps, pe - ps); - strncpy(s->gateway, ss.c_str(), MAX_LEN_IP_STR); - s->gateway[MAX_LEN_IP_STR - 1] = '\0'; - ps = pe + 1; - - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(",", pe); - - - ss = pRespStr->substr(ps, pe - ps); - strncpy(s->dnsPrimary, ss.c_str(), MAX_LEN_IP_STR); - s->dnsPrimary[MAX_LEN_IP_STR - 1] = '\0'; - ps = pe + 1; - - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(".", pe); - if (pe == string::npos) - return (false); - else - pe += 1; - pe = pRespStr->find(",", pe); - - - ss = pRespStr->substr(ps, pe - ps); - strncpy(s->dnsSecondary, ss.c_str(), MAX_LEN_IP_STR); - s->dnsSecondary[MAX_LEN_IP_STR - 1] = '\0'; - - dbgPuts("~~~~~~~~~~ WNC IP Stats ~~~~~~~~~~~~"); - dbgPuts("ip: ", false); dbgPutsNoTime(s->ip); - dbgPuts("mask: ", false); dbgPutsNoTime(s->mask); - dbgPuts("gateway: ", false); dbgPutsNoTime(s->gateway); - dbgPuts("dns pri: ", false); dbgPutsNoTime(s->dnsPrimary); - dbgPuts("dns sec: ", false); dbgPutsNoTime(s->dnsSecondary); - dbgPuts("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - - return (true); - } - } - } - - return (false); -} - -bool WncController::deleteSMSTextFromMem(char msgIdx) -{ - const char * err = "deleteSMSTextFromMem: Failed!"; - - switch (msgIdx) - { - case '*': - at_deleteSMSTextFromMem_wnc('1'); - at_deleteSMSTextFromMem_wnc('2'); - at_deleteSMSTextFromMem_wnc('3'); - return (true); // WNC may error if slot empty, just ignore! - - case '1': - case '2': - case '3': - if (true == at_deleteSMSTextFromMem_wnc(msgIdx)) - return (true); - else { - dbgPuts(err); - return (false); - } - - default: - dbgPuts(err); - return (false); - } -} - -bool WncController::sendSMSTextFromMem(char msgIdx) -{ - const char * err = "deleteSMSTextFromMem: Failed!"; - - switch (msgIdx) - { - case '*': - at_sendSMStextMem_wnc('1'); - at_sendSMStextMem_wnc('2'); - at_sendSMStextMem_wnc('3'); - return (true); // WNC may error if slot is empty, just ignore! - - case '1': - case '2': - case '3': - if (at_sendSMStextMem_wnc(msgIdx) == true) - return (true); - else { - dbgPuts(err); - return (false); - } - - default: - dbgPuts(err); - return (false); - } -} - -bool WncController::at_deleteSMSTextFromMem_wnc(char n) -{ - string cmdStr, respStr; - // Message is stored in WNC, now send it! - cmdStr = "AT+CMGD="; - cmdStr += n; - cmdStr += "\r\n"; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); - AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - return (r == WNC_AT_CMD_OK); -} - -bool WncController::at_sendSMStextMem_wnc(char n) -{ - string cmdStr, respStr; - // Message is stored in WNC, now send it! - cmdStr = "AT+CMSS="; - cmdStr += n; - cmdStr += "\r\n"; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); - AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - return (r == WNC_AT_CMD_OK); -} - -bool WncController::at_sendSMStext_wnc(const char * const phoneNum, const char * const text) -{ - string respStr; - string * pRespStr; - size_t l = strlen(text); - - if (l <= MAX_WNC_SMS_LENGTH) - { - // Check to see if the SMS service is available - checkCellLink(); - if (m_sReadyForSMS == true) { - at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); - string cmdStr("AT+CMGS=\""); - cmdStr += phoneNum; - cmdStr += "\""; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); - cmdStr += "\x0d"; // x0d = <ENTER> - // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! - // And we want a delay before sending the actual text part of the string! - mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { - // Part 2 of the text, this is the actual text part: - cmdStr = text; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); - cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to send! - AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - if (respStr.size() == 0) - return (false); - else - return (r == WNC_AT_CMD_OK); - } - } - } - - return (false); -} - -bool WncController::saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx) -{ - if (at_saveSMStext_wnc(phoneNum, text, msgIdx) == true) - return (true); - else { - dbgPuts("saveSMSTextToMem: failed!\r\n"); - return (false); - } -} - -bool WncController::at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx) -{ - string respStr; - size_t l = strlen(text); - - if (l <= MAX_WNC_SMS_LENGTH) - { - // Check to see if the SMS service is available - checkCellLink(); - if (m_sReadyForSMS == true) { - string cmdStr("AT+CMGW=\""); - cmdStr += phoneNum; - cmdStr += "\""; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); - cmdStr += "\x0d"; // x0d = <ENTER> - // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! - // And we want a delay before sending the actual text part of the string! - mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { - // Part 2 of the text, this is the actual text part: - cmdStr = text; - dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); - cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to save! - AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); - dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); - if (respStr.size() > 0) { - // respStr will have the SMS index - size_t pos1 = respStr.find("+CMGW: "); - size_t pos2 = respStr.rfind("OK"); - if (pos1 != string::npos && pos2 != string::npos) { - *msgIdx = *string(respStr.substr(pos1+7, 1)).c_str(); - return (true); - } - else { - *msgIdx = '!'; - } - } - } - } - } - - return (false); -} - -bool WncController::at_readSMSlog_wnc(string ** log) -{ - return (at_send_wnc_cmd("AT+CMGL", log, m_sCmdTimeoutMs) == WNC_AT_CMD_OK); -} - -size_t WncController::at_readSMStext_wnc(const char n, const char ** log) -{ - static string smsReadTxtStr; - string * pRespStr; - string cmdStr; - - smsReadTxtStr.erase(); - cmdStr = "AT+CMGR"; - cmdStr += '1'; - if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) - *log = pRespStr->c_str(); - else - *log = "\0"; - - return (pRespStr->size()); -} - -bool WncController::at_at_wnc(void) -{ - string * pRespStr; - return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat? -} - -bool WncController::at_init_wnc(bool hardReset) -{ - string * pRespStr; - AtCmdErr_e cmdRes; - - if (hardReset == true) - dbgPuts("Hard Soft Reset!"); - - dbgPuts("Start AT init of WNC:"); - - // Kick it twice to perhaps remove cued responses from an incomplete - // power cycle. - at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); - at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); - - // Dump the firmware revision on the debug log: - at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs); - - // Quick commands below do not need to check cellular connectivity - at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Echo Off - at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs); // 2 - verbose error, 1 - numeric error, 0 - just ERROR - - // Setup 3 memory slots in the WNC SIM for SMS usage. - at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); - at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs); - - cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Heartbeat? - - // If the simple commands are not working, no chance of more complex. - // I have seen re-trying commands make it worse. - if (cmdRes != WNC_AT_CMD_OK) - return (false); - - cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs); - if (cmdRes != WNC_AT_CMD_OK) - return (false); - - cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs); - if (cmdRes != WNC_AT_CMD_OK) - return (false); - - dbgPuts("SUCCESS: AT init of WNC!"); - - return (true); -} - -int16_t WncController::at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec) -{ - string * pRespStr; - string cmd_str("AT@SOCKCREAT="); - AtCmdErr_e res; - - if (tcp) cmd_str += "1"; // TCP - else cmd_str += "2"; // else UDP - - cmd_str += ",0"; - res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - if (res == WNC_AT_CMD_OK && pRespStr->size() > 0) - { - size_t pos1 = pRespStr->find("T:"); - size_t pos2 = pRespStr->rfind("OK"); - if ((pos1 != string::npos) && (pos2 != string::npos)) { - size_t numLen = pos2 - (pos1 + 2); - string sockStr = pRespStr->substr(pos1 + 2, numLen); - cmd_str = "AT@SOCKCONN="; - cmd_str += sockStr; - cmd_str += ",\""; - cmd_str += ip; - cmd_str += "\","; - cmd_str += _to_string(port); - cmd_str += ","; - if (timeOutSec < 30) - timeOutSec = 30; - else if (timeOutSec > 360) - timeOutSec = 360; - cmd_str += _to_string(timeOutSec); - res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000); - if (m_sMoreDebugEnabled) { - at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs); - at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs); - } - return (strtol(sockStr.c_str(), NULL, 10)); - } - else { - dbgPuts("Invalid sockcreat response!"); - return (0); - } - } - else - return (0); -} - -bool WncController::at_sockclose_wnc(uint16_t numSock) -{ - string * pRespStr; - string cmd_str("AT@SOCKCLOSE="); - - cmd_str += _to_string(numSock); - - // Don't check the cell status to close the socket - AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - - if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) { - for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) { - res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK)) - break; - } - } - - return (res == WNC_AT_CMD_OK); -} - -bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr) -{ - string * pRespStr; - string str(s); - AtCmdErr_e r; - - ipStr->erase(); // Clear out string until resolved! - str = "AT@DNSRESVDON=\"" + str; - str += "\""; - r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS); - if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) { - size_t pos_start = pRespStr->find(":\"") + 2; - if (pos_start != string::npos) { - size_t pos_end = pRespStr->find("\"", pos_start) - 1; - if (pos_end != string::npos) { - if (pos_end > pos_start) { - // Make a copy for use later (the source string is re-used) - *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1); - return (true); - } - } - } - } - - *ipStr = INVALID_IP_STR; - - return (false); -} - -bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs) -{ - // Now, give the modem x seconds to start responding by - // sending simple 'AT' commands to modem once per second. - if (timeoutSecs > 0) { - do { - timeoutSecs--; - dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); - dbgPutsNoTime(" ", false); - AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); - if (rc == WNC_AT_CMD_OK) { - dbgPutsNoTime(""); // CR LF - return true; //timer.read(); - } - waitMs(500); - } - while (timeoutSecs > 0); - dbgPutsNoTime(""); // CR LF - } - - return (false); -} - -WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp) -{ - AtCmdErr_e result; - - if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) { - string * pRespStr; - const char * num2str; - string cmd_str; - - if (isTcp == true) - cmd_str="AT@SOCKWRITE="; - else - cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND="; - - cmd_str += _to_string(numSock); - cmd_str += ","; - cmd_str += _to_string(n); - cmd_str += ",\""; - while(n > 0) { - n--; - num2str = _to_hex_string(*s++); - // Always 2-digit ascii hex: - if (num2str[1] == '\0') - cmd_str += '0'; - cmd_str += num2str; - } - cmd_str += "\""; - result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - } - else { - dbgPuts("sockwrite Err, string len bad!"); - result = WNC_AT_CMD_ERR; - } - - return (result); -} - -WncController::AtCmdErr_e WncController::at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp) -{ - AtCmdErr_e result = WNC_AT_CMD_OK; - - string * pRespStr; - string cmd_str; - size_t pos_start, pos_end; - int i; - - pS->erase(); // Start with a fresh string - - if (isTcp == true) - cmd_str="AT@SOCKREAD="; - else - cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; - - cmd_str += _to_string(numSock); - cmd_str += ","; - cmd_str += _to_string(MAX_WNC_READ_BYTES); - - // Experimental: read should not need to check cell net status - result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - if (result == WNC_AT_CMD_OK) { - if (pRespStr->size() > 0) { - pos_start = pRespStr->find("\""); - pos_end = pRespStr->rfind("\""); - // Make sure search finds what it's looking for! - if (pos_start != string::npos && pos_end != string::npos) { - pos_start++; - i = pos_end - pos_start; // Num hex chars, 2 per byte - } - else - i = 0; - } - else - i = 0; - - if ((i < 0) || ((i % 2) == 1)) - dbgPuts("Invalid READ string!"); - - if (i > 2*MAX_WNC_READ_BYTES) { - i = 2*MAX_WNC_READ_BYTES; - dbgPuts("DANGER WNC read data does not match length!"); - } - - // If data, convert the hex string into byte values - while (i > 0) { - i -= 2; - *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); - pos_start += 2; - } - } - - return (result); -} - -WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp) -{ - AtCmdErr_e result = WNC_AT_CMD_OK; - *numRead = 0; - - if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) { - string * pRespStr; - string cmd_str; - size_t pos_start, pos_end; - int i; - - if (isTcp == true) - cmd_str="AT@SOCKREAD="; - else - cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; - - cmd_str += _to_string(numSock); - cmd_str += ","; - cmd_str += _to_string(n); - - // Experimental: read should not need to check cell net status - result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); - if (result == WNC_AT_CMD_OK) { - if (pRespStr->size() > 0) { - pos_start = pRespStr->find("\""); - pos_end = pRespStr->rfind("\""); - // Make sure search finds what it's looking for! - if (pos_start != string::npos && pos_end != string::npos) { - pos_start++; - i = pos_end - pos_start; // Num hex chars, 2 per byte - } - else - i = 0; - } - else - i = 0; - - if ((i < 0) || ((i % 2) == 1)) - dbgPuts("Invalid READ string!"); - - if (i > 2*n) { - // Bound the ill formated WNC read string! - i = 2*n; - dbgPuts("TRUNCATING read data!"); - } - - // If data, convert the hex string into byte values - i /= 2; - *numRead = i; - while (i > 0) { - i--; - *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); - pos_start += 2; - } - } - } - else { - dbgPuts("sockread Err, to many to read!"); - result = WNC_AT_CMD_ERR; - } - - return (result); -} - -bool WncController::at_reinitialize_mdm(void) -{ - // Atempt to re-register -// string * pRespStr; -// dbgPuts("Force re-register!"); -// at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs); -// waitMs(31000); -// at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs); -// waitMs(31000); - - // Initialize the modem - dbgPuts("Modem RE-initializing with SOFT Reset..."); - - string * pRespStr; - at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs); - waitMs(5000); - - // Now, give the modem time to start responding by - // sending simple 'AT' commands to the modem once per second. - int timeoutSecs = WNC_REINIT_MAX_TIME_MS; - do { - dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); - AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); - if (rc == WNC_AT_CMD_OK) { - dbgPutsNoTime(""); // CR LF - break; - } - waitMs(500); - timeoutSecs--; - } - while (timeoutSecs > 0); - - if (timeoutSecs <= 0) - dbgPuts("\r\nModem RE-init FAILED!"); - else - dbgPuts("\r\nModem RE-init complete!"); - - return (timeoutSecs > 0); -} - -WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf) -{ - rsp->erase(); // Clean up from possible prior cmd response - - // Don't bother the WNC if user hasn't turned it on. - if (m_sState == WNC_OFF) - return (WNC_AT_CMD_WNC_NOT_ON); - - size_t n = strlen(cmd); - - // Wait per WNC advise - waitMs(WNC_WAIT_FOR_AT_CMD_MS); - - if (cmd && n > 0) { - sendCmd(cmd, crLf); -// sendCmd(cmd, n, 1000, crLf); // 3rd arg is micro seconds between chars sent - } - - startTimerA(); - while (getTimerTicksA_mS() < timeout_ms) { - n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS()); - - if (n == 0) - continue; - - if (rsp->rfind("OK") != string::npos) { - stopTimerA(); - return (WNC_AT_CMD_OK); - } - - if (rsp->rfind("+CME ERROR") != string::npos) { - stopTimerA(); - return (WNC_AT_CMD_ERRCME); - } - - if (rsp->rfind("@EXTERR") != string::npos) { - stopTimerA(); - return (WNC_AT_CMD_ERREXT); - } - - if (rsp->rfind("ERROR") != string::npos) { - stopTimerA(); - return (WNC_AT_CMD_ERR); - } - } - stopTimerA(); - - return (WNC_AT_CMD_TIMEOUT); -} - -bool WncController::at_setapn_wnc(const char * const apnStr) -{ - string * pRespStr; - - string cmd_str("AT%PDNSET=1,"); - cmd_str += apnStr; - cmd_str += ",IP"; - if (WNC_AT_CMD_OK == at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, WNC_APNSET_TIMEOUT_MS)) // Set APN, cmd seems to take a little longer sometimes - return (true); - else - return (false); -} - -bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber) -{ - string * pRespStr; - AtCmdErr_e cmdRes; - cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER - if (cmdRes != WNC_AT_CMD_OK) - return (false); - - if (pRespStr->size() == 0) { - dbgPuts("Strange RSSI result!"); - return (false); - } - else { - size_t pos1 = pRespStr->find("SQ:"); - size_t pos2 = pRespStr->rfind(","); - // Sanity check - if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) { - string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 ); - int rawRssi = atoi(subStr.c_str()); - - // Convert WNC RSSI into dBm range: - // 0 - -113 dBm - // 1 - -111 dBm - // 2..30 - -109 to -53 dBm - // 31 - -51dBm or > - // 99 - not known or not detectable - if (rawRssi == 99) - *dBm = -199; - else if (rawRssi == 0) - *dBm = -113; - else if (rawRssi == 1) - *dBm = -111; - else if (rawRssi == 31) - *dBm = -51; - else if (rawRssi >= 2 && rawRssi <= 30) - *dBm = -113 + 2 * rawRssi; - else { - dbgPuts("Invalid RSSI!"); - return (false); - } - // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4 - // 99 - unknown or undetectable - subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1)); - *ber = atoi(subStr.c_str()); - } - else { - dbgPuts("Strange RSSI result2!"); - return (false); - } - } - - return (true); -} - -bool WncController::checkCellLink(void) -{ - string * pRespStr; - size_t pos; - int regSts; - int cmdRes1, cmdRes2; - - if (m_sState == WNC_OFF) - return (false); - - m_sState = WNC_ON_NO_CELL_LINK; - - if (m_sMoreDebugEnabled) - dbgPuts("<-------- Begin Cell Status ------------"); - - cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER - - // If no response, don't bother with more commands - if (cmdRes1 != WNC_AT_CMD_TIMEOUT) - cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs); // Check if SIM locked - else { - if (m_sMoreDebugEnabled) - dbgPuts("------------ WNC No Response! --------->"); - - return (false); - } - - if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0)) - { - if (m_sMoreDebugEnabled) - { - if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT)) - dbgPuts("------------ WNC No Response! --------->"); - else - dbgPuts("------------ WNC Cmd Error! ----------->"); - } - - // If by a miracle it responds to the 2nd after the 1st, keep going - if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0)) - return (false); - } - - // If SIM Card not ready don't bother with commands! - if (pRespStr->find("CPIN: READY") == string::npos) - { - if (m_sMoreDebugEnabled) - dbgPuts("------------ WNC SIM Problem! --------->"); - - return (false); - } - - // SIM card OK, now check for signal and cellular network registration - cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs); // Check if registered on network - if (cmdRes1 != WNC_AT_CMD_OK || pRespStr->size() == 0) - { - if (m_sMoreDebugEnabled) - dbgPuts("------------ WNC +CREG? Fail! --------->"); - - return (false); - } - else - { - pos = pRespStr->find("CREG: "); - if (pos != string::npos) - { - // The registration is the 2nd arg in the comma separated list - *pRespStr = pRespStr->substr(pos+8, 1); - regSts = atoi(pRespStr->c_str()); - switch (regSts) { - case 1: - case 5: - case 6: - case 7: - m_sReadyForSMS = true; - break; - default: - m_sReadyForSMS = false; - dbgPuts("SMS Service Down!"); - } - - // 1 - registered home, 5 - registered roaming - if ((regSts != 1) && (regSts != 5)) - { - if (m_sMoreDebugEnabled) - dbgPuts("------ WNC Cell Link Down for Data! --->"); - - return (false); - } - } - - if (m_sMoreDebugEnabled) - dbgPuts("------------ WNC Ready ---------------->"); - } - - // If we made it this far and the WNC did respond, keep the ON state - if (m_sState != WNC_NO_RESPONSE) - m_sState = WNC_ON; - - return (true); -} - -int WncController::dbgPutsNoTime(const char * s, bool crlf) -{ - if (m_sDebugEnabled == true) { - int r = dbgWriteChars(s); - if (crlf == true) - return (dbgWriteChars("\r\n")); - else - return (r); - } - else - return 0; -}; - -int WncController::dbgPuts(const char * s, bool crlf) -{ - dbgPutsNoTime("[*] ", false); - dbgPutsNoTime(_to_string(getLogTimerTicks()), false); - dbgPutsNoTime(" ", false); - - int r = dbgPutsNoTime(s, false); - - if (crlf == true) - return (dbgPutsNoTime("", true)); - else - return (r); -}; - -void WncController::sendCmd(const char * cmd, bool crLf) -{ - puts(cmd); - if (crLf == true) - puts("\r\n"); -} - -void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf) -{ - while (n--) { - putc(*cmd++); - waitUs(wait_uS); - }; - if (crLf == true) { - putc('\r'); - waitUs(wait_uS); - putc('\n'); - waitUs(wait_uS); - } -} - -}; // End namespace WncController_fk +/* + Copyright (c) 2016 Fred Kellerman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + @file WncController.cpp + @purpose Controls WNC 14A2A Cellular Modem + @version 1.0 + @date July 2016 + @author Fred Kellerman + + + An Example of usage: + + WncControllerK64F mdm(&wncPinList, &mdmUart, &debugUart); + + mdm.enableDebug(true, true); + + if (false == mdm.powerWncOn("m2m.com.attz", 60)) { + while(1); + } + + // ICCID and MSISDN + string iccid; string msisdn; + if (mdm.getICCID(&iccid) == true) { + if (mdm.convertICCIDtoMSISDN(iccid, &msisdn) == true) { + // Send an SMS message (must use 15-digit MISDN number!) + mdm.sendSMSText(msisdn.c_str(), "Hello from WNC Kit -> from WNC"); + } + } + + // Get an IP address setup for the socket #1 (0 indexed)) + if (true == mdm.resolveUrl(0, "www.att.com")) + { + // Report server IP + if (true == mdm.getIpAddr(0, ipAddrStr)) { + debugUart.puts("Server IP: "); + debugUart.puts(ipAddrStr); + debugUart.puts("\r\n"); + } + + // Open Socket #1, TCP=true resolved IP on port 80: + if (true == mdm.openSocket(0, 80, true)) { + // Write some data + const uint8_t * dataStr = "GET /index.html HTTP/1.0\r\nFrom: someuser@someuser.com\r\nUser-Agent: HTTPTool/1.0\r\n\r\n"; + if (true == mdm.write(0, dataStr, strlen((const char *)dataStr))) + { + const uint8_t * myBuf; + mdm.setReadRetries(0, 20); + uint32_t n = mdm.read(0, &myBuf); + if (n > 0) + debugUart.printf("Read %d chars: %s\r\n", n, myBuf); + else + debugUart.puts("No read data!\r\n"); + } + } + } + +*/ + + +#include <cstdlib> +#include <cctype> +#include <string.h> +#include "WncController.h" + +namespace WncController_fk { + +///////////////////////////////////////////////////// +// Static initializers +///////////////////////////////////////////////////// +WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS]; +const WncController::WncSocketInfo_s WncController::defaultSockStruct = { 0, false, "192.168.0.1", 80, 0, 25, true, 30 }; + +WncController::WncState_e WncController::m_sState = WNC_OFF; +uint16_t WncController::m_sCmdTimeoutMs = WNC_CMD_TIMEOUT_MS; +string WncController::m_sApnStr = "NULL"; +string WncController::m_sWncStr; +uint8_t WncController::m_sPowerUpTimeoutSecs = MAX_POWERUP_TIMEOUT; +bool WncController::m_sDebugEnabled = false; +bool WncController::m_sMoreDebugEnabled = false; +bool WncController::m_sCheckNetStatus = false; // Turn on internet status check between every command +const char * const WncController::INVALID_IP_STR = ""; +bool WncController::m_sReadyForSMS = false; + + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +static char* itoa(int64_t value, char* result, int base) +{ + // check that the base is valid + if ( base < 2 || base > 36 ) { + *result = '\0'; + return result; + } + + char* ptr = result, *ptr1 = result, tmp_char; + int64_t tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if ( tmp_value < 0 ) + *ptr++ = '-'; + + *ptr-- = '\0'; + + while ( ptr1 < ptr ) { + tmp_char = *ptr; + *ptr-- = *ptr1; + *ptr1++ = tmp_char; + } + + return result; +} + +const char * WncController::_to_string(int64_t value) +{ + static char str[21]; // room for signed 64-bit + null + itoa(value, str, 10); + return (str); +} + +const char * WncController::_to_hex_string(uint8_t value) +{ + static char str[3]; // room for 8-bit + null + itoa(value, str, 16); + return (str); +} + +WncController::WncController(void) +{ + for(unsigned i; i<MAX_NUM_WNC_SOCKETS; i++) + m_sSock[i] = defaultSockStruct; +} + +void WncController::enableDebug(bool on, bool moreDebugOn) +{ + m_sDebugEnabled = on; + m_sMoreDebugEnabled = moreDebugOn; +} + +WncController::WncState_e WncController::getWncStatus(void) +{ + return (m_sState); +} + +int16_t WncController::getDbmRssi(void) +{ + int16_t rssi, ber; + if (at_getrssiber_wnc(&rssi, &ber) == true) + return (rssi); + else + return (99); +} + +int16_t WncController::get3gBer(void) +{ + int16_t rssi, ber; + if (at_getrssiber_wnc(&rssi, &ber) == true) + return (ber); + else + return (99); +} + +bool WncController::powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs) +{ + dbgPuts("Waiting for WNC to Initialize..."); + m_sPowerUpTimeoutSecs = powerUpTimeoutSecs; + m_sState = WNC_ON_NO_CELL_LINK; // Turn soft on to allow "AT" for init to be sent! + if (initWncModem(powerUpTimeoutSecs) == true) { + // Set the Apn + setApnName(apn); + if (false == softwareInitMdm()) { + dbgPuts("Software init failed!"); + m_sState = WNC_OFF; + } + } + else { + dbgPuts("Power up failed!"); + m_sState = WNC_OFF; + } + + return ((m_sState == WNC_ON) || (m_sState == WNC_ON_NO_CELL_LINK)); +} + +size_t WncController::sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout) +{ + string * respStr; + + if (sizeRespBuf > 0) { + AtCmdErr_e r = at_send_wnc_cmd(cmd, &respStr, ms_timeout); + strncpy(resp, respStr->c_str(), sizeRespBuf); + if (respStr->size() > sizeRespBuf) + dbgPuts("sendCustomCmd truncated!"); + + return (respStr->size()); + } + + dbgPuts("sendCustomCmd: would have overrun!"); + + return (0); +} + +bool WncController::pingUrl(const char * url) +{ + string ipAddr; + + if (true == at_dnsresolve_wnc(url, &ipAddr)) + return (pingIp(ipAddr.c_str())); + else + dbgPuts("pingUrl DNS resolve: failed!"); + + return (false); +} + +bool WncController::pingIp(const char * ip) +{ + if (true == at_ping_wnc(ip)) + return (true); + else + dbgPuts("pingIp: failed!"); + + return (false); +} + +bool WncController::getWncNetworkingStats(WncIpStats * s) +{ + return (at_get_wnc_net_stats(s)); +} + +bool WncController::getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR]) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + strncpy(myIpAddr, m_sSock[numSock].myIpAddressStr.c_str(), MAX_LEN_IP_STR); + myIpAddr[MAX_LEN_IP_STR - 1] = '\0'; + return (true); + } + else { + myIpAddr[0] = '\0'; + return (false); + } +} + +bool WncController::setApnName(const char * const apnStr) +{ + if (at_setapn_wnc(apnStr) == true) + { + m_sApnStr = apnStr; + return (true); + } + else + return (false); +} + +bool WncController::resolveUrl(uint16_t numSock, const char * url) +{ + bool cmdRes; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (strlen(url) > 0) { + cmdRes = at_dnsresolve_wnc(url, &m_sSock[numSock].myIpAddressStr); + if (cmdRes == false) + dbgPuts("Cannot resolve URL!"); + return (cmdRes); + } + else + dbgPuts("Invalid URL"); + } + else + dbgPuts("Invalid Sock num!"); + + return (false); +} + +bool WncController::setIpAddr(uint16_t numSock, const char * ipStr) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + m_sSock[numSock].myIpAddressStr = ipStr; + return (true); + } + else { + dbgPuts("Bad socket num!"); + return (false); + } +} + +void WncController::setWncCmdTimeout(uint16_t toMs) +{ + m_sCmdTimeoutMs = toMs; +} + +bool WncController::openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (resolveUrl(numSock, url) == true) + return (openSocket(numSock, port, tcp, timeOutSec)); + + return (false); +} + +bool WncController::openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (setIpAddr(numSock, ipAddr) == true) + return (openSocket(numSock, port, tcp, timeOutSec)); + + return (false); +} + +bool WncController::openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + // IPV4 ip addr sanity check! + size_t lenIpStr = m_sSock[numSock].myIpAddressStr.size(); + if (lenIpStr < 7 || lenIpStr > 15) { + dbgPuts("Invalid IP Address!"); + return (false); + } + + // Already open ? Must close if want to re-open with new settings. + if (m_sSock[numSock].open == true) { + dbgPuts("Socket already open, close then re-open!"); + if (true == at_sockclose_wnc(m_sSock[numSock].numWncSock)) + m_sSock[numSock].open = false; + else + return (false); + } + + m_sSock[numSock].myPort = port; + m_sSock[numSock].isTcp = tcp; + m_sSock[numSock].timeOutSec = timeOutSec; + + int16_t numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), port, numSock, tcp, timeOutSec); + m_sSock[numSock].numWncSock = numWncSock; + if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].open = true; + else { + m_sSock[numSock].open = false; + dbgPuts("Socket open fail!!!!"); + + // If the modem is not responding don't bother it. + if (WNC_NO_RESPONSE != getWncStatus()) { + // Work-around. If the sock open fails it needs to be told + // to close. If 6 sock opens happen with a fail, it further + // crashes the WNC. Not sure why the sock won't open. + at_sockclose_wnc(m_sSock[numSock].numWncSock); + } + } + } + else { + dbgPuts("Bad socket num or IP!"); + return (false); + } + + return (m_sSock[numSock].open); +} + +bool WncController::sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp) +{ + bool result = true; + + AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, m_sSock[numSock].numWncSock, isTcp); + if (cmdRes != WNC_AT_CMD_OK) { + if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been written out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + } + result = false; + } + + return (result); +} + +bool WncController::write(uint16_t numSock, const uint8_t * s, uint32_t n) +{ + bool result; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + if (n <= MAX_WNC_WRITE_BYTES) { + result = sockWrite(s, n, numSock, m_sSock[numSock].isTcp); + } + else { + uint16_t rem = n % MAX_WNC_WRITE_BYTES; + while (n >= MAX_WNC_WRITE_BYTES) { + n -= MAX_WNC_WRITE_BYTES; + result = sockWrite(s, MAX_WNC_WRITE_BYTES, numSock, m_sSock[numSock].isTcp); + if (result == false) { + n = 0; + rem = 0; + dbgPuts("Sock write fail!"); + } + else + s += MAX_WNC_WRITE_BYTES; + } + if (rem > 0) + result = sockWrite(s, rem, numSock, m_sSock[numSock].isTcp); + } + } + else { + dbgPuts("Socket is closed for write!"); + result = false; + } + } + else { + dbgPuts("Bad socket num!"); + result = false; + } + + return (result); +} + +size_t WncController::read(uint16_t numSock, const uint8_t ** readBuf) +{ + static string theBuf; + string readStr; + + theBuf.erase(); // Clean-up from last time + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + uint8_t i = m_sSock[numSock].readRetries; + uint16_t to = m_sSock[numSock].readRetryWaitMs; + bool foundData = false; + do { + AtCmdErr_e cmdRes; + cmdRes = at_sockread_wnc(&readStr, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + if (WNC_AT_CMD_OK == cmdRes) { + // This will let this loop read until the socket data is + // empty. If no data, then wait the retry amount of time. + if (readStr.size() > 0) { + theBuf += readStr; + foundData = true; + i = 1; + } + else { + // Once data is found start returning it asap + if (foundData == false) + waitMs(to); + } + } + else { + theBuf += readStr; // Append what if any we got before it errored. + dbgPuts("Sockread failed!"); + if (WNC_NO_RESPONSE == getWncStatus()) { + i = 0; + } + else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been read out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + i = 0; + } + else + waitMs(to); + } + } while (i-- > 0); + } + else { + dbgPuts("Socket is closed for read"); + } + } + else { + dbgPuts("Bad socket num!"); + } + + *readBuf = (const uint8_t *)theBuf.c_str(); + + return (theBuf.size()); +} + +size_t WncController::read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen) +{ + uint32_t numCopied = 0; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + uint8_t i = m_sSock[numSock].readRetries; + uint16_t to = m_sSock[numSock].readRetryWaitMs; + bool foundData = false; + uint16_t numRead; + do { + AtCmdErr_e cmdRes; + if (maxReadBufLen < MAX_WNC_READ_BYTES) + cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + else + cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + + if (WNC_AT_CMD_OK == cmdRes) { + // This will let this loop read until the socket data is + // empty. If no data, then wait the retry amount of time. + if (numRead > 0) { + foundData = true; + i = 1; + if (numRead <= maxReadBufLen) { + maxReadBufLen -= numRead; + numCopied += numRead; + readBuf += numRead; + } + else { + i = 0; // No more room for data! + dbgPutsNoTime("No more room for read data!"); + } + } + else { + // Once data is found start returning it asap + if (foundData == false) + waitMs(to); + } + } + else { + dbgPuts("Sockread failed!"); + if (WNC_NO_RESPONSE == getWncStatus()) { + i = 0; + } + else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been read out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + i = 0; + } + else + waitMs(to); + } + } while ((i-- > 0) && (maxReadBufLen > 0)); + } + else { + dbgPuts("Socket is closed for read"); + } + } + else { + dbgPuts("Bad socket num!"); + } + + return (numCopied); +} + +void WncController::setReadRetries(uint16_t numSock, uint16_t retries) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].readRetries = retries; + else + dbgPuts("Bad socket num!"); +} + +void WncController::setReadRetryWait(uint16_t numSock, uint16_t readRetryWaitMs) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].readRetryWaitMs = readRetryWaitMs; + else + dbgPuts("Bad socket num!"); +} + +bool WncController::closeSocket(uint16_t numSock) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + + if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) + dbgPuts("Sock close may not have closed!"); + + // Even with an error the socket could have closed, + // can't tell for sure so just soft close it for now. + m_sSock[numSock].open = false; + } + else { + dbgPuts("Bad socket num!"); + } + + return (m_sSock[numSock].open == false); +} + +size_t WncController::mdmGetline(string * buff, int timeout_ms) +{ + char chin = '\0'; + char chin_last; + size_t len = 0; + + startTimerB(); + while ((len <= MAX_LEN_WNC_CMD_RESPONSE) && (getTimerTicksB_mS() < timeout_ms)) { + if (charReady()) { + chin_last = chin; + chin = getc(); + if (isprint(chin)) { + *buff += chin; + len++; // Bound the copy length to something reaonable just in case + continue; + } + else if ((('\r' == chin_last) && ('\n' == chin)) || (('\n' == chin_last) && ('\r' == chin))) { + break; + } + } + } + stopTimerB(); + + if (len > MAX_LEN_WNC_CMD_RESPONSE) + dbgPuts("Max cmd length reply exceeded!"); + + return (len); +} + +bool WncController::softwareInitMdm(void) +{ + static bool reportStatus = true; + unsigned i; + + if (checkCellLink() == true) { + if (reportStatus == false) { + dbgPuts("Re-connected to cellular network!"); + reportStatus = true; + } + + // WNC has SIM and registered on network so + // soft initialize the WNC. + for (i = 0; i < WNC_SOFT_INIT_RETRY_COUNT; i++) + if (at_init_wnc() == true) + break; + + // If it did not respond try a hardware init + if (i == WNC_SOFT_INIT_RETRY_COUNT) + { + at_reinitialize_mdm(); + return (at_init_wnc(true)); // Hard reset occurred so make it go through the software init(); + } + else + return (true); + } + else + { + if (reportStatus == true) { + dbgPuts("Not connected to cellular network!"); + reportStatus = false; + } + return (false); + } +} + +WncController::AtCmdErr_e WncController::sendWncCmd(const char * const s, string ** r, int ms_timeout) +{ + if (checkCellLink() == false) { + static string noRespStr; + + // Save some run-time! + if (m_sDebugEnabled) + { + dbgPuts("FAIL send cmd: ", false); + if (m_sMoreDebugEnabled && m_sDebugEnabled) { + dbgPutsNoTime(s); + } + else { + size_t n = strlen(s); + if (n <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPutsNoTime(s); + } + else { + string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); + truncStr += ".."; + truncStr += &s[n-(WNC_TRUNC_DEBUG_LENGTH/2)]; + dbgPutsNoTime(truncStr.c_str()); + } + } + } + + noRespStr.erase(); + *r = &noRespStr; + + return (WNC_AT_CMD_NO_CELL_LINK); + } + + if (m_sCheckNetStatus) + { + if (m_sMoreDebugEnabled) + dbgPuts("[---------- Network Status -------------"); + string * pRespStr; + at_send_wnc_cmd("AT@SOCKDIAL?", &pRespStr, m_sCmdTimeoutMs); + if (m_sMoreDebugEnabled) + dbgPuts("---------------------------------------]"); + } + + // If WNC ready, send user command + return (at_send_wnc_cmd(s, r, ms_timeout)); +} + +WncController::AtCmdErr_e WncController::at_send_wnc_cmd(const char * s, string ** r, int ms_timeout) +{ + // Save some run-time! + if (m_sDebugEnabled) + { + if (m_sMoreDebugEnabled) { + dbgPuts("TX: ", false); dbgPutsNoTime(s); + } + else { + if (m_sDebugEnabled) { // Save some run-time! + size_t n = strlen(s); + if (n <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPuts("TX: ", false); dbgPutsNoTime(s); + } + else { + string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); + truncStr += ".."; + truncStr += &s[n - (WNC_TRUNC_DEBUG_LENGTH/2)]; + dbgPuts("TX: ", false); dbgPutsNoTime(truncStr.c_str()); + } + } + } + } + + AtCmdErr_e atResult = mdmSendAtCmdRsp(s, ms_timeout, &m_sWncStr); + *r = &m_sWncStr; // Return a pointer to the static string + + if (atResult != WNC_AT_CMD_TIMEOUT) { + // If a prior command timed out but a new one works then + // change the state back to ON. We don't know here in this + // method if the Cell Link is good so assume it is. When a command + // that depends on the cell link is made it will update the state. + if (m_sState == WNC_NO_RESPONSE) + m_sState = WNC_ON; + + // Save some run-time! + if (m_sDebugEnabled) + { + dbgPuts("RX: ", false); + if (m_sMoreDebugEnabled) { + dbgPutsNoTime(m_sWncStr.c_str()); + } + else { + if (m_sWncStr.size() <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPutsNoTime(m_sWncStr.c_str()); + } + else { + string truncStr = m_sWncStr.substr(0,WNC_TRUNC_DEBUG_LENGTH/2) + ".."; + truncStr += m_sWncStr.substr(m_sWncStr.size() - (WNC_TRUNC_DEBUG_LENGTH/2), WNC_TRUNC_DEBUG_LENGTH/2); + dbgPutsNoTime(truncStr.c_str()); + } + } + } + } + else { + m_sState = WNC_NO_RESPONSE; + dbgPuts("AT Cmd TIMEOUT!"); + dbgPuts("RX: ", false); dbgPutsNoTime(m_sWncStr.c_str()); + } + + return (atResult); +} + +void WncController::closeOpenSocket(uint16_t numSock) +{ + // Try to open and close the socket + do { + dbgPuts("Try to close and re-open socket"); + if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) { + if (WNC_NO_RESPONSE == getWncStatus()) { + dbgPuts("No response for closeOpenSocket1"); + return ; + } + } + + int numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec); + m_sSock[numSock].numWncSock = numWncSock; + if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].open = true; + else { + m_sSock[numSock].open = false; + dbgPuts("Failed to re-open socket!"); + } + + if (WNC_NO_RESPONSE == getWncStatus()) { + dbgPuts("No response for closeOpenSocket2"); + return ; + } + } while (m_sSock[numSock].open == false); +} + +bool WncController::getICCID(string * iccid) +{ + if (at_geticcid_wnc(iccid) == false) { + dbgPuts("getICCID error!"); + return (false); + } + + return (true); +} + +bool WncController::at_geticcid_wnc(string * iccid) +{ + string * respStr; + + iccid->erase(); + + AtCmdErr_e r = at_send_wnc_cmd("AT%CCID", &respStr, m_sCmdTimeoutMs); + + if (r != WNC_AT_CMD_OK || respStr->size() == 0) + return (false); + + size_t pos = respStr->find("AT%CCID"); + if (pos == string::npos) + return (false); + + size_t posOK = respStr->rfind("OK"); + if (posOK == string::npos) + return (false); + + pos += 7; // Advanced to the number + *iccid = respStr->substr(pos, posOK - pos); + + return (true); +} + +bool WncController::convertICCIDtoMSISDN(const string & iccid, string * msisdn) +{ + msisdn->erase(); + + if (iccid.size() != 20 && iccid.size() != 19) { + dbgPuts("Invalid ICCID length!"); + return (false); + } + + *msisdn = "882350"; + + if (iccid.size() == 20) + *msisdn += iccid.substr(10,iccid.size() - 11); + else + *msisdn += iccid.substr(10,iccid.size() - 10); + + return (true); +} + +bool WncController::sendSMSText(const char * const phoneNum, const char * const text) +{ + if (at_sendSMStext_wnc(phoneNum, text) == true) + return (true); + else { + dbgPuts("sendSMSText: Failed!"); + return (false); + } +} + +bool WncController::readSMSLog(struct WncSmsList * log) +{ + string * logStr; + uint16_t i; + + if (at_readSMSlog_wnc(&logStr) == false) { + dbgPuts("readSMSLog: Failed!"); + return (false); + } + + // Clean slate + log->msgCount = 0; + + if (logStr->size() == 0) + return (false); + + // Pick out the stuff from the string and convert to struct + string s; + size_t pos2; + size_t pos = logStr->find("+CMGL:"); + + for(i=0; i<MAX_WNC_SMS_MSG_SLOTS; i++) { + // Start with a clean slate, let parsing fill out later. + log->e[i].unread = false; + log->e[i].incoming = false; + log->e[i].unsent = false; + log->e[i].pduMode = false; + log->e[i].msgReceipt = false; + + log->e[i].idx = logStr->at(pos + 7); + if (pos == string::npos) + return (false); + pos2 = logStr->find(",\"", pos); + if (pos2 == string::npos) { + // If the WNC acts wrong and receives a PDU mode + // SMS there will not be any quotes in the response, + // just take the whole reply and make it the message body for + // now, mark it as an unread message, set the pdu flag! + log->e[log->msgCount].unread = true; + log->e[log->msgCount].pduMode = true; + log->msgCount++; + + pos2 = logStr->find("+CMGL", pos + 5); + if (pos2 == string::npos) { + pos2 = logStr->find("OK", pos + 5); + if (pos2 == string::npos) { + dbgPuts("Strange SMS Log Ending!"); + return (false); + } + i = MAX_WNC_SMS_MSG_SLOTS; + } + log->e[log->msgCount].msg = logStr->substr(0, pos2 - pos); + pos = pos2; // for loop starts off expecting pos to point to next log msg + continue; + } + pos += 2; // Advance to the text we want + pos2 = logStr->find("\",", pos); + if ((pos2 == string::npos) || (pos >= pos2)) + return (false); + + // Setup attributes + s = logStr->substr(pos, pos2 - pos); + if (s.find("REC READ") != string::npos) + log->e[i].incoming = true; + if (s.find("REC UNREAD") != string::npos) { + log->e[i].unread = true; + log->e[i].incoming = true; + } + if (s.find("STO UNSENT") != string::npos) + log->e[i].unsent = true; + if (logStr->find(",,") == string::npos) + log->e[i].msgReceipt = true; + + // Tele number + pos2 = logStr->find(",\"", pos2); + if (pos2 == string::npos) + return (false); + pos2 += 2; // Advance to next field + pos = logStr->find("\",", pos2); + if ((pos == string::npos) || (pos2 > pos)) + return (false); + if (pos == pos2) + log->e[i].number.erase(); + else + log->e[i].number = logStr->substr(pos2, pos - pos2); + + // Date + pos = logStr->find(",\"", pos); + if (pos == string::npos) + return (false); + pos += 2; // Beginning of date field + pos2 = logStr->find(",", pos); // End of timestamp field + if ((pos2 == string::npos) || (pos > pos2)) + return (false); + if (pos == pos2) + log->e[i].date.erase(); + else + log->e[i].date = logStr->substr(pos, pos2 - pos); + + // Timestamp + pos = logStr->find("\",", pos2); // End of timestamp + if (pos == string::npos) + return (false); + pos2 += 1; // Beginning of time field + if (pos < pos2) + return (false); + if (pos == pos2) + log->e[i].time.erase(); + else + log->e[i].time = logStr->substr(pos2, pos - pos2); + + // Message field + + // We don't know how many messages we have so the next search + // could end with +CMGL or OK. + pos += 2; // Advanced to message text + pos2 = logStr->find("+CMGL", pos); + if (pos2 == string::npos) { + pos2 = logStr->find("OK", pos); + if (pos2 == string::npos) { + dbgPuts("Strange SMS Log Ending!"); + return (false); + } + i = MAX_WNC_SMS_MSG_SLOTS; // break + } + if (pos > pos2) + return (false); + if (pos == pos2) + log->e[log->msgCount].msg.erase(); + else + log->e[log->msgCount].msg = logStr->substr(pos, pos2 - pos); + + log->msgCount++; // Message complete + } + + return (true); +} + +bool WncController::readUnreadSMSText(struct WncSmsList * w, bool deleteRead) +{ + struct WncController::WncSmsList tmp; + + if (readSMSLog(&tmp) == false) + return (false); + + w->msgCount = 0; + for(uint16_t i = 0; i < tmp.msgCount; i++) { + if (tmp.e[i].unread == true) { + w->e[w->msgCount] = tmp.e[i]; + w->msgCount++; + if (deleteRead == true) { + // Clean up message that was copied out and read + deleteSMSTextFromMem(w->e[i].idx); + } + } + } + + return (w->msgCount > 0); +} + +size_t WncController::getSignalQuality(const char ** log) +{ + size_t n; + + n = at_getSignalQuality_wnc(log); + if (n == 0) + dbgPuts("readSMSText: Failed!"); + + return (n); +} + +size_t WncController::at_getSignalQuality_wnc(const char ** log) +{ + string * pRespStr; + static string logStr; + + logStr.erase(); + + if (at_send_wnc_cmd("AT%MEAS=\"0\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr = *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=0: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"1\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=1: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"2\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=2: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"3\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=3: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"4\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=4: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"5\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=5: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=8: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"98\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=98: failed!"); + + *log = logStr.c_str(); + + return (logStr.size()); +} + +bool WncController::getTimeDate(struct WncDateTime * tod) +{ + if (at_gettimedate_wnc(tod) == true) + return (true); + else { + dbgPuts("Get time date failed!"); + return (false); + } +} + +bool WncController::at_ping_wnc(const char * ip) +{ + string * pRespStr; + string cmdStr = "AT@PINGREQ=\""; + cmdStr += ip; + cmdStr += "\""; + return (at_send_wnc_cmd(cmdStr.c_str(), &pRespStr, WNC_PING_CMD_TIMEOUT_MS) == WNC_AT_CMD_OK); +} + +bool WncController::at_gettimedate_wnc(struct WncDateTime * tod) +{ + string * pRespStr; + char * pEnd; + + if (at_send_wnc_cmd("AT+CCLK?", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + size_t pos1 = pRespStr->find("+CCLK:"); + if (pos1 != string::npos) { + pEnd = (char *)pRespStr->c_str() + pos1 + 8; + tod->year = strtol(pEnd, &pEnd, 10); + tod->month = strtol(pEnd+1, &pEnd, 10); + tod->day = strtol(pEnd+1, &pEnd, 10); + tod->hour = strtol(pEnd+1, &pEnd, 10); + tod->min = strtol(pEnd+1, &pEnd, 10); + tod->sec = strtol(pEnd+1, &pEnd, 10); + return (true); + } + } + } + + return (false); +} + +bool WncController::at_get_wnc_net_stats(WncIpStats * s) +{ + string * pRespStr; + AtCmdErr_e cmdRes = at_send_wnc_cmd("AT+CGCONTRDP=1", &pRespStr, m_sCmdTimeoutMs); + + if (WNC_AT_CMD_OK == cmdRes) { + if (pRespStr->size() > 0) { + memset((void*)s, '\0', sizeof(*s)); // Clean-up + string ss; + size_t pe; + size_t ps = pRespStr->rfind("\""); + if (ps != string::npos) { + ps += 2; // Skip the , after the " + pe = ps; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + + ss = pRespStr->substr(ps, pe - 1 - ps); + strncpy(s->ip, ss.c_str(), MAX_LEN_IP_STR); + s->ip[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->mask, ss.c_str(), MAX_LEN_IP_STR); + s->mask[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->gateway, ss.c_str(), MAX_LEN_IP_STR); + s->gateway[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->dnsPrimary, ss.c_str(), MAX_LEN_IP_STR); + s->dnsPrimary[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->dnsSecondary, ss.c_str(), MAX_LEN_IP_STR); + s->dnsSecondary[MAX_LEN_IP_STR - 1] = '\0'; + + dbgPuts("~~~~~~~~~~ WNC IP Stats ~~~~~~~~~~~~"); + dbgPuts("ip: ", false); dbgPutsNoTime(s->ip); + dbgPuts("mask: ", false); dbgPutsNoTime(s->mask); + dbgPuts("gateway: ", false); dbgPutsNoTime(s->gateway); + dbgPuts("dns pri: ", false); dbgPutsNoTime(s->dnsPrimary); + dbgPuts("dns sec: ", false); dbgPutsNoTime(s->dnsSecondary); + dbgPuts("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + + return (true); + } + } + } + + return (false); +} + +bool WncController::deleteSMSTextFromMem(char msgIdx) +{ + const char * err = "deleteSMSTextFromMem: Failed!"; + + switch (msgIdx) + { + case '*': + at_deleteSMSTextFromMem_wnc('1'); + at_deleteSMSTextFromMem_wnc('2'); + at_deleteSMSTextFromMem_wnc('3'); + return (true); // WNC may error if slot empty, just ignore! + + case '1': + case '2': + case '3': + if (true == at_deleteSMSTextFromMem_wnc(msgIdx)) + return (true); + else { + dbgPuts(err); + return (false); + } + + default: + dbgPuts(err); + return (false); + } +} + +bool WncController::sendSMSTextFromMem(char msgIdx) +{ + const char * err = "deleteSMSTextFromMem: Failed!"; + + switch (msgIdx) + { + case '*': + at_sendSMStextMem_wnc('1'); + at_sendSMStextMem_wnc('2'); + at_sendSMStextMem_wnc('3'); + return (true); // WNC may error if slot is empty, just ignore! + + case '1': + case '2': + case '3': + if (at_sendSMStextMem_wnc(msgIdx) == true) + return (true); + else { + dbgPuts(err); + return (false); + } + + default: + dbgPuts(err); + return (false); + } +} + +bool WncController::at_deleteSMSTextFromMem_wnc(char n) +{ + string cmdStr, respStr; + // Message is stored in WNC, now send it! + cmdStr = "AT+CMGD="; + cmdStr += n; + cmdStr += "\r\n"; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + return (r == WNC_AT_CMD_OK); +} + +bool WncController::at_sendSMStextMem_wnc(char n) +{ + string cmdStr, respStr; + // Message is stored in WNC, now send it! + cmdStr = "AT+CMSS="; + cmdStr += n; + cmdStr += "\r\n"; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + return (r == WNC_AT_CMD_OK); +} + +bool WncController::at_sendSMStext_wnc(const char * const phoneNum, const char * const text) +{ + string respStr; + string * pRespStr; + size_t l = strlen(text); + + if (l <= MAX_WNC_SMS_LENGTH) + { + // Check to see if the SMS service is available + checkCellLink(); + if (m_sReadyForSMS == true) { + at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); + string cmdStr("AT+CMGS=\""); + cmdStr += phoneNum; + cmdStr += "\""; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x0d"; // x0d = <ENTER> + // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! + // And we want a delay before sending the actual text part of the string! + mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { + // Part 2 of the text, this is the actual text part: + cmdStr = text; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to send! + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if (respStr.size() == 0) + return (false); + else + return (r == WNC_AT_CMD_OK); + } + } + } + + return (false); +} + +bool WncController::saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx) +{ + if (at_saveSMStext_wnc(phoneNum, text, msgIdx) == true) + return (true); + else { + dbgPuts("saveSMSTextToMem: failed!\r\n"); + return (false); + } +} + +bool WncController::at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx) +{ + string respStr; + size_t l = strlen(text); + + if (l <= MAX_WNC_SMS_LENGTH) + { + // Check to see if the SMS service is available + checkCellLink(); + if (m_sReadyForSMS == true) { + string cmdStr("AT+CMGW=\""); + cmdStr += phoneNum; + cmdStr += "\""; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x0d"; // x0d = <ENTER> + // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! + // And we want a delay before sending the actual text part of the string! + mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { + // Part 2 of the text, this is the actual text part: + cmdStr = text; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to save! + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if (respStr.size() > 0) { + // respStr will have the SMS index + size_t pos1 = respStr.find("+CMGW: "); + size_t pos2 = respStr.rfind("OK"); + if (pos1 != string::npos && pos2 != string::npos) { + *msgIdx = *string(respStr.substr(pos1+7, 1)).c_str(); + return (true); + } + else { + *msgIdx = '!'; + } + } + } + } + } + + return (false); +} + +bool WncController::at_readSMSlog_wnc(string ** log) +{ + return (at_send_wnc_cmd("AT+CMGL", log, m_sCmdTimeoutMs) == WNC_AT_CMD_OK); +} + +size_t WncController::at_readSMStext_wnc(const char n, const char ** log) +{ + static string smsReadTxtStr; + string * pRespStr; + string cmdStr; + + smsReadTxtStr.erase(); + cmdStr = "AT+CMGR"; + cmdStr += '1'; + if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) + *log = pRespStr->c_str(); + else + *log = "\0"; + + return (pRespStr->size()); +} + +bool WncController::at_at_wnc(void) +{ + string * pRespStr; + return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat? +} + +bool WncController::at_init_wnc(bool hardReset) +{ + string * pRespStr; + AtCmdErr_e cmdRes; + + if (hardReset == true) + dbgPuts("Hard Soft Reset!"); + + dbgPuts("Start AT init of WNC:"); + + // Kick it twice to perhaps remove cued responses from an incomplete + // power cycle. + at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); + at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); + + // Dump the firmware revision on the debug log: + at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs); + + // Quick commands below do not need to check cellular connectivity + at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Echo Off + at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs); // 2 - verbose error, 1 - numeric error, 0 - just ERROR + + // Setup 3 memory slots in the WNC SIM for SMS usage. + at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); + at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs); + + cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Heartbeat? + + // If the simple commands are not working, no chance of more complex. + // I have seen re-trying commands make it worse. + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs); + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs); + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + dbgPuts("SUCCESS: AT init of WNC!"); + + return (true); +} + +int16_t WncController::at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec) +{ + string * pRespStr; + string cmd_str("AT@SOCKCREAT="); + AtCmdErr_e res; + + if (tcp) cmd_str += "1"; // TCP + else cmd_str += "2"; // else UDP + + cmd_str += ",0"; + res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (res == WNC_AT_CMD_OK && pRespStr->size() > 0) + { + size_t pos1 = pRespStr->find("T:"); + size_t pos2 = pRespStr->rfind("OK"); + if ((pos1 != string::npos) && (pos2 != string::npos)) { + size_t numLen = pos2 - (pos1 + 2); + string sockStr = pRespStr->substr(pos1 + 2, numLen); + cmd_str = "AT@SOCKCONN="; + cmd_str += sockStr; + cmd_str += ",\""; + cmd_str += ip; + cmd_str += "\","; + cmd_str += _to_string(port); + cmd_str += ","; + if (timeOutSec < 30) + timeOutSec = 30; + else if (timeOutSec > 360) + timeOutSec = 360; + cmd_str += _to_string(timeOutSec); + res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000); + if (m_sMoreDebugEnabled) { + at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs); + at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs); + } + return (strtol(sockStr.c_str(), NULL, 10)); + } + else { + dbgPuts("Invalid sockcreat response!"); + return (0); + } + } + else + return (0); +} + +bool WncController::at_sockclose_wnc(uint16_t numSock) +{ + string * pRespStr; + string cmd_str("AT@SOCKCLOSE="); + + cmd_str += _to_string(numSock); + + // Don't check the cell status to close the socket + AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + + if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) { + for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) { + res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK)) + break; + } + } + + return (res == WNC_AT_CMD_OK); +} + +bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr) +{ + string * pRespStr; + string str(s); + AtCmdErr_e r; + + ipStr->erase(); // Clear out string until resolved! + str = "AT@DNSRESVDON=\"" + str; + str += "\""; + r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS); + if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) { + size_t pos_start = pRespStr->find(":\"") + 2; + if (pos_start != string::npos) { + size_t pos_end = pRespStr->find("\"", pos_start) - 1; + if (pos_end != string::npos) { + if (pos_end > pos_start) { + // Make a copy for use later (the source string is re-used) + *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1); + return (true); + } + } + } + } + + *ipStr = INVALID_IP_STR; + + return (false); +} + +bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs) +{ + // Now, give the modem x seconds to start responding by + // sending simple 'AT' commands to modem once per second. + if (timeoutSecs > 0) { + do { + timeoutSecs--; + dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); + dbgPutsNoTime(" ", false); + AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); + if (rc == WNC_AT_CMD_OK) { + dbgPutsNoTime(""); // CR LF + return true; //timer.read(); + } + waitMs(500); + } + while (timeoutSecs > 0); + dbgPutsNoTime(""); // CR LF + } + + return (false); +} + +WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result; + + if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) { + string * pRespStr; + const char * num2str; + string cmd_str; + + if (isTcp == true) + cmd_str="AT@SOCKWRITE="; + else + cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(n); + cmd_str += ",\""; + while(n > 0) { + n--; + num2str = _to_hex_string(*s++); + // Always 2-digit ascii hex: + if (num2str[1] == '\0') + cmd_str += '0'; + cmd_str += num2str; + } + cmd_str += "\""; + result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + } + else { + dbgPuts("sockwrite Err, string len bad!"); + result = WNC_AT_CMD_ERR; + } + + return (result); +} + +WncController::AtCmdErr_e WncController::at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result = WNC_AT_CMD_OK; + + string * pRespStr; + string cmd_str; + size_t pos_start, pos_end; + int i; + + pS->erase(); // Start with a fresh string + + if (isTcp == true) + cmd_str="AT@SOCKREAD="; + else + cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(MAX_WNC_READ_BYTES); + + // Experimental: read should not need to check cell net status + result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (result == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + pos_start = pRespStr->find("\""); + pos_end = pRespStr->rfind("\""); + // Make sure search finds what it's looking for! + if (pos_start != string::npos && pos_end != string::npos) { + pos_start++; + i = pos_end - pos_start; // Num hex chars, 2 per byte + } + else + i = 0; + } + else + i = 0; + + if ((i < 0) || ((i % 2) == 1)) + dbgPuts("Invalid READ string!"); + + if (i > 2*MAX_WNC_READ_BYTES) { + i = 2*MAX_WNC_READ_BYTES; + dbgPuts("DANGER WNC read data does not match length!"); + } + + // If data, convert the hex string into byte values + while (i > 0) { + i -= 2; + *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); + pos_start += 2; + } + } + + return (result); +} + +WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result = WNC_AT_CMD_OK; + *numRead = 0; + + if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) { + string * pRespStr; + string cmd_str; + size_t pos_start, pos_end; + int i; + + if (isTcp == true) + cmd_str="AT@SOCKREAD="; + else + cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(n); + + // Experimental: read should not need to check cell net status + result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (result == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + pos_start = pRespStr->find("\""); + pos_end = pRespStr->rfind("\""); + // Make sure search finds what it's looking for! + if (pos_start != string::npos && pos_end != string::npos) { + pos_start++; + i = pos_end - pos_start; // Num hex chars, 2 per byte + } + else + i = 0; + } + else + i = 0; + + if ((i < 0) || ((i % 2) == 1)) + dbgPuts("Invalid READ string!"); + + if (i > 2*n) { + // Bound the ill formated WNC read string! + i = 2*n; + dbgPuts("TRUNCATING read data!"); + } + + // If data, convert the hex string into byte values + i /= 2; + *numRead = i; + while (i > 0) { + i--; + *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); + pos_start += 2; + } + } + } + else { + dbgPuts("sockread Err, to many to read!"); + result = WNC_AT_CMD_ERR; + } + + return (result); +} + +bool WncController::at_reinitialize_mdm(void) +{ + // Atempt to re-register +// string * pRespStr; +// dbgPuts("Force re-register!"); +// at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs); +// waitMs(31000); +// at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs); +// waitMs(31000); + + // Initialize the modem + dbgPuts("Modem RE-initializing with SOFT Reset..."); + + string * pRespStr; + at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs); + waitMs(5000); + + // Now, give the modem time to start responding by + // sending simple 'AT' commands to the modem once per second. + int timeoutSecs = WNC_REINIT_MAX_TIME_MS; + do { + dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); + AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); + if (rc == WNC_AT_CMD_OK) { + dbgPutsNoTime(""); // CR LF + break; + } + waitMs(500); + timeoutSecs--; + } + while (timeoutSecs > 0); + + if (timeoutSecs <= 0) + dbgPuts("\r\nModem RE-init FAILED!"); + else + dbgPuts("\r\nModem RE-init complete!"); + + return (timeoutSecs > 0); +} + +WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf) +{ + rsp->erase(); // Clean up from possible prior cmd response + + // Don't bother the WNC if user hasn't turned it on. + if (m_sState == WNC_OFF) + return (WNC_AT_CMD_WNC_NOT_ON); + + size_t n = strlen(cmd); + + // Wait per WNC advise + waitMs(WNC_WAIT_FOR_AT_CMD_MS); + + if (cmd && n > 0) { + sendCmd(cmd, crLf); +// sendCmd(cmd, n, 1000, crLf); // 3rd arg is micro seconds between chars sent + } + + startTimerA(); + while (getTimerTicksA_mS() < timeout_ms) { + n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS()); + + if (n == 0) + continue; + + if (rsp->rfind("OK") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_OK); + } + + if (rsp->rfind("+CME ERROR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERRCME); + } + + if (rsp->rfind("@EXTERR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERREXT); + } + + if (rsp->rfind("ERROR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERR); + } + } + stopTimerA(); + + return (WNC_AT_CMD_TIMEOUT); +} + +bool WncController::at_setapn_wnc(const char * const apnStr) +{ + string * pRespStr; + + string cmd_str("AT%PDNSET=1,"); + cmd_str += apnStr; + cmd_str += ",IP"; + if (WNC_AT_CMD_OK == at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, WNC_APNSET_TIMEOUT_MS)) // Set APN, cmd seems to take a little longer sometimes + return (true); + else + return (false); +} + +bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber) +{ + string * pRespStr; + AtCmdErr_e cmdRes; + cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + if (pRespStr->size() == 0) { + dbgPuts("Strange RSSI result!"); + return (false); + } + else { + size_t pos1 = pRespStr->find("SQ:"); + size_t pos2 = pRespStr->rfind(","); + // Sanity check + if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) { + string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 ); + int rawRssi = atoi(subStr.c_str()); + + // Convert WNC RSSI into dBm range: + // 0 - -113 dBm + // 1 - -111 dBm + // 2..30 - -109 to -53 dBm + // 31 - -51dBm or > + // 99 - not known or not detectable + if (rawRssi == 99) + *dBm = -199; + else if (rawRssi == 0) + *dBm = -113; + else if (rawRssi == 1) + *dBm = -111; + else if (rawRssi == 31) + *dBm = -51; + else if (rawRssi >= 2 && rawRssi <= 30) + *dBm = -113 + 2 * rawRssi; + else { + dbgPuts("Invalid RSSI!"); + return (false); + } + // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4 + // 99 - unknown or undetectable + subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1)); + *ber = atoi(subStr.c_str()); + } + else { + dbgPuts("Strange RSSI result2!"); + return (false); + } + } + + return (true); +} + +bool WncController::checkCellLink(void) +{ + string * pRespStr; + size_t pos; + int regSts; + int cmdRes1, cmdRes2; + + if (m_sState == WNC_OFF) + return (false); + + m_sState = WNC_ON_NO_CELL_LINK; + + if (m_sMoreDebugEnabled) + dbgPuts("<-------- Begin Cell Status ------------"); + + cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER + + // If no response, don't bother with more commands + if (cmdRes1 != WNC_AT_CMD_TIMEOUT) + cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs); // Check if SIM locked + else { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC No Response! --------->"); + + return (false); + } + + if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0)) + { + if (m_sMoreDebugEnabled) + { + if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT)) + dbgPuts("------------ WNC No Response! --------->"); + else + dbgPuts("------------ WNC Cmd Error! ----------->"); + } + + // If by a miracle it responds to the 2nd after the 1st, keep going + if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0)) + return (false); + } + + // If SIM Card not ready don't bother with commands! + if (pRespStr->find("CPIN: READY") == string::npos) + { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC SIM Problem! --------->"); + + return (false); + } + + // SIM card OK, now check for signal and cellular network registration + cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs); // Check if registered on network + if (cmdRes1 != WNC_AT_CMD_OK || pRespStr->size() == 0) + { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC +CREG? Fail! --------->"); + + return (false); + } + else + { + pos = pRespStr->find("CREG: "); + if (pos != string::npos) + { + // The registration is the 2nd arg in the comma separated list + *pRespStr = pRespStr->substr(pos+8, 1); + regSts = atoi(pRespStr->c_str()); + switch (regSts) { + case 1: + case 5: + case 6: + case 7: + m_sReadyForSMS = true; + break; + default: + m_sReadyForSMS = false; + dbgPuts("SMS Service Down!"); + } + + // 1 - registered home, 5 - registered roaming + if ((regSts != 1) && (regSts != 5)) + { + if (m_sMoreDebugEnabled) + dbgPuts("------ WNC Cell Link Down for Data! --->"); + + return (false); + } + } + + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC Ready ---------------->"); + } + + // If we made it this far and the WNC did respond, keep the ON state + if (m_sState != WNC_NO_RESPONSE) + m_sState = WNC_ON; + + return (true); +} + +int WncController::dbgPutsNoTime(const char * s, bool crlf) +{ + if (m_sDebugEnabled == true) { + int r = dbgWriteChars(s); + if (crlf == true) + return (dbgWriteChars("\r\n")); + else + return (r); + } + else + return 0; +}; + +int WncController::dbgPuts(const char * s, bool crlf) +{ + dbgPutsNoTime("[*] ", false); + dbgPutsNoTime(_to_string(getLogTimerTicks()), false); + dbgPutsNoTime(" ", false); + + int r = dbgPutsNoTime(s, false); + + if (crlf == true) + return (dbgPutsNoTime("", true)); + else + return (r); +}; + +void WncController::sendCmd(const char * cmd, bool crLf) +{ + puts(cmd); + if (crLf == true) + puts("\r\n"); +} + +void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf) +{ + while (n--) { + putc(*cmd++); + waitUs(wait_uS); + }; + if (crLf == true) { + putc('\r'); + waitUs(wait_uS); + putc('\n'); + waitUs(wait_uS); + } +} + +}; // End namespace WncController_fk +