I added functionality to get the RSSI, BER, and Cell Neighbor for reporting connection issues to M2X

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers WncController.cpp Source File

WncController.cpp

00001 /*
00002     Copyright (c) 2016 Fred Kellerman
00003  
00004     Permission is hereby granted, free of charge, to any person obtaining a copy
00005     of this software and associated documentation files (the "Software"), to deal
00006     in the Software without restriction, including without limitation the rights
00007     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008     copies of the Software, and to permit persons to whom the Software is
00009     furnished to do so, subject to the following conditions:
00010  
00011     The above copyright notice and this permission notice shall be included in
00012     all copies or substantial portions of the Software.
00013  
00014     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00020     THE SOFTWARE.
00021     
00022     @file          WncController.cpp
00023     @purpose       Controls WNC 14A2A Cellular Modem
00024     @version       1.0
00025     @date          July 2016
00026     @author        Fred Kellerman
00027     
00028     
00029     An Example of usage:
00030     
00031     WncControllerK64F mdm(&wncPinList, &mdmUart, &debugUart);
00032 
00033     mdm.enableDebug(true, true);
00034 
00035     if (false == mdm.powerWncOn("m2m.com.attz", 60)) {
00036         while(1);
00037     }
00038 
00039     // ICCID and MSISDN
00040     string iccid; string msisdn;
00041     if (mdm.getICCID(&iccid) == true) {
00042         if (mdm.convertICCIDtoMSISDN(iccid, &msisdn) == true) {
00043            // Send an SMS message (must use 15-digit MISDN number!)
00044            mdm.sendSMSText(msisdn.c_str(), "Hello from WNC Kit -> from WNC");
00045         }
00046     }
00047 
00048     // Get an IP address setup for the socket #1 (0 indexed))
00049     if (true == mdm.resolveUrl(0, "www.att.com"))
00050     {
00051         // Report server IP
00052         if (true == mdm.getIpAddr(0, ipAddrStr)) {
00053             debugUart.puts("Server IP: ");
00054             debugUart.puts(ipAddrStr);
00055             debugUart.puts("\r\n");
00056         }
00057 
00058         // Open Socket #1, TCP=true resolved IP on port 80:
00059         if (true == mdm.openSocket(0, 80, true)) {
00060             // Write some data
00061             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";
00062             if (true == mdm.write(0, dataStr, strlen((const char *)dataStr)))
00063             {
00064                 const uint8_t * myBuf;
00065                 mdm.setReadRetries(0, 20);
00066                 uint32_t n = mdm.read(0, &myBuf);
00067                 if (n > 0)
00068                     debugUart.printf("Read %d chars: %s\r\n", n, myBuf);
00069                 else
00070                     debugUart.puts("No read data!\r\n");
00071             }
00072         }
00073     }
00074     
00075 */
00076 
00077 
00078 #include <cstdlib>
00079 #include <cctype>
00080 #include <string.h>
00081 #include "WncController.h"
00082 
00083 namespace WncController_fk {
00084 
00085 /////////////////////////////////////////////////////
00086 // Static initializers
00087 /////////////////////////////////////////////////////
00088 WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS];
00089 const WncController::WncSocketInfo_s WncController::defaultSockStruct = { 0, false, "192.168.0.1", 80, 0, 25, true, 30 };
00090 
00091 WncController::WncState_e WncController::m_sState = WNC_OFF;
00092 uint16_t WncController::m_sCmdTimeoutMs = WNC_CMD_TIMEOUT_MS;
00093 string WncController::m_sApnStr = "NULL";
00094 string WncController::m_sWncStr;
00095 uint8_t WncController::m_sPowerUpTimeoutSecs = MAX_POWERUP_TIMEOUT;
00096 bool WncController::m_sDebugEnabled = false;
00097 bool WncController::m_sMoreDebugEnabled = false;
00098 bool WncController::m_sCheckNetStatus = false;   // Turn on internet status check between every command
00099 const char * const WncController::INVALID_IP_STR = "";
00100 bool WncController::m_sReadyForSMS = false;
00101 
00102 
00103 /**
00104  * C++ version 0.4 char* style "itoa":
00105  * Written by Lukás Chmela
00106  * Released under GPLv3.
00107  */
00108 static char* itoa(int64_t value, char* result, int base)
00109 {
00110     // check that the base is valid
00111     if ( base < 2 || base > 36 ) {
00112         *result = '\0';
00113         return result;
00114     }
00115 
00116     char* ptr = result, *ptr1 = result, tmp_char;
00117     int64_t tmp_value;
00118 
00119     do {
00120         tmp_value = value;
00121         value /= base;
00122         *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
00123     } while ( value );
00124 
00125     // Apply negative sign
00126     if ( tmp_value < 0 )
00127         *ptr++ = '-';
00128 
00129     *ptr-- = '\0';
00130 
00131     while ( ptr1 < ptr ) {
00132         tmp_char = *ptr;
00133         *ptr-- = *ptr1;
00134         *ptr1++ = tmp_char;
00135     }
00136 
00137     return result;
00138 }
00139 
00140 const char * WncController::_to_string(int64_t value)
00141 {
00142     static char str[21];  // room for signed 64-bit + null
00143     itoa(value, str, 10);
00144     return (str);
00145 }
00146 
00147 const char * WncController::_to_hex_string(uint8_t value)
00148 {
00149     static char str[3];   // room for 8-bit + null
00150     itoa(value, str, 16);
00151     return (str);
00152 }
00153 
00154 WncController::WncController(void)
00155 {
00156     for(unsigned i; i<MAX_NUM_WNC_SOCKETS; i++)
00157         m_sSock[i] = defaultSockStruct;
00158 }
00159 
00160 void WncController::enableDebug(bool on, bool moreDebugOn)
00161 {
00162     m_sDebugEnabled = on;
00163     m_sMoreDebugEnabled = moreDebugOn;
00164 }
00165 
00166 WncController::WncState_e WncController::getWncStatus(void)
00167 {
00168     return (m_sState);
00169 }
00170 
00171 int16_t WncController::getDbmRssi(void)
00172 {
00173     int16_t rssi, ber;
00174     if (at_getrssiber_wnc(&rssi, &ber) == true)
00175         return (rssi);
00176     else
00177         return (99);
00178 }
00179 
00180 int16_t WncController::get3gBer(void)
00181 {
00182     int16_t rssi, ber;
00183     if (at_getrssiber_wnc(&rssi, &ber) == true)
00184         return (ber);
00185     else
00186         return (99);
00187 }
00188 
00189 bool WncController::powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs)
00190 {
00191     dbgPuts("Waiting for WNC to Initialize...");
00192     m_sPowerUpTimeoutSecs = powerUpTimeoutSecs;
00193     m_sState = WNC_ON_NO_CELL_LINK;  // Turn soft on to allow "AT" for init to be sent!
00194     if (initWncModem(powerUpTimeoutSecs) == true) {
00195         // Set the Apn
00196         setApnName(apn);
00197         if (false == softwareInitMdm()) {
00198             dbgPuts("Software init failed!");
00199             m_sState = WNC_OFF;
00200         }
00201     }
00202     else {
00203         dbgPuts("Power up failed!");
00204         m_sState = WNC_OFF;
00205     }
00206     
00207     return ((m_sState == WNC_ON) || (m_sState == WNC_ON_NO_CELL_LINK));
00208 }
00209 
00210 size_t WncController::sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout)
00211 {
00212     string * respStr;
00213     
00214     if (sizeRespBuf > 0) {
00215         AtCmdErr_e r = at_send_wnc_cmd(cmd, &respStr, ms_timeout);
00216         strncpy(resp, respStr->c_str(), sizeRespBuf);
00217         if (respStr->size() > sizeRespBuf)
00218             dbgPuts("sendCustomCmd truncated!");
00219             
00220         return (respStr->size());
00221     }
00222     
00223     dbgPuts("sendCustomCmd: would have overrun!");
00224     
00225     return (0);
00226 }
00227 
00228 bool WncController::pingUrl(const char * url)
00229 {
00230     string ipAddr;
00231     
00232     if (true == at_dnsresolve_wnc(url, &ipAddr))
00233         return (pingIp(ipAddr.c_str()));
00234     else
00235         dbgPuts("pingUrl DNS resolve: failed!");
00236         
00237     return (false);
00238 }
00239 
00240 bool WncController::pingIp(const char * ip)
00241 {
00242     if (true == at_ping_wnc(ip))
00243         return (true);
00244     else
00245         dbgPuts("pingIp: failed!");
00246     
00247     return (false);
00248 }
00249 
00250 bool WncController::getWncNetworkingStats(WncIpStats * s)
00251 {
00252     return (at_get_wnc_net_stats(s));
00253 }
00254 
00255 bool WncController::getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR])
00256 {
00257     if (numSock < MAX_NUM_WNC_SOCKETS) {
00258         strncpy(myIpAddr, m_sSock[numSock].myIpAddressStr.c_str(), MAX_LEN_IP_STR);
00259         myIpAddr[MAX_LEN_IP_STR - 1] = '\0';
00260         return (true);
00261     }
00262     else {
00263         myIpAddr[0] = '\0';
00264         return (false);
00265     }
00266 }
00267 
00268 bool WncController::setApnName(const char * const apnStr)
00269 {
00270     if (at_setapn_wnc(apnStr) == true)
00271     {
00272         m_sApnStr = apnStr;
00273         return (true);
00274     }
00275     else
00276         return (false);
00277 }
00278 
00279 bool WncController::resolveUrl(uint16_t numSock, const char * url)
00280 {
00281     bool cmdRes;
00282     
00283     if (numSock < MAX_NUM_WNC_SOCKETS) {
00284         if (strlen(url) > 0) {
00285             cmdRes = at_dnsresolve_wnc(url, &m_sSock[numSock].myIpAddressStr);
00286             if (cmdRes == false)
00287                 dbgPuts("Cannot resolve URL!");
00288             return (cmdRes);
00289         }
00290         else
00291             dbgPuts("Invalid URL");
00292     }
00293     else
00294         dbgPuts("Invalid Sock num!");
00295 
00296     return (false);
00297 }
00298 
00299 bool WncController::setIpAddr(uint16_t numSock, const char * ipStr)
00300 {
00301     if (numSock < MAX_NUM_WNC_SOCKETS) {
00302         m_sSock[numSock].myIpAddressStr = ipStr;
00303         return (true);
00304     }
00305     else {
00306         dbgPuts("Bad socket num!");
00307         return (false);
00308     }
00309 }
00310 
00311 void WncController::setWncCmdTimeout(uint16_t toMs)
00312 {
00313     m_sCmdTimeoutMs = toMs;
00314 }
00315 
00316 bool WncController::openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec)
00317 {
00318     if (resolveUrl(numSock, url) == true)
00319         return (openSocket(numSock, port, tcp, timeOutSec));
00320     
00321     return (false);
00322 }
00323 
00324 bool WncController::openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec)
00325 {
00326     if (setIpAddr(numSock, ipAddr) == true)
00327         return (openSocket(numSock, port, tcp, timeOutSec));
00328     
00329     return (false);
00330 }
00331  
00332 bool WncController::openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec)
00333 {
00334     if (numSock < MAX_NUM_WNC_SOCKETS) {
00335         // IPV4 ip addr sanity check!
00336         size_t lenIpStr = m_sSock[numSock].myIpAddressStr.size();
00337         if (lenIpStr < 7 || lenIpStr > 15) {
00338             dbgPuts("Invalid IP Address!");
00339             return (false);
00340         }
00341         
00342         // Already open ? Must close if want to re-open with new settings.
00343         if (m_sSock[numSock].open == true) {
00344             dbgPuts("Socket already open, close then re-open!");
00345             if (true == at_sockclose_wnc(m_sSock[numSock].numWncSock))
00346                 m_sSock[numSock].open = false;
00347             else
00348                 return (false);
00349         }
00350         
00351         m_sSock[numSock].myPort = port;
00352         m_sSock[numSock].isTcp = tcp;
00353         m_sSock[numSock].timeOutSec = timeOutSec;
00354         
00355         int16_t numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), port, numSock, tcp, timeOutSec);
00356         m_sSock[numSock].numWncSock = numWncSock;
00357         if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS)
00358             m_sSock[numSock].open = true;
00359         else {
00360             m_sSock[numSock].open = false;
00361             dbgPuts("Socket open fail!!!!");
00362             
00363             // If the modem is not responding don't bother it.
00364             if (WNC_NO_RESPONSE != getWncStatus()) {
00365                 // Work-around.  If the sock open fails it needs to be told
00366                 // to close.  If 6 sock opens happen with a fail, it further
00367                 // crashes the WNC.  Not sure why the sock won't open.
00368                 at_sockclose_wnc(m_sSock[numSock].numWncSock);
00369             }
00370         }
00371     }
00372     else {
00373         dbgPuts("Bad socket num or IP!");
00374         return (false);
00375     }
00376 
00377     return (m_sSock[numSock].open);
00378 }
00379 
00380 bool WncController::sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp)
00381 {
00382     bool result = true;
00383     
00384     AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, m_sSock[numSock].numWncSock, isTcp);
00385     if (cmdRes != WNC_AT_CMD_OK) {
00386         if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
00387         {
00388             // This may throw away any data that hasn't been written out of the WNC
00389             //  but at this point with the way the WNC currently works we have
00390             //  no choice.
00391             closeOpenSocket(numSock);
00392         }
00393         result = false;
00394     }
00395     
00396     return (result);
00397 }
00398 
00399 bool WncController::write(uint16_t numSock, const uint8_t * s, uint32_t n)
00400 {
00401     bool result;
00402     
00403     if (numSock < MAX_NUM_WNC_SOCKETS) {
00404         if (m_sSock[numSock].open == true) {
00405             if (n <= MAX_WNC_WRITE_BYTES) {
00406                 result = sockWrite(s, n, numSock, m_sSock[numSock].isTcp);
00407             }
00408             else {
00409                 uint16_t rem = n % MAX_WNC_WRITE_BYTES;
00410                 while (n >= MAX_WNC_WRITE_BYTES) {
00411                     n -= MAX_WNC_WRITE_BYTES;
00412                     result = sockWrite(s, MAX_WNC_WRITE_BYTES, numSock, m_sSock[numSock].isTcp);
00413                     if (result == false) {
00414                         n = 0;
00415                         rem = 0;
00416                         dbgPuts("Sock write fail!");
00417                     }
00418                     else
00419                         s += MAX_WNC_WRITE_BYTES;
00420                 }
00421                 if (rem > 0)
00422                     result = sockWrite(s, rem, numSock, m_sSock[numSock].isTcp);                
00423             }
00424         }
00425         else {
00426             dbgPuts("Socket is closed for write!");
00427             result = false;
00428         }
00429     }
00430     else {
00431         dbgPuts("Bad socket num!");
00432         result = false;
00433     }
00434 
00435     return (result);
00436 }
00437 
00438 size_t WncController::read(uint16_t numSock, const uint8_t ** readBuf)
00439 {
00440     static string theBuf;
00441     string readStr;
00442     
00443     theBuf.erase();  // Clean-up from last time
00444 
00445     if (numSock < MAX_NUM_WNC_SOCKETS) {
00446         if (m_sSock[numSock].open == true) {
00447             uint8_t   i = m_sSock[numSock].readRetries;
00448             uint16_t to = m_sSock[numSock].readRetryWaitMs;
00449             bool foundData = false;
00450             do {
00451                 AtCmdErr_e cmdRes;
00452                 cmdRes = at_sockread_wnc(&readStr, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp);
00453                 if (WNC_AT_CMD_OK == cmdRes) {
00454                     // This will let this loop read until the socket data is
00455                     //  empty.  If no data, then wait the retry amount of time.
00456                     if (readStr.size() > 0) {
00457                         theBuf += readStr;
00458                         foundData = true;
00459                         i = 1;
00460                     }
00461                     else {
00462                         // Once data is found start returning it asap
00463                         if (foundData == false)
00464                             waitMs(to);
00465                     }
00466                 }
00467                 else {
00468                     theBuf += readStr; // Append what if any we got before it errored.
00469                     dbgPuts("Sockread failed!");
00470                     if (WNC_NO_RESPONSE == getWncStatus()) {
00471                         i = 0;
00472                     }
00473                     else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
00474                     {
00475                         // This may throw away any data that hasn't been read out of the WNC
00476                         //  but at this point with the way the WNC currently works we have
00477                         //  no choice.
00478                         closeOpenSocket(numSock);
00479                         i = 0;
00480                     }
00481                     else
00482                         waitMs(to);
00483                 }
00484             } while (i-- > 0);
00485         }
00486         else {
00487             dbgPuts("Socket is closed for read");
00488         }
00489     }
00490     else {
00491         dbgPuts("Bad socket num!");
00492     }
00493 
00494     *readBuf = (const uint8_t *)theBuf.c_str();
00495 
00496     return (theBuf.size());
00497 }
00498 
00499 size_t WncController::read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen)
00500 {
00501     uint32_t numCopied = 0;
00502     
00503     if (numSock < MAX_NUM_WNC_SOCKETS) {
00504         if (m_sSock[numSock].open == true) {
00505             uint8_t   i = m_sSock[numSock].readRetries;
00506             uint16_t to = m_sSock[numSock].readRetryWaitMs;
00507             bool foundData = false;
00508             uint16_t numRead;
00509             do {
00510                 AtCmdErr_e cmdRes;
00511                 if (maxReadBufLen < MAX_WNC_READ_BYTES)
00512                     cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp);
00513                 else
00514                     cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp);
00515 
00516                 if (WNC_AT_CMD_OK == cmdRes) {
00517                     // This will let this loop read until the socket data is
00518                     //  empty.  If no data, then wait the retry amount of time.
00519                     if (numRead > 0) {
00520                         foundData = true;
00521                         i = 1;
00522                         if (numRead <= maxReadBufLen) {
00523                             maxReadBufLen -= numRead;
00524                             numCopied     += numRead;
00525                             readBuf       += numRead;
00526                         }
00527                         else {
00528                             i = 0; // No more room for data!
00529                             dbgPutsNoTime("No more room for read data!");
00530                         } 
00531                     }
00532                     else {
00533                         // Once data is found start returning it asap
00534                         if (foundData == false)
00535                             waitMs(to);
00536                     }
00537                 }
00538                 else {
00539                     dbgPuts("Sockread failed!");
00540                     if (WNC_NO_RESPONSE == getWncStatus()) {
00541                         i = 0;
00542                     }
00543                     else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
00544                     {
00545                         // This may throw away any data that hasn't been read out of the WNC
00546                         //  but at this point with the way the WNC currently works we have
00547                         //  no choice.
00548                         closeOpenSocket(numSock);
00549                         i = 0;
00550                     }
00551                     else
00552                         waitMs(to);
00553                 }
00554             } while ((i-- > 0) && (maxReadBufLen > 0));
00555         }
00556         else {
00557             dbgPuts("Socket is closed for read");
00558         }
00559     }
00560     else {
00561         dbgPuts("Bad socket num!");
00562     }
00563 
00564     return (numCopied);
00565 }
00566 
00567 void WncController::setReadRetries(uint16_t numSock, uint16_t retries)
00568 {
00569     if (numSock < MAX_NUM_WNC_SOCKETS)
00570         m_sSock[numSock].readRetries = retries;
00571     else
00572         dbgPuts("Bad socket num!");
00573 }
00574 
00575 void WncController::setReadRetryWait(uint16_t numSock, uint16_t readRetryWaitMs)
00576 {
00577     if (numSock < MAX_NUM_WNC_SOCKETS)
00578         m_sSock[numSock].readRetryWaitMs = readRetryWaitMs;
00579     else
00580         dbgPuts("Bad socket num!");
00581 }
00582 
00583 bool WncController::closeSocket(uint16_t numSock)
00584 {
00585     if (numSock < MAX_NUM_WNC_SOCKETS) {
00586 
00587         if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock))
00588             dbgPuts("Sock close may not have closed!");
00589                 
00590         // Even with an error the socket could have closed,
00591         //  can't tell for sure so just soft close it for now.
00592         m_sSock[numSock].open = false;
00593     }
00594     else {
00595         dbgPuts("Bad socket num!");
00596     }
00597 
00598     return (m_sSock[numSock].open == false);
00599 }
00600 
00601 size_t WncController::mdmGetline(string * buff, int timeout_ms)
00602 {
00603     char chin = '\0';
00604     char chin_last;
00605     size_t len = 0;
00606 
00607     startTimerB();
00608     while ((len <= MAX_LEN_WNC_CMD_RESPONSE) && (getTimerTicksB_mS() < timeout_ms)) {
00609         if (charReady()) {
00610             chin_last = chin;
00611             chin = getc();
00612             if (isprint(chin)) {
00613                 *buff += chin;
00614                 len++;  // Bound the copy length to something reaonable just in case
00615                 continue;
00616             }
00617             else if ((('\r' == chin_last) && ('\n' == chin)) || (('\n' == chin_last) && ('\r' == chin)))  {
00618                 break;
00619             }
00620         }
00621     }
00622     stopTimerB();
00623     
00624     if (len > MAX_LEN_WNC_CMD_RESPONSE)
00625         dbgPuts("Max cmd length reply exceeded!");
00626 
00627     return (len);
00628 }
00629 
00630 bool WncController::softwareInitMdm(void)
00631 {
00632   static bool reportStatus = true;
00633   unsigned i;
00634   
00635   if (checkCellLink() == true) {
00636       if (reportStatus == false) {
00637           dbgPuts("Re-connected to cellular network!");
00638           reportStatus = true;
00639       }
00640       
00641       // WNC has SIM and registered on network so 
00642       //  soft initialize the WNC.
00643       for (i = 0; i < WNC_SOFT_INIT_RETRY_COUNT; i++)
00644           if (at_init_wnc() == true)
00645               break;
00646                   
00647       // If it did not respond try a hardware init
00648       if (i == WNC_SOFT_INIT_RETRY_COUNT)
00649       {
00650           at_reinitialize_mdm();
00651           return (at_init_wnc(true));  // Hard reset occurred so make it go through the software init();
00652       }
00653       else
00654           return (true);
00655   }
00656   else
00657   {
00658       if (reportStatus == true) {
00659            dbgPuts("Not connected to cellular network!");
00660            reportStatus = false;
00661       }
00662       return (false);
00663   }
00664 }
00665 
00666 WncController::AtCmdErr_e WncController::sendWncCmd(const char * const s, string ** r, int ms_timeout)
00667 {
00668     if (checkCellLink() == false) {
00669         static string noRespStr;
00670 
00671         // Save some run-time!
00672         if (m_sDebugEnabled)
00673         {
00674             dbgPuts("FAIL send cmd: ", false);
00675             if (m_sMoreDebugEnabled && m_sDebugEnabled) {
00676                 dbgPutsNoTime(s);
00677             }
00678             else {
00679                 size_t n = strlen(s);
00680                 if (n <= WNC_TRUNC_DEBUG_LENGTH) {
00681                     dbgPutsNoTime(s);
00682                 }
00683                 else {
00684                     string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2);
00685                     truncStr += "..";
00686                     truncStr += &s[n-(WNC_TRUNC_DEBUG_LENGTH/2)];
00687                     dbgPutsNoTime(truncStr.c_str());
00688                 }
00689             }    
00690         }
00691         
00692         noRespStr.erase();
00693         *r = &noRespStr;
00694 
00695         return (WNC_AT_CMD_NO_CELL_LINK);
00696     }
00697     
00698     if (m_sCheckNetStatus)
00699     {
00700         if (m_sMoreDebugEnabled)
00701             dbgPuts("[---------- Network Status -------------");
00702         string * pRespStr;
00703         at_send_wnc_cmd("AT@SOCKDIAL?", &pRespStr, m_sCmdTimeoutMs);
00704         if (m_sMoreDebugEnabled)
00705            dbgPuts("---------------------------------------]");
00706     }
00707     
00708     // If WNC ready, send user command
00709     return (at_send_wnc_cmd(s, r, ms_timeout));
00710 }
00711 
00712 WncController::AtCmdErr_e WncController::at_send_wnc_cmd(const char * s, string ** r, int ms_timeout)
00713 {
00714     // Save some run-time!
00715     if (m_sDebugEnabled)
00716     {
00717         if (m_sMoreDebugEnabled) {
00718            dbgPuts("TX: ", false); dbgPutsNoTime(s);
00719         }
00720         else {
00721             if (m_sDebugEnabled) {  // Save some run-time!
00722                 size_t n = strlen(s);
00723                 if (n <= WNC_TRUNC_DEBUG_LENGTH) {
00724                     dbgPuts("TX: ", false); dbgPutsNoTime(s);
00725                 }
00726                 else {
00727                     string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2);
00728                     truncStr += "..";
00729                     truncStr += &s[n - (WNC_TRUNC_DEBUG_LENGTH/2)];
00730                     dbgPuts("TX: ", false); dbgPutsNoTime(truncStr.c_str());
00731                 }
00732             }
00733         }
00734     }
00735 
00736     AtCmdErr_e atResult = mdmSendAtCmdRsp(s, ms_timeout, &m_sWncStr);
00737     *r = &m_sWncStr;   // Return a pointer to the static string
00738       
00739     if (atResult != WNC_AT_CMD_TIMEOUT) {
00740         // If a prior command timed out but a new one works then
00741         //  change the state back to ON.  We don't know here in this 
00742         //  method if the Cell Link is good so assume it is. When a command
00743         //  that depends on the cell link is made it will update the state.
00744         if (m_sState == WNC_NO_RESPONSE)
00745             m_sState = WNC_ON;
00746             
00747         // Save some run-time!
00748         if (m_sDebugEnabled)
00749         {        
00750             dbgPuts("RX: ", false);
00751             if (m_sMoreDebugEnabled) {
00752                 dbgPutsNoTime(m_sWncStr.c_str());
00753             }
00754             else {
00755                 if (m_sWncStr.size() <= WNC_TRUNC_DEBUG_LENGTH) {
00756                     dbgPutsNoTime(m_sWncStr.c_str());
00757                 }
00758                 else {
00759                     string truncStr = m_sWncStr.substr(0,WNC_TRUNC_DEBUG_LENGTH/2) + "..";
00760                     truncStr += m_sWncStr.substr(m_sWncStr.size() - (WNC_TRUNC_DEBUG_LENGTH/2), WNC_TRUNC_DEBUG_LENGTH/2);
00761                     dbgPutsNoTime(truncStr.c_str());
00762                 }
00763             }
00764         }
00765     }
00766     else {
00767         m_sState = WNC_NO_RESPONSE;
00768         dbgPuts("AT Cmd TIMEOUT!");
00769         dbgPuts("RX: ", false); dbgPutsNoTime(m_sWncStr.c_str());
00770     }
00771     
00772     return (atResult);
00773 }
00774 
00775 void WncController::closeOpenSocket(uint16_t numSock)
00776 {
00777     // Try to open and close the socket
00778     do {
00779         dbgPuts("Try to close and re-open socket");
00780         if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) {
00781             if (WNC_NO_RESPONSE == getWncStatus()) {
00782                 dbgPuts("No response for closeOpenSocket1");
00783                 return ;
00784             }
00785         }        
00786 
00787         int numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec);
00788         m_sSock[numSock].numWncSock = numWncSock;
00789         if (numWncSock > 0 && numWncSock <= MAX_NUM_WNC_SOCKETS)
00790             m_sSock[numSock].open = true;
00791         else {
00792             m_sSock[numSock].open = false;
00793             dbgPuts("Failed to re-open socket!");
00794         }
00795         
00796         if (WNC_NO_RESPONSE == getWncStatus()) {
00797             dbgPuts("No response for closeOpenSocket2");
00798             return ;
00799         }
00800     } while (m_sSock[numSock].open == false);
00801 }
00802 
00803 bool WncController::getICCID(string * iccid)
00804 {
00805     if (at_geticcid_wnc(iccid) == false) {
00806         dbgPuts("getICCID error!");
00807         return (false);
00808     }
00809     
00810     return (true);
00811 }
00812 
00813 bool WncController::at_geticcid_wnc(string * iccid)
00814 {
00815     string * respStr;
00816     
00817     iccid->erase();
00818     
00819     AtCmdErr_e r = at_send_wnc_cmd("AT%CCID", &respStr, m_sCmdTimeoutMs);
00820 
00821     if (r != WNC_AT_CMD_OK || respStr->size() == 0)
00822         return (false);
00823 
00824     size_t pos = respStr->find("AT%CCID");
00825     if (pos == string::npos)
00826         return (false);
00827     
00828     size_t posOK = respStr->rfind("OK");
00829     if (posOK == string::npos)
00830         return (false);
00831 
00832     pos += 7; // Advanced to the number
00833     *iccid = respStr->substr(pos, posOK - pos);
00834     
00835     return (true);
00836 }
00837 
00838 bool WncController::convertICCIDtoMSISDN(const string & iccid, string * msisdn)
00839 {
00840     msisdn->erase();
00841     
00842     if (iccid.size() != 20 && iccid.size() != 19) {
00843         dbgPuts("Invalid ICCID length!");
00844         return (false);
00845     }
00846  
00847     *msisdn = "882350";
00848     
00849     if (iccid.size() == 20)
00850         *msisdn += iccid.substr(10,iccid.size() - 11);
00851     else
00852         *msisdn += iccid.substr(10,iccid.size() - 10);
00853     
00854     return (true);
00855 }
00856 
00857 bool WncController::sendSMSText(const char * const phoneNum, const char * const text)
00858 {
00859     if (at_sendSMStext_wnc(phoneNum, text) == true)
00860         return (true);
00861     else {
00862         dbgPuts("sendSMSText: Failed!");
00863         return (false);
00864     }
00865 }
00866 
00867 bool WncController::readSMSLog(struct WncSmsList * log)
00868 {
00869     string * logStr;
00870     uint16_t i;
00871     
00872     if (at_readSMSlog_wnc(&logStr) == false) {
00873         dbgPuts("readSMSLog: Failed!");
00874         return (false);
00875     }
00876     
00877     // Clean slate    
00878     log->msgCount = 0;
00879 
00880     if (logStr->size() == 0)
00881         return (false);
00882 
00883     // Pick out the stuff from the string and convert to struct
00884     string s;
00885     size_t pos2;
00886     size_t pos = logStr->find("+CMGL:");
00887         
00888     for(i=0; i<MAX_WNC_SMS_MSG_SLOTS; i++) {
00889         // Start with a clean slate, let parsing fill out later.
00890         log->e[i].unread = false;
00891         log->e[i].incoming = false;
00892         log->e[i].unsent = false;
00893         log->e[i].pduMode = false;
00894         log->e[i].msgReceipt = false;
00895 
00896         log->e[i].idx = logStr->at(pos + 7);
00897         if (pos == string::npos)
00898             return (false);
00899         pos2 = logStr->find(",\"", pos);
00900         if (pos2 == string::npos) {
00901             // If the WNC acts wrong and receives a PDU mode
00902             //  SMS there will not be any quotes in the response,
00903             //  just take the whole reply and make it the message body for
00904             //  now, mark it as an unread message, set the pdu flag!
00905             log->e[log->msgCount].unread = true;
00906             log->e[log->msgCount].pduMode = true;
00907             log->msgCount++;
00908 
00909             pos2 = logStr->find("+CMGL", pos + 5);
00910             if (pos2 == string::npos) {
00911                 pos2 = logStr->find("OK", pos + 5);
00912                 if (pos2 == string::npos) {
00913                     dbgPuts("Strange SMS Log Ending!");
00914                     return (false);
00915                 }
00916                 i = MAX_WNC_SMS_MSG_SLOTS;
00917             }
00918             log->e[log->msgCount].msg = logStr->substr(0, pos2 - pos);
00919             pos = pos2;  // for loop starts off expecting pos to point to next log msg
00920             continue;
00921         }
00922         pos += 2;  // Advance to the text we want
00923         pos2 = logStr->find("\",", pos);
00924         if ((pos2 == string::npos) || (pos >= pos2))
00925             return (false);
00926                     
00927         // Setup attributes
00928         s = logStr->substr(pos, pos2 - pos);
00929         if (s.find("REC READ") != string::npos)
00930             log->e[i].incoming = true;
00931         if (s.find("REC UNREAD") != string::npos) {
00932             log->e[i].unread = true;
00933             log->e[i].incoming = true;
00934         }
00935         if (s.find("STO UNSENT") != string::npos)
00936             log->e[i].unsent = true;
00937         if (logStr->find(",,") == string::npos)
00938             log->e[i].msgReceipt = true;
00939             
00940         // Tele number
00941         pos2 = logStr->find(",\"", pos2);
00942         if (pos2 == string::npos)
00943             return (false);  
00944         pos2 += 2;  // Advance to next field
00945         pos = logStr->find("\",", pos2);
00946         if ((pos == string::npos) || (pos2 > pos))
00947             return (false);
00948         if (pos == pos2)
00949             log->e[i].number.erase();
00950         else    
00951             log->e[i].number = logStr->substr(pos2, pos - pos2);
00952         
00953         // Date
00954         pos = logStr->find(",\"", pos);
00955         if (pos == string::npos)
00956             return (false);
00957         pos += 2; // Beginning of date field
00958         pos2 = logStr->find(",", pos); // End of timestamp field
00959         if ((pos2 == string::npos) || (pos > pos2))
00960             return (false);
00961         if (pos == pos2)
00962             log->e[i].date.erase();
00963         else
00964             log->e[i].date = logStr->substr(pos, pos2 - pos);
00965 
00966         // Timestamp
00967         pos = logStr->find("\",", pos2); // End of timestamp
00968         if (pos == string::npos)
00969             return (false);
00970         pos2 += 1; // Beginning of time field
00971         if (pos < pos2)
00972             return (false);
00973         if (pos == pos2)
00974             log->e[i].time.erase();
00975         else
00976             log->e[i].time = logStr->substr(pos2, pos - pos2);
00977         
00978         // Message field
00979         
00980         // We don't know how many messages we have so the next search
00981         // could end with +CMGL or OK.
00982         pos += 2;  // Advanced to message text
00983         pos2 = logStr->find("+CMGL", pos);
00984         if (pos2 == string::npos) {
00985             pos2 = logStr->find("OK", pos);
00986             if (pos2 == string::npos) {
00987                 dbgPuts("Strange SMS Log Ending!");
00988                 return (false);
00989             }
00990             i = MAX_WNC_SMS_MSG_SLOTS; // break
00991         }
00992         if (pos > pos2)
00993             return (false);
00994         if (pos == pos2)
00995             log->e[log->msgCount].msg.erase();
00996         else
00997             log->e[log->msgCount].msg = logStr->substr(pos, pos2 - pos);
00998 
00999         log->msgCount++;  // Message complete
01000     }    
01001     
01002     return (true);
01003 }
01004 
01005 bool WncController::readUnreadSMSText(struct WncSmsList * w, bool deleteRead)
01006 {
01007     struct WncController::WncSmsList tmp;
01008     
01009     if (readSMSLog(&tmp) == false)
01010         return (false);
01011     
01012     w->msgCount = 0;
01013     for(uint16_t i = 0; i < tmp.msgCount; i++) {
01014         if (tmp.e[i].unread == true) {
01015             w->e[w->msgCount] = tmp.e[i];
01016             w->msgCount++;
01017             if (deleteRead == true) {
01018                 // Clean up message that was copied out and read
01019                 deleteSMSTextFromMem(w->e[i].idx);
01020             }
01021         }
01022     }
01023     
01024     return (w->msgCount > 0);
01025 }
01026 
01027 size_t WncController::getSignalQuality(const char ** log)
01028 {
01029     size_t n;
01030 
01031     n = at_getSignalQuality_wnc(log);
01032     if (n == 0)
01033         dbgPuts("readSMSText: Failed!");
01034         
01035     return (n);
01036 }
01037 
01038 size_t WncController::at_getSignalQuality_wnc(const char ** log)
01039 {
01040     string * pRespStr;
01041     static string logStr;
01042     
01043     logStr.erase();
01044 
01045     if (at_send_wnc_cmd("AT%MEAS=\"0\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01046         logStr = *pRespStr;
01047         logStr += "\r\n";
01048     }
01049     else
01050         dbgPuts("AT%MEAS=0: failed!");
01051         
01052     if (at_send_wnc_cmd("AT%MEAS=\"1\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01053         logStr += *pRespStr;
01054         logStr += "\r\n";
01055     }
01056     else
01057         dbgPuts("AT%MEAS=1: failed!");
01058 
01059     if (at_send_wnc_cmd("AT%MEAS=\"2\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01060         logStr += *pRespStr;
01061         logStr += "\r\n";
01062     }
01063     else
01064         dbgPuts("AT%MEAS=2: failed!");
01065 
01066     if (at_send_wnc_cmd("AT%MEAS=\"3\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01067         logStr += *pRespStr;
01068         logStr += "\r\n";
01069     }
01070     else
01071         dbgPuts("AT%MEAS=3: failed!");
01072 
01073     if (at_send_wnc_cmd("AT%MEAS=\"4\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01074         logStr += *pRespStr;
01075         logStr += "\r\n";
01076     }
01077     else
01078         dbgPuts("AT%MEAS=4: failed!");
01079 
01080     if (at_send_wnc_cmd("AT%MEAS=\"5\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01081         logStr += *pRespStr;
01082         logStr += "\r\n";
01083     }
01084     else
01085         dbgPuts("AT%MEAS=5: failed!");
01086         
01087     if (at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01088         logStr += *pRespStr;
01089         logStr += "\r\n";
01090     }
01091     else
01092         dbgPuts("AT%MEAS=8: failed!");
01093         
01094     if (at_send_wnc_cmd("AT%MEAS=\"98\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01095         logStr += *pRespStr;
01096         logStr += "\r\n";
01097     }
01098     else
01099         dbgPuts("AT%MEAS=98: failed!");
01100 
01101     *log = logStr.c_str();
01102     
01103     return (logStr.size());
01104 }
01105 
01106 bool WncController::getTimeDate(struct WncDateTime * tod)
01107 {
01108     if (at_gettimedate_wnc(tod) == true)
01109         return (true);
01110     else {
01111         dbgPuts("Get time date failed!");
01112         return (false);
01113     }
01114 }
01115 
01116 bool WncController::at_ping_wnc(const char * ip)
01117 {
01118     string * pRespStr;
01119     string cmdStr = "AT@PINGREQ=\"";
01120     cmdStr += ip;
01121     cmdStr += "\"";
01122     return (at_send_wnc_cmd(cmdStr.c_str(), &pRespStr, WNC_PING_CMD_TIMEOUT_MS) == WNC_AT_CMD_OK);
01123 }
01124 
01125 bool WncController::at_gettimedate_wnc(struct WncDateTime * tod)
01126 {
01127     string * pRespStr;
01128     char * pEnd;
01129 
01130     if (at_send_wnc_cmd("AT+CCLK?", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
01131         if (pRespStr->size() > 0) {
01132             size_t pos1 = pRespStr->find("+CCLK:");
01133             if (pos1 != string::npos) {
01134                 pEnd = (char *)pRespStr->c_str() + pos1 + 8;
01135                 tod->year  = strtol(pEnd, &pEnd, 10);
01136                 tod->month = strtol(pEnd+1, &pEnd, 10);
01137                 tod->day   = strtol(pEnd+1, &pEnd, 10);
01138                 tod->hour  = strtol(pEnd+1, &pEnd, 10);
01139                 tod->min   = strtol(pEnd+1, &pEnd, 10);
01140                 tod->sec   = strtol(pEnd+1, &pEnd, 10);
01141                 return (true);
01142             }
01143         }
01144     }
01145 
01146     return (false);
01147 }
01148 
01149 bool WncController::at_get_wnc_net_stats(WncIpStats * s)
01150 {
01151     string * pRespStr;
01152     AtCmdErr_e cmdRes = at_send_wnc_cmd("AT+CGCONTRDP=1", &pRespStr, m_sCmdTimeoutMs);
01153     
01154     if (WNC_AT_CMD_OK == cmdRes) {
01155         if (pRespStr->size() > 0) {
01156             memset((void*)s, '\0', sizeof(*s));  // Clean-up
01157             string ss;
01158             size_t pe;
01159             size_t ps = pRespStr->rfind("\"");
01160             if (ps != string::npos) {
01161                 ps += 2;  // Skip the , after the "
01162                 pe = ps;
01163 
01164                 pe = pRespStr->find(".", pe);
01165                 if (pe == string::npos)
01166                     return (false);
01167                 else
01168                     pe += 1;
01169                 pe = pRespStr->find(".", pe);
01170                 if (pe == string::npos)
01171                     return (false);
01172                 else
01173                     pe += 1;
01174                 pe = pRespStr->find(".", pe);
01175                 if (pe == string::npos)
01176                     return (false);
01177                 else
01178                     pe += 1;
01179                 pe = pRespStr->find(".", pe);
01180                 if (pe == string::npos)
01181                     return (false);
01182                 else
01183                     pe += 1;
01184 
01185                 ss = pRespStr->substr(ps, pe - 1 - ps);
01186                 strncpy(s->ip, ss.c_str(), MAX_LEN_IP_STR);
01187                 s->ip[MAX_LEN_IP_STR - 1] = '\0';
01188                 ps = pe;
01189 
01190                 pe = pRespStr->find(".", pe);
01191                 if (pe == string::npos)
01192                     return (false);
01193                 else
01194                     pe += 1;
01195                 pe = pRespStr->find(".", pe);
01196                 if (pe == string::npos)
01197                     return (false);
01198                 else
01199                     pe += 1;
01200                 pe = pRespStr->find(".", pe);
01201                 if (pe == string::npos)
01202                     return (false);
01203                 else
01204                     pe += 1;
01205                 pe = pRespStr->find(",", pe);
01206 
01207                 ss = pRespStr->substr(ps, pe - ps);
01208                 strncpy(s->mask, ss.c_str(), MAX_LEN_IP_STR);
01209                 s->mask[MAX_LEN_IP_STR - 1] = '\0';
01210                 ps = pe + 1;
01211 
01212                 pe = pRespStr->find(".", pe);
01213                 if (pe == string::npos)
01214                     return (false);
01215                 else
01216                     pe += 1;
01217                 pe = pRespStr->find(".", pe);
01218                 if (pe == string::npos)
01219                     return (false);
01220                 else
01221                     pe += 1;
01222                 pe = pRespStr->find(".", pe);
01223                 if (pe == string::npos)
01224                     return (false);
01225                 else
01226                     pe += 1;
01227                 pe = pRespStr->find(",", pe);
01228 
01229                 ss = pRespStr->substr(ps, pe - ps);
01230                 strncpy(s->gateway, ss.c_str(), MAX_LEN_IP_STR);
01231                 s->gateway[MAX_LEN_IP_STR - 1] = '\0';
01232                 ps = pe + 1;
01233 
01234                 pe = pRespStr->find(".", pe);
01235                 if (pe == string::npos)
01236                     return (false);
01237                 else
01238                     pe += 1;
01239                 pe = pRespStr->find(".", pe);
01240                 if (pe == string::npos)
01241                     return (false);
01242                 else
01243                     pe += 1;
01244                 pe = pRespStr->find(".", pe);
01245                 if (pe == string::npos)
01246                     return (false);
01247                 else
01248                     pe += 1;
01249                 pe = pRespStr->find(",", pe);
01250 
01251 
01252                 ss = pRespStr->substr(ps, pe - ps);
01253                 strncpy(s->dnsPrimary, ss.c_str(), MAX_LEN_IP_STR);
01254                 s->dnsPrimary[MAX_LEN_IP_STR - 1] = '\0';
01255                 ps = pe + 1;
01256 
01257                 pe = pRespStr->find(".", pe);
01258                 if (pe == string::npos)
01259                     return (false);
01260                 else
01261                     pe += 1;
01262                 pe = pRespStr->find(".", pe);
01263                 if (pe == string::npos)
01264                     return (false);
01265                 else
01266                     pe += 1;
01267                 pe = pRespStr->find(".", pe);
01268                 if (pe == string::npos)
01269                     return (false);
01270                 else
01271                     pe += 1;
01272                 pe = pRespStr->find(",", pe);
01273 
01274 
01275                 ss = pRespStr->substr(ps, pe - ps);
01276                 strncpy(s->dnsSecondary, ss.c_str(), MAX_LEN_IP_STR);
01277                 s->dnsSecondary[MAX_LEN_IP_STR - 1] = '\0';
01278                 
01279                 dbgPuts("~~~~~~~~~~ WNC IP Stats ~~~~~~~~~~~~");
01280                 dbgPuts("ip: ", false);      dbgPutsNoTime(s->ip);
01281                 dbgPuts("mask: ", false);    dbgPutsNoTime(s->mask);
01282                 dbgPuts("gateway: ", false); dbgPutsNoTime(s->gateway);
01283                 dbgPuts("dns pri: ", false); dbgPutsNoTime(s->dnsPrimary);
01284                 dbgPuts("dns sec: ", false); dbgPutsNoTime(s->dnsSecondary);
01285                 dbgPuts("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
01286             
01287                 return (true);
01288             }
01289         }
01290     }
01291 
01292     return (false);
01293 }
01294 
01295 bool WncController::deleteSMSTextFromMem(char msgIdx)
01296 {
01297     const char * err = "deleteSMSTextFromMem: Failed!";
01298     
01299     switch (msgIdx)
01300     {
01301         case '*':
01302             at_deleteSMSTextFromMem_wnc('1');
01303             at_deleteSMSTextFromMem_wnc('2');
01304             at_deleteSMSTextFromMem_wnc('3');
01305             return (true); // WNC may error if slot empty, just ignore!
01306 
01307         case '1':
01308         case '2':
01309         case '3':
01310             if (true == at_deleteSMSTextFromMem_wnc(msgIdx))
01311                 return (true);
01312             else {
01313                 dbgPuts(err);
01314                 return (false);
01315             }
01316 
01317         default:
01318             dbgPuts(err);
01319             return (false);
01320     }
01321 }
01322 
01323 bool WncController::sendSMSTextFromMem(char msgIdx)
01324 {
01325     const char * err = "deleteSMSTextFromMem: Failed!";
01326     
01327     switch (msgIdx)
01328     {
01329         case '*':
01330             at_sendSMStextMem_wnc('1');
01331             at_sendSMStextMem_wnc('2');
01332             at_sendSMStextMem_wnc('3');
01333             return (true); // WNC may error if slot is empty, just ignore!
01334 
01335         case '1':
01336         case '2':
01337         case '3':
01338             if (at_sendSMStextMem_wnc(msgIdx) == true)
01339                 return (true);
01340             else {
01341                 dbgPuts(err);
01342                 return (false);
01343             }
01344 
01345         default:
01346             dbgPuts(err);
01347             return (false);
01348     }
01349 }
01350 
01351 bool WncController::at_deleteSMSTextFromMem_wnc(char n)
01352 {
01353     string cmdStr, respStr;
01354     // Message is stored in WNC, now send it!
01355     cmdStr = "AT+CMGD=";
01356     cmdStr += n;
01357     cmdStr += "\r\n";
01358     dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false);
01359     AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr);
01360     dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01361     return (r == WNC_AT_CMD_OK);
01362 }
01363 
01364 bool WncController::at_sendSMStextMem_wnc(char n)
01365 {
01366     string cmdStr, respStr;
01367     // Message is stored in WNC, now send it!
01368     cmdStr = "AT+CMSS=";
01369     cmdStr += n;
01370     cmdStr += "\r\n";
01371     dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false);
01372     AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr);
01373     dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01374     return (r == WNC_AT_CMD_OK);
01375 }    
01376 
01377 bool WncController::at_sendSMStext_wnc(const char * const phoneNum, const char * const text)
01378 {
01379     string respStr;
01380     string * pRespStr;
01381     size_t l = strlen(text);
01382     
01383     if (l <= MAX_WNC_SMS_LENGTH)
01384     {
01385         // Check to see if the SMS service is available
01386         checkCellLink();
01387         if (m_sReadyForSMS == true) {
01388             at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
01389             string cmdStr("AT+CMGS=\"");
01390             cmdStr += phoneNum;
01391             cmdStr += "\"";
01392             dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
01393             cmdStr += "\x0d"; // x0d = <ENTER>
01394             // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet!
01395             // And we want a delay before sending the actual text part of the string!
01396             mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false);  //  False turns off auto-addition of CR+LF (the WNC wants nothing here)
01397             dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01398             if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) {
01399                 // Part 2 of the text, this is the actual text part:
01400                 cmdStr = text;
01401                 dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
01402                 cmdStr += "\x1A";  // <CTRL>-Z is what tells the WNC the message is complete to send!
01403                 AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr);
01404                 dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01405                 if (respStr.size() == 0)
01406                     return (false);
01407                 else
01408                     return (r == WNC_AT_CMD_OK);
01409             }
01410         }
01411     }
01412 
01413     return (false);
01414 }
01415 
01416 bool WncController::saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx)
01417 {
01418     if (at_saveSMStext_wnc(phoneNum, text, msgIdx) == true)
01419         return (true);
01420     else {
01421         dbgPuts("saveSMSTextToMem: failed!\r\n");
01422         return (false);
01423     }
01424 }
01425 
01426 bool WncController::at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx)
01427 {
01428     string respStr;
01429     size_t l = strlen(text);
01430     
01431     if (l <= MAX_WNC_SMS_LENGTH)
01432     {
01433         // Check to see if the SMS service is available
01434         checkCellLink();
01435         if (m_sReadyForSMS == true) {
01436             string cmdStr("AT+CMGW=\"");
01437             cmdStr += phoneNum;
01438             cmdStr += "\"";
01439             dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
01440             cmdStr += "\x0d"; // x0d = <ENTER>
01441             // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet!
01442             // And we want a delay before sending the actual text part of the string!
01443             mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false);  //  False turns off auto-addition of CR+LF (the WNC wants nothing here)
01444             dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01445             if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) {
01446                 // Part 2 of the text, this is the actual text part:
01447                 cmdStr = text;
01448                 dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
01449                 cmdStr += "\x1A";  // <CTRL>-Z is what tells the WNC the message is complete to save!
01450                 AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr);
01451                 dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
01452                 if (respStr.size() > 0) {
01453                     // respStr will have the SMS index
01454                     size_t pos1 = respStr.find("+CMGW: ");
01455                     size_t pos2 = respStr.rfind("OK");
01456                     if (pos1 != string::npos && pos2 != string::npos) {
01457                         *msgIdx = *string(respStr.substr(pos1+7, 1)).c_str();
01458                         return (true);
01459                     }
01460                     else {
01461                         *msgIdx = '!';
01462                     }
01463                 }
01464             }
01465         }
01466     }
01467         
01468     return (false);
01469 }
01470 
01471 bool WncController::at_readSMSlog_wnc(string ** log)
01472 {
01473     return (at_send_wnc_cmd("AT+CMGL", log, m_sCmdTimeoutMs) == WNC_AT_CMD_OK);
01474 }
01475 
01476 string * WncController::at_readCellNeighbor_wnc()
01477 {
01478     string * pRespStr;
01479     at_send_wnc_cmd("$QCRSRP?", &pRespStr, m_sCmdTimeoutMs);
01480     return pRespStr;
01481 }
01482 
01483 size_t WncController::at_readSMStext_wnc(const char n, const char ** log)
01484 {
01485     static string smsReadTxtStr;
01486     string * pRespStr;
01487     string cmdStr;
01488         
01489     smsReadTxtStr.erase();
01490     cmdStr = "AT+CMGR";
01491     cmdStr += '1';
01492     if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK)
01493         *log = pRespStr->c_str();
01494     else
01495         *log = "\0";
01496         
01497     return (pRespStr->size());
01498 }
01499 
01500 bool WncController::at_at_wnc(void)
01501 {
01502     string * pRespStr;
01503     return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat?
01504 }
01505 
01506 bool WncController::at_init_wnc(bool hardReset)
01507 {
01508   string * pRespStr;
01509   AtCmdErr_e cmdRes;
01510   
01511   if (hardReset == true)
01512       dbgPuts("Hard Soft Reset!");
01513   
01514   dbgPuts("Start AT init of WNC:");
01515   
01516   // Kick it twice to perhaps remove cued responses from an incomplete
01517   //  power cycle.
01518   at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
01519   at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
01520   
01521   // Dump the firmware revision on the debug log:
01522   at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs);
01523 
01524   // Quick commands below do not need to check cellular connectivity
01525   at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);  // Echo Off
01526   at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs);      // 2 - verbose error, 1 - numeric error, 0 - just ERROR
01527 
01528   // Setup 3 memory slots in the WNC SIM for SMS usage.
01529   at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
01530   at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs);
01531 
01532   cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);     // Heartbeat?
01533   
01534   // If the simple commands are not working, no chance of more complex.
01535   //  I have seen re-trying commands make it worse.
01536   if (cmdRes != WNC_AT_CMD_OK)
01537       return (false);
01538   
01539   cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs);
01540   if (cmdRes != WNC_AT_CMD_OK)
01541       return (false);
01542   
01543   cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs);
01544   if (cmdRes != WNC_AT_CMD_OK)
01545       return (false);
01546   
01547   dbgPuts("SUCCESS: AT init of WNC!");
01548   
01549   return (true);
01550 }
01551 
01552 int16_t WncController::at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec)
01553 {
01554     string * pRespStr;
01555     string cmd_str("AT@SOCKCREAT=");
01556     AtCmdErr_e res;
01557 
01558     if (tcp) cmd_str += "1";  // TCP
01559     else cmd_str += "2";      // else UDP
01560 
01561     cmd_str += ",0";
01562     res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01563     if (res == WNC_AT_CMD_OK && pRespStr->size() > 0)
01564     {
01565         size_t pos1 = pRespStr->find("T:");
01566         size_t pos2 = pRespStr->rfind("OK");
01567         if ((pos1 != string::npos) && (pos2 != string::npos)) {
01568             size_t numLen = pos2 - (pos1 + 2);
01569             string sockStr = pRespStr->substr(pos1 + 2, numLen);
01570             cmd_str = "AT@SOCKCONN=";
01571             cmd_str += sockStr;
01572             cmd_str += ",\"";
01573             cmd_str += ip;
01574             cmd_str += "\",";
01575             cmd_str += _to_string(port);
01576             cmd_str += ",";
01577             if (timeOutSec < 30)
01578                 timeOutSec = 30;
01579             else if (timeOutSec > 360)
01580                 timeOutSec = 360;
01581             cmd_str += _to_string(timeOutSec);
01582             res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000);
01583             if (m_sMoreDebugEnabled) {
01584                 at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs);
01585                 at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs);
01586             }
01587             return (strtol(sockStr.c_str(), NULL, 10));
01588         }
01589         else {
01590             dbgPuts("Invalid sockcreat response!");
01591             return (0);
01592         }
01593     }
01594     else
01595         return (0);
01596 }
01597 
01598 bool WncController::at_sockclose_wnc(uint16_t numSock)
01599 {
01600     string * pRespStr;
01601     string cmd_str("AT@SOCKCLOSE=");
01602 
01603     cmd_str += _to_string(numSock);
01604  
01605     // Don't check the cell status to close the socket
01606     AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01607     
01608     if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) {
01609         for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) {
01610             res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01611             if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK))
01612                 break;
01613         }
01614     }
01615     
01616     return (res == WNC_AT_CMD_OK); 
01617 }
01618 
01619 bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr)
01620 {
01621     string * pRespStr;
01622     string str(s);
01623     AtCmdErr_e r;
01624 
01625     ipStr->erase(); // Clear out string until resolved!
01626     str = "AT@DNSRESVDON=\"" + str;
01627     str += "\"";
01628     r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS);
01629     if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) {
01630         size_t pos_start = pRespStr->find(":\"") + 2;
01631         if (pos_start !=  string::npos) {
01632             size_t pos_end = pRespStr->find("\"", pos_start) - 1;
01633             if (pos_end != string::npos) {
01634                 if (pos_end > pos_start) {
01635                     // Make a copy for use later (the source string is re-used)
01636                     *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1);
01637                     return (true);
01638                 }
01639             }
01640         }
01641     }
01642 
01643     *ipStr = INVALID_IP_STR;
01644 
01645     return (false);
01646 }
01647 
01648 bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs)
01649 {
01650     // Now, give the modem x seconds to start responding by
01651     // sending simple 'AT' commands to modem once per second.
01652     if (timeoutSecs > 0) {
01653         do {
01654             timeoutSecs--;
01655             dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
01656             dbgPutsNoTime(" ", false);
01657             AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
01658             if (rc == WNC_AT_CMD_OK) {
01659                 dbgPutsNoTime("");  // CR LF
01660                 return true; //timer.read();
01661             }
01662             waitMs(500);
01663         }
01664         while (timeoutSecs > 0);    
01665         dbgPutsNoTime(""); // CR LF
01666     }
01667     
01668     return (false);
01669 }
01670 
01671 WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp)
01672 {
01673     AtCmdErr_e result;
01674 
01675     if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) {
01676         string * pRespStr;
01677         const char * num2str;
01678         string cmd_str;
01679 
01680         if (isTcp == true)
01681             cmd_str="AT@SOCKWRITE=";
01682         else
01683             cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND=";
01684 
01685         cmd_str += _to_string(numSock);
01686         cmd_str += ",";
01687         cmd_str += _to_string(n);
01688         cmd_str += ",\"";
01689         while(n > 0) {
01690             n--;
01691             num2str = _to_hex_string(*s++);
01692             // Always 2-digit ascii hex:
01693             if (num2str[1] == '\0')
01694                 cmd_str += '0';
01695             cmd_str += num2str;
01696         }
01697         cmd_str += "\"";
01698         result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01699     }
01700     else {
01701         dbgPuts("sockwrite Err, string len bad!");
01702         result = WNC_AT_CMD_ERR;
01703     }
01704     
01705     return (result);
01706 }
01707 
01708 WncController::AtCmdErr_e WncController::at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp)
01709 {
01710     AtCmdErr_e result = WNC_AT_CMD_OK;
01711 
01712     string * pRespStr;
01713     string cmd_str;
01714     size_t pos_start, pos_end;
01715     int i;
01716     
01717     pS->erase();  // Start with a fresh string
01718 
01719     if (isTcp == true)
01720         cmd_str="AT@SOCKREAD=";
01721     else
01722         cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
01723 
01724     cmd_str += _to_string(numSock);
01725     cmd_str += ",";
01726     cmd_str += _to_string(MAX_WNC_READ_BYTES);
01727             
01728     // Experimental: read should not need to check cell net status
01729     result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01730     if (result == WNC_AT_CMD_OK) {
01731         if (pRespStr->size() > 0) {
01732             pos_start = pRespStr->find("\"");
01733             pos_end   = pRespStr->rfind("\"");
01734             // Make sure search finds what it's looking for!
01735             if (pos_start != string::npos && pos_end != string::npos) {
01736                 pos_start++;
01737                 i = pos_end - pos_start;  // Num hex chars, 2 per byte
01738             }
01739             else
01740                 i = 0;
01741         }
01742         else
01743             i = 0;
01744             
01745         if ((i < 0) || ((i % 2) == 1))
01746             dbgPuts("Invalid READ string!");
01747         
01748         if (i > 2*MAX_WNC_READ_BYTES) {
01749             i = 2*MAX_WNC_READ_BYTES;
01750             dbgPuts("DANGER WNC read data does not match length!");
01751         }
01752             
01753         // If data, convert the hex string into byte values
01754         while (i > 0) {
01755             i -= 2;
01756             *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
01757             pos_start += 2;
01758         }
01759     }
01760 
01761     return (result);
01762 }
01763 
01764 WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp)
01765 {
01766     AtCmdErr_e result = WNC_AT_CMD_OK;
01767     *numRead = 0;
01768     
01769     if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) {
01770         string * pRespStr;
01771         string cmd_str;
01772         size_t pos_start, pos_end;
01773         int i;
01774 
01775         if (isTcp == true)
01776             cmd_str="AT@SOCKREAD=";
01777         else
01778             cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
01779 
01780         cmd_str += _to_string(numSock);
01781         cmd_str += ",";
01782         cmd_str += _to_string(n);
01783             
01784         // Experimental: read should not need to check cell net status
01785         result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01786         if (result == WNC_AT_CMD_OK) {
01787             if (pRespStr->size() > 0) {
01788                 pos_start = pRespStr->find("\"");
01789                 pos_end   = pRespStr->rfind("\"");
01790                 // Make sure search finds what it's looking for!
01791                 if (pos_start != string::npos && pos_end != string::npos) {
01792                     pos_start++;
01793                     i = pos_end - pos_start;  // Num hex chars, 2 per byte
01794                 }
01795                 else
01796                     i = 0;
01797             }
01798             else
01799                 i = 0;
01800                 
01801             if ((i < 0) || ((i % 2) == 1))
01802                 dbgPuts("Invalid READ string!");
01803                 
01804             if (i > 2*n) {
01805                 // Bound the ill formated WNC read string!
01806                 i = 2*n;
01807                 dbgPuts("TRUNCATING read data!");
01808             }
01809 
01810             // If data, convert the hex string into byte values
01811             i /= 2;
01812             *numRead = i;
01813             while (i > 0) {
01814                 i--;
01815                 *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
01816                 pos_start += 2;
01817             }
01818         }
01819     }
01820     else {
01821         dbgPuts("sockread Err, to many to read!");
01822         result = WNC_AT_CMD_ERR;
01823     }
01824 
01825     return (result);
01826 }
01827 
01828 bool WncController::at_reinitialize_mdm(void)
01829 {
01830      // Atempt to re-register
01831 //     string * pRespStr;
01832 //     dbgPuts("Force re-register!");
01833 //     at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs);
01834 //     waitMs(31000);
01835 //     at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs);
01836 //     waitMs(31000);
01837     
01838     // Initialize the modem
01839     dbgPuts("Modem RE-initializing with SOFT Reset...");
01840 
01841     string * pRespStr;
01842     at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs);
01843     waitMs(5000);
01844 
01845     // Now, give the modem time to start responding by
01846     // sending simple 'AT' commands to the modem once per second.
01847     int timeoutSecs = WNC_REINIT_MAX_TIME_MS;
01848     do {
01849         dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
01850         AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
01851         if (rc == WNC_AT_CMD_OK) {
01852             dbgPutsNoTime("");  // CR LF
01853             break;
01854         }
01855         waitMs(500);
01856         timeoutSecs--;
01857     }
01858     while (timeoutSecs > 0);    
01859     
01860     if (timeoutSecs <= 0)
01861         dbgPuts("\r\nModem RE-init FAILED!");
01862     else
01863         dbgPuts("\r\nModem RE-init complete!");
01864         
01865     return (timeoutSecs > 0);
01866 }
01867 
01868 WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf)
01869 {
01870     rsp->erase(); // Clean up from possible prior cmd response
01871 
01872     // Don't bother the WNC if user hasn't turned it on.
01873     if (m_sState == WNC_OFF)
01874         return (WNC_AT_CMD_WNC_NOT_ON);
01875         
01876     size_t n = strlen(cmd);
01877     
01878     // Wait per WNC advise
01879     waitMs(WNC_WAIT_FOR_AT_CMD_MS);
01880  
01881     if (cmd && n > 0) {
01882         sendCmd(cmd, crLf);
01883 //        sendCmd(cmd, n, 1000, crLf);  // 3rd arg is micro seconds between chars sent
01884     }
01885 
01886     startTimerA();
01887     while (getTimerTicksA_mS() < timeout_ms) {
01888         n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS());
01889 
01890         if (n == 0)
01891             continue;
01892         
01893         if (rsp->rfind("OK") != string::npos) {
01894             stopTimerA();
01895             return (WNC_AT_CMD_OK);
01896         }
01897         
01898         if (rsp->rfind("+CME ERROR") != string::npos) {
01899             stopTimerA();
01900             return (WNC_AT_CMD_ERRCME);
01901         }
01902         
01903         if (rsp->rfind("@EXTERR") != string::npos) {
01904             stopTimerA();
01905             return (WNC_AT_CMD_ERREXT);
01906         }
01907             
01908         if (rsp->rfind("ERROR") != string::npos) {
01909             stopTimerA();
01910             return (WNC_AT_CMD_ERR);
01911         }
01912     }
01913     stopTimerA();
01914     
01915     return (WNC_AT_CMD_TIMEOUT);
01916 }
01917 
01918 bool WncController::at_setapn_wnc(const char * const apnStr)
01919 {
01920     string * pRespStr;
01921     
01922     string cmd_str("AT%PDNSET=1,");
01923     cmd_str += apnStr;
01924     cmd_str += ",IP";
01925     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
01926         return (true);
01927     else
01928         return (false);
01929 }
01930 
01931 bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber)
01932 {
01933     string * pRespStr;
01934     AtCmdErr_e cmdRes;    
01935     cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs);       // Check RSSI,BER
01936     if (cmdRes != WNC_AT_CMD_OK)
01937         return (false);
01938     
01939     if (pRespStr->size() == 0) {
01940         dbgPuts("Strange RSSI result!");
01941         return (false);
01942     }
01943     else {
01944         size_t pos1 = pRespStr->find("SQ:");
01945         size_t pos2 = pRespStr->rfind(",");
01946         // Sanity check
01947         if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) {
01948             string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 );
01949             int rawRssi = atoi(subStr.c_str());
01950         
01951             // Convert WNC RSSI into dBm range:
01952             //  0 - -113 dBm
01953             //  1 - -111 dBm
01954             //  2..30 - -109 to -53 dBm
01955             //  31 - -51dBm or >
01956             //  99 - not known or not detectable
01957             if (rawRssi == 99)
01958                 *dBm = -199;
01959             else if (rawRssi == 0)
01960                 *dBm = -113;
01961             else if (rawRssi == 1)
01962                 *dBm = -111;
01963             else if (rawRssi == 31)
01964                 *dBm = -51;
01965             else if (rawRssi >= 2 && rawRssi <= 30)
01966                 *dBm = -113 + 2 * rawRssi;
01967             else {
01968                 dbgPuts("Invalid RSSI!");
01969                 return (false);
01970             }
01971             // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4
01972             //                99 - unknown or undetectable
01973             subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1));
01974             *ber = atoi(subStr.c_str());
01975         }
01976         else {
01977             dbgPuts("Strange RSSI result2!");
01978             return (false);
01979         }
01980     }
01981     
01982     return (true);
01983 }
01984 
01985 bool WncController::checkCellLink(void)
01986 {
01987     string * pRespStr;
01988     size_t pos;
01989     int regSts;
01990     int cmdRes1, cmdRes2;
01991 
01992     if (m_sState == WNC_OFF)
01993         return (false);
01994     
01995     m_sState = WNC_ON_NO_CELL_LINK;
01996 
01997     if (m_sMoreDebugEnabled)
01998         dbgPuts("<-------- Begin Cell Status ------------");
01999 
02000     cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs);       // Check RSSI,BER
02001 
02002     // If no response, don't bother with more commands
02003     if (cmdRes1 != WNC_AT_CMD_TIMEOUT)
02004         cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs);     // Check if SIM locked
02005     else {
02006         if (m_sMoreDebugEnabled)
02007             dbgPuts("------------ WNC No Response! --------->");
02008 
02009         return (false);
02010     }
02011     
02012     if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0))
02013     {
02014         if (m_sMoreDebugEnabled)
02015         {
02016             if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT))
02017                 dbgPuts("------------ WNC No Response! --------->");
02018             else
02019                 dbgPuts("------------ WNC Cmd Error! ----------->");
02020         }
02021         
02022         // If by a miracle it responds to the 2nd after the 1st, keep going
02023         if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0))
02024             return (false);      
02025     }
02026   
02027     // If SIM Card not ready don't bother with commands!
02028     if (pRespStr->find("CPIN: READY") == string::npos)
02029     {
02030         if (m_sMoreDebugEnabled)
02031             dbgPuts("------------ WNC SIM Problem! --------->");
02032 
02033         return (false);
02034     }
02035 
02036     // SIM card OK, now check for signal and cellular network registration
02037     cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs);      // Check if registered on network
02038     if (cmdRes1 != WNC_AT_CMD_OK || pRespStr->size() == 0)
02039     {
02040         if (m_sMoreDebugEnabled)
02041             dbgPuts("------------ WNC +CREG? Fail! --------->");
02042 
02043         return (false);
02044     }
02045     else
02046     {
02047         pos = pRespStr->find("CREG: ");
02048         if (pos != string::npos)
02049         {
02050             // The registration is the 2nd arg in the comma separated list
02051             *pRespStr = pRespStr->substr(pos+8, 1);
02052             regSts = atoi(pRespStr->c_str());
02053             switch (regSts) {
02054                 case 1:
02055                 case 5:
02056                 case 6:
02057                 case 7:
02058                     m_sReadyForSMS = true;
02059                     break;
02060                 default:
02061                     m_sReadyForSMS = false;
02062                     dbgPuts("SMS Service Down!");
02063             }
02064 
02065             // 1 - registered home, 5 - registered roaming
02066             if ((regSts != 1) && (regSts != 5))
02067             {
02068                 if (m_sMoreDebugEnabled)
02069                     dbgPuts("------ WNC Cell Link Down for Data! --->");
02070 
02071                 return (false);
02072             }
02073         }
02074 
02075         if (m_sMoreDebugEnabled)
02076             dbgPuts("------------ WNC Ready ---------------->");
02077     }
02078     
02079     // If we made it this far and the WNC did respond, keep the ON state
02080     if (m_sState != WNC_NO_RESPONSE)
02081         m_sState = WNC_ON;
02082     
02083     return (true);
02084 }
02085 
02086 int WncController::dbgPutsNoTime(const char * s, bool crlf)
02087 {
02088     if (m_sDebugEnabled == true) {
02089         int r = dbgWriteChars(s);
02090         if (crlf == true)
02091             return (dbgWriteChars("\r\n"));
02092         else
02093             return (r);
02094     }
02095     else
02096         return 0;
02097 };
02098 
02099 int WncController::dbgPuts(const char * s, bool crlf)
02100 {
02101     dbgPutsNoTime("[*] ", false);
02102     dbgPutsNoTime(_to_string(getLogTimerTicks()), false);
02103     dbgPutsNoTime(" ", false);
02104 
02105     int r = dbgPutsNoTime(s, false);
02106     
02107     if (crlf == true)
02108         return (dbgPutsNoTime("", true));
02109     else
02110         return (r);
02111 };
02112     
02113 void WncController::sendCmd(const char * cmd, bool crLf)
02114 {
02115     puts(cmd);
02116     if (crLf == true)
02117         puts("\r\n");
02118 }
02119 
02120 void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf)
02121 {
02122     while (n--) {
02123         putc(*cmd++);
02124         waitUs(wait_uS);
02125     };
02126     if (crLf == true) {
02127         putc('\r');
02128         waitUs(wait_uS);
02129         putc('\n');
02130         waitUs(wait_uS);
02131     }
02132 }
02133 
02134 }; // End namespace WncController_fk