This library controls the WNC. There is a derived class for usage from the K64F board.

Fork of WncControllerLibrary by Fred Kellerman

Revision:
36:d1a98d5f2bbd
Parent:
35:7c9d0f29ff7a
Child:
37:92acf8c20e6d
--- 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
+