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