Jim Flynn / Mbed OS wifi_Example

Dependencies:   X_NUCLEO_IKS01A2 mbed-http

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