Tyler Davis / WncControllerModified

Fork of WncControllerLibrary by Fred Kellerman

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 size_t WncController::at_readSMStext_wnc(const char n, const char ** log)
01477 {
01478     static string smsReadTxtStr;
01479     string * pRespStr;
01480     string cmdStr;
01481         
01482     smsReadTxtStr.erase();
01483     cmdStr = "AT+CMGR";
01484     cmdStr += '1';
01485     if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK)
01486         *log = pRespStr->c_str();
01487     else
01488         *log = "\0";
01489         
01490     return (pRespStr->size());
01491 }
01492 
01493 bool WncController::at_at_wnc(void)
01494 {
01495     string * pRespStr;
01496     return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat?
01497 }
01498 
01499 bool WncController::at_init_wnc(bool hardReset)
01500 {
01501   string * pRespStr;
01502   AtCmdErr_e cmdRes;
01503   
01504   if (hardReset == true)
01505       dbgPuts("Hard Soft Reset!");
01506   
01507   dbgPuts("Start AT init of WNC:");
01508   
01509   // Kick it twice to perhaps remove cued responses from an incomplete
01510   //  power cycle.
01511   at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
01512   at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
01513   
01514   // Dump the firmware revision on the debug log:
01515   at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs);
01516 
01517   // Quick commands below do not need to check cellular connectivity
01518   at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);  // Echo Off
01519   at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs);      // 2 - verbose error, 1 - numeric error, 0 - just ERROR
01520 
01521   // Setup 3 memory slots in the WNC SIM for SMS usage.
01522   at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
01523   at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs);
01524 
01525   cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);     // Heartbeat?
01526   
01527   // If the simple commands are not working, no chance of more complex.
01528   //  I have seen re-trying commands make it worse.
01529   if (cmdRes != WNC_AT_CMD_OK)
01530       return (false);
01531   
01532   cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs);
01533   if (cmdRes != WNC_AT_CMD_OK)
01534       return (false);
01535   
01536   cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs);
01537   if (cmdRes != WNC_AT_CMD_OK)
01538       return (false);
01539   
01540   dbgPuts("SUCCESS: AT init of WNC!");
01541   
01542   return (true);
01543 }
01544 
01545 int16_t WncController::at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec)
01546 {
01547     string * pRespStr;
01548     string cmd_str("AT@SOCKCREAT=");
01549     AtCmdErr_e res;
01550 
01551     if (tcp) cmd_str += "1";  // TCP
01552     else cmd_str += "2";      // else UDP
01553 
01554     cmd_str += ",0";
01555     res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01556     if (res == WNC_AT_CMD_OK && pRespStr->size() > 0)
01557     {
01558         size_t pos1 = pRespStr->find("T:");
01559         size_t pos2 = pRespStr->rfind("OK");
01560         if ((pos1 != string::npos) && (pos2 != string::npos)) {
01561             size_t numLen = pos2 - (pos1 + 2);
01562             string sockStr = pRespStr->substr(pos1 + 2, numLen);
01563             cmd_str = "AT@SOCKCONN=";
01564             cmd_str += sockStr;
01565             cmd_str += ",\"";
01566             cmd_str += ip;
01567             cmd_str += "\",";
01568             cmd_str += _to_string(port);
01569             cmd_str += ",";
01570             if (timeOutSec < 30)
01571                 timeOutSec = 30;
01572             else if (timeOutSec > 360)
01573                 timeOutSec = 360;
01574             cmd_str += _to_string(timeOutSec);
01575             res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000);
01576             if (m_sMoreDebugEnabled) {
01577                 at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs);
01578                 at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs);
01579             }
01580             return (strtol(sockStr.c_str(), NULL, 10));
01581         }
01582         else {
01583             dbgPuts("Invalid sockcreat response!");
01584             return (0);
01585         }
01586     }
01587     else
01588         return (0);
01589 }
01590 
01591 bool WncController::at_sockclose_wnc(uint16_t numSock)
01592 {
01593     string * pRespStr;
01594     string cmd_str("AT@SOCKCLOSE=");
01595 
01596     cmd_str += _to_string(numSock);
01597  
01598     // Don't check the cell status to close the socket
01599     AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01600     
01601     if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) {
01602         for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) {
01603             res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01604             if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK))
01605                 break;
01606         }
01607     }
01608     
01609     return (res == WNC_AT_CMD_OK); 
01610 }
01611 
01612 bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr)
01613 {
01614     string * pRespStr;
01615     string str(s);
01616     AtCmdErr_e r;
01617 
01618     ipStr->erase(); // Clear out string until resolved!
01619     str = "AT@DNSRESVDON=\"" + str;
01620     str += "\"";
01621     r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS);
01622     if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) {
01623         size_t pos_start = pRespStr->find(":\"") + 2;
01624         if (pos_start !=  string::npos) {
01625             size_t pos_end = pRespStr->find("\"", pos_start) - 1;
01626             if (pos_end != string::npos) {
01627                 if (pos_end > pos_start) {
01628                     // Make a copy for use later (the source string is re-used)
01629                     *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1);
01630                     return (true);
01631                 }
01632             }
01633         }
01634     }
01635 
01636     *ipStr = INVALID_IP_STR;
01637 
01638     return (false);
01639 }
01640 
01641 bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs)
01642 {
01643     // Now, give the modem x seconds to start responding by
01644     // sending simple 'AT' commands to modem once per second.
01645     if (timeoutSecs > 0) {
01646         do {
01647             timeoutSecs--;
01648             dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
01649             dbgPutsNoTime(" ", false);
01650             AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
01651             if (rc == WNC_AT_CMD_OK) {
01652                 dbgPutsNoTime("");  // CR LF
01653                 return true; //timer.read();
01654             }
01655             waitMs(500);
01656         }
01657         while (timeoutSecs > 0);    
01658         dbgPutsNoTime(""); // CR LF
01659     }
01660     
01661     return (false);
01662 }
01663 
01664 WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp)
01665 {
01666     AtCmdErr_e result;
01667 
01668     if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) {
01669         string * pRespStr;
01670         const char * num2str;
01671         string cmd_str;
01672 
01673         if (isTcp == true)
01674             cmd_str="AT@SOCKWRITE=";
01675         else
01676             cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND=";
01677 
01678         cmd_str += _to_string(numSock);
01679         cmd_str += ",";
01680         cmd_str += _to_string(n);
01681         cmd_str += ",\"";
01682         while(n > 0) {
01683             n--;
01684             num2str = _to_hex_string(*s++);
01685             // Always 2-digit ascii hex:
01686             if (num2str[1] == '\0')
01687                 cmd_str += '0';
01688             cmd_str += num2str;
01689         }
01690         cmd_str += "\"";
01691         result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01692     }
01693     else {
01694         dbgPuts("sockwrite Err, string len bad!");
01695         result = WNC_AT_CMD_ERR;
01696     }
01697     
01698     return (result);
01699 }
01700 
01701 WncController::AtCmdErr_e WncController::at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp)
01702 {
01703     AtCmdErr_e result = WNC_AT_CMD_OK;
01704 
01705     string * pRespStr;
01706     string cmd_str;
01707     size_t pos_start, pos_end;
01708     int i;
01709     
01710     pS->erase();  // Start with a fresh string
01711 
01712     if (isTcp == true)
01713         cmd_str="AT@SOCKREAD=";
01714     else
01715         cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
01716 
01717     cmd_str += _to_string(numSock);
01718     cmd_str += ",";
01719     cmd_str += _to_string(MAX_WNC_READ_BYTES);
01720             
01721     // Experimental: read should not need to check cell net status
01722     result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01723     if (result == WNC_AT_CMD_OK) {
01724         if (pRespStr->size() > 0) {
01725             pos_start = pRespStr->find("\"");
01726             pos_end   = pRespStr->rfind("\"");
01727             // Make sure search finds what it's looking for!
01728             if (pos_start != string::npos && pos_end != string::npos) {
01729                 pos_start++;
01730                 i = pos_end - pos_start;  // Num hex chars, 2 per byte
01731             }
01732             else
01733                 i = 0;
01734         }
01735         else
01736             i = 0;
01737             
01738         if ((i < 0) || ((i % 2) == 1))
01739             dbgPuts("Invalid READ string!");
01740         
01741         if (i > 2*MAX_WNC_READ_BYTES) {
01742             i = 2*MAX_WNC_READ_BYTES;
01743             dbgPuts("DANGER WNC read data does not match length!");
01744         }
01745             
01746         // If data, convert the hex string into byte values
01747         while (i > 0) {
01748             i -= 2;
01749             *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
01750             pos_start += 2;
01751         }
01752     }
01753 
01754     return (result);
01755 }
01756 
01757 WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp)
01758 {
01759     AtCmdErr_e result = WNC_AT_CMD_OK;
01760     *numRead = 0;
01761     
01762     if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) {
01763         string * pRespStr;
01764         string cmd_str;
01765         size_t pos_start, pos_end;
01766         int i;
01767 
01768         if (isTcp == true)
01769             cmd_str="AT@SOCKREAD=";
01770         else
01771             cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
01772 
01773         cmd_str += _to_string(numSock);
01774         cmd_str += ",";
01775         cmd_str += _to_string(n);
01776             
01777         // Experimental: read should not need to check cell net status
01778         result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
01779         if (result == WNC_AT_CMD_OK) {
01780             if (pRespStr->size() > 0) {
01781                 pos_start = pRespStr->find("\"");
01782                 pos_end   = pRespStr->rfind("\"");
01783                 // Make sure search finds what it's looking for!
01784                 if (pos_start != string::npos && pos_end != string::npos) {
01785                     pos_start++;
01786                     i = pos_end - pos_start;  // Num hex chars, 2 per byte
01787                 }
01788                 else
01789                     i = 0;
01790             }
01791             else
01792                 i = 0;
01793                 
01794             if ((i < 0) || ((i % 2) == 1))
01795                 dbgPuts("Invalid READ string!");
01796                 
01797             if (i > 2*n) {
01798                 // Bound the ill formated WNC read string!
01799                 i = 2*n;
01800                 dbgPuts("TRUNCATING read data!");
01801             }
01802 
01803             // If data, convert the hex string into byte values
01804             i /= 2;
01805             *numRead = i;
01806             while (i > 0) {
01807                 i--;
01808                 *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
01809                 pos_start += 2;
01810             }
01811         }
01812     }
01813     else {
01814         dbgPuts("sockread Err, to many to read!");
01815         result = WNC_AT_CMD_ERR;
01816     }
01817 
01818     return (result);
01819 }
01820 
01821 bool WncController::at_reinitialize_mdm(void)
01822 {
01823      // Atempt to re-register
01824 //     string * pRespStr;
01825 //     dbgPuts("Force re-register!");
01826 //     at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs);
01827 //     waitMs(31000);
01828 //     at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs);
01829 //     waitMs(31000);
01830     
01831     // Initialize the modem
01832     dbgPuts("Modem RE-initializing with SOFT Reset...");
01833 
01834     string * pRespStr;
01835     at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs);
01836     waitMs(5000);
01837 
01838     // Now, give the modem time to start responding by
01839     // sending simple 'AT' commands to the modem once per second.
01840     int timeoutSecs = WNC_REINIT_MAX_TIME_MS;
01841     do {
01842         dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
01843         AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
01844         if (rc == WNC_AT_CMD_OK) {
01845             dbgPutsNoTime("");  // CR LF
01846             break;
01847         }
01848         waitMs(500);
01849         timeoutSecs--;
01850     }
01851     while (timeoutSecs > 0);    
01852     
01853     if (timeoutSecs <= 0)
01854         dbgPuts("\r\nModem RE-init FAILED!");
01855     else
01856         dbgPuts("\r\nModem RE-init complete!");
01857         
01858     return (timeoutSecs > 0);
01859 }
01860 
01861 WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf)
01862 {
01863     rsp->erase(); // Clean up from possible prior cmd response
01864 
01865     // Don't bother the WNC if user hasn't turned it on.
01866     if (m_sState == WNC_OFF)
01867         return (WNC_AT_CMD_WNC_NOT_ON);
01868         
01869     size_t n = strlen(cmd);
01870     
01871     // Wait per WNC advise
01872     waitMs(WNC_WAIT_FOR_AT_CMD_MS);
01873  
01874     if (cmd && n > 0) {
01875         sendCmd(cmd, crLf);
01876 //        sendCmd(cmd, n, 1000, crLf);  // 3rd arg is micro seconds between chars sent
01877     }
01878 
01879     startTimerA();
01880     while (getTimerTicksA_mS() < timeout_ms) {
01881         n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS());
01882 
01883         if (n == 0)
01884             continue;
01885         
01886         if (rsp->rfind("OK") != string::npos) {
01887             stopTimerA();
01888             return (WNC_AT_CMD_OK);
01889         }
01890         
01891         if (rsp->rfind("+CME ERROR") != string::npos) {
01892             stopTimerA();
01893             return (WNC_AT_CMD_ERRCME);
01894         }
01895         
01896         if (rsp->rfind("@EXTERR") != string::npos) {
01897             stopTimerA();
01898             return (WNC_AT_CMD_ERREXT);
01899         }
01900             
01901         if (rsp->rfind("ERROR") != string::npos) {
01902             stopTimerA();
01903             return (WNC_AT_CMD_ERR);
01904         }
01905     }
01906     stopTimerA();
01907     
01908     return (WNC_AT_CMD_TIMEOUT);
01909 }
01910 
01911 bool WncController::at_setapn_wnc(const char * const apnStr)
01912 {
01913     string * pRespStr;
01914     
01915     string cmd_str("AT%PDNSET=1,");
01916     cmd_str += apnStr;
01917     cmd_str += ",IP";
01918     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
01919         return (true);
01920     else
01921         return (false);
01922 }
01923 
01924 bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber)
01925 {
01926     string * pRespStr;
01927     AtCmdErr_e cmdRes;    
01928     cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs);       // Check RSSI,BER
01929     if (cmdRes != WNC_AT_CMD_OK)
01930         return (false);
01931     
01932     if (pRespStr->size() == 0) {
01933         dbgPuts("Strange RSSI result!");
01934         return (false);
01935     }
01936     else {
01937         size_t pos1 = pRespStr->find("SQ:");
01938         size_t pos2 = pRespStr->rfind(",");
01939         // Sanity check
01940         if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) {
01941             string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 );
01942             int rawRssi = atoi(subStr.c_str());
01943         
01944             // Convert WNC RSSI into dBm range:
01945             //  0 - -113 dBm
01946             //  1 - -111 dBm
01947             //  2..30 - -109 to -53 dBm
01948             //  31 - -51dBm or >
01949             //  99 - not known or not detectable
01950             if (rawRssi == 99)
01951                 *dBm = -199;
01952             else if (rawRssi == 0)
01953                 *dBm = -113;
01954             else if (rawRssi == 1)
01955                 *dBm = -111;
01956             else if (rawRssi == 31)
01957                 *dBm = -51;
01958             else if (rawRssi >= 2 && rawRssi <= 30)
01959                 *dBm = -113 + 2 * rawRssi;
01960             else {
01961                 dbgPuts("Invalid RSSI!");
01962                 return (false);
01963             }
01964             // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4
01965             //                99 - unknown or undetectable
01966             subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1));
01967             *ber = atoi(subStr.c_str());
01968         }
01969         else {
01970             dbgPuts("Strange RSSI result2!");
01971             return (false);
01972         }
01973     }
01974     
01975     return (true);
01976 }
01977 
01978 bool WncController::checkCellLink(void)
01979 {
01980     string * pRespStr;
01981     size_t pos;
01982     int regSts;
01983     int cmdRes1, cmdRes2;
01984 
01985     if (m_sState == WNC_OFF)
01986         return (false);
01987     
01988     m_sState = WNC_ON_NO_CELL_LINK;
01989 
01990     if (m_sMoreDebugEnabled)
01991         dbgPuts("<-------- Begin Cell Status ------------");
01992 
01993     cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs);       // Check RSSI,BER
01994 
01995     // If no response, don't bother with more commands
01996     if (cmdRes1 != WNC_AT_CMD_TIMEOUT)
01997         cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs);     // Check if SIM locked
01998     else {
01999         if (m_sMoreDebugEnabled)
02000             dbgPuts("------------ WNC No Response! --------->");
02001 
02002         return (false);
02003     }
02004     
02005     if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0))
02006     {
02007         if (m_sMoreDebugEnabled)
02008         {
02009             if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT))
02010                 dbgPuts("------------ WNC No Response! --------->");
02011             else
02012                 dbgPuts("------------ WNC Cmd Error! ----------->");
02013         }
02014         
02015         // If by a miracle it responds to the 2nd after the 1st, keep going
02016         if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0))
02017             return (false);      
02018     }
02019   
02020     // If SIM Card not ready don't bother with commands!
02021     if (pRespStr->find("CPIN: READY") == string::npos)
02022     {
02023         if (m_sMoreDebugEnabled)
02024             dbgPuts("------------ WNC SIM Problem! --------->");
02025 
02026         return (false);
02027     }
02028 
02029     // SIM card OK, now check for signal and cellular network registration
02030     cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs);      // Check if registered on network
02031     if (cmdRes1 != WNC_AT_CMD_OK || pRespStr->size() == 0)
02032     {
02033         if (m_sMoreDebugEnabled)
02034             dbgPuts("------------ WNC +CREG? Fail! --------->");
02035 
02036         return (false);
02037     }
02038     else
02039     {
02040         pos = pRespStr->find("CREG: ");
02041         if (pos != string::npos)
02042         {
02043             // The registration is the 2nd arg in the comma separated list
02044             *pRespStr = pRespStr->substr(pos+8, 1);
02045             regSts = atoi(pRespStr->c_str());
02046             switch (regSts) {
02047                 case 1:
02048                 case 5:
02049                 case 6:
02050                 case 7:
02051                     m_sReadyForSMS = true;
02052                     break;
02053                 default:
02054                     m_sReadyForSMS = false;
02055                     dbgPuts("SMS Service Down!");
02056             }
02057 
02058             // 1 - registered home, 5 - registered roaming
02059             if ((regSts != 1) && (regSts != 5))
02060             {
02061                 if (m_sMoreDebugEnabled)
02062                     dbgPuts("------ WNC Cell Link Down for Data! --->");
02063 
02064                 return (false);
02065             }
02066         }
02067 
02068         if (m_sMoreDebugEnabled)
02069             dbgPuts("------------ WNC Ready ---------------->");
02070     }
02071     dbgPuts("Testing");
02072     int cmdRes4 = at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs);
02073     int cmdRes3 = at_send_wnc_cmd("AT+CGEQOS?", &pRespStr, m_sCmdTimeoutMs);
02074 
02075 
02076     // If we made it this far and the WNC did respond, keep the ON state
02077     if (m_sState != WNC_NO_RESPONSE)
02078         m_sState = WNC_ON;
02079     
02080     return (true);
02081 }
02082 
02083 int WncController::dbgPutsNoTime(const char * s, bool crlf)
02084 {
02085     if (m_sDebugEnabled == true) {
02086         int r = dbgWriteChars(s);
02087         if (crlf == true)
02088             return (dbgWriteChars("\r\n"));
02089         else
02090             return (r);
02091     }
02092     else
02093         return 0;
02094 };
02095 
02096 int WncController::dbgPuts(const char * s, bool crlf)
02097 {
02098     dbgPutsNoTime("[*] ", false);
02099     dbgPutsNoTime(_to_string(getLogTimerTicks()), false);
02100     dbgPutsNoTime(" ", false);
02101 
02102     int r = dbgPutsNoTime(s, false);
02103     
02104     if (crlf == true)
02105         return (dbgPutsNoTime("", true));
02106     else
02107         return (r);
02108 };
02109     
02110 void WncController::sendCmd(const char * cmd, bool crLf)
02111 {
02112     puts(cmd);
02113     if (crLf == true)
02114         puts("\r\n");
02115 }
02116 
02117 void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf)
02118 {
02119     while (n--) {
02120         putc(*cmd++);
02121         waitUs(wait_uS);
02122     };
02123     if (crLf == true) {
02124         putc('\r');
02125         waitUs(wait_uS);
02126         putc('\n');
02127         waitUs(wait_uS);
02128     }
02129 }
02130 
02131 }; // End namespace WncController_fk
02132