Mbed Cloud example program for workshop in W27 2018.

Dependencies:   MMA7660 LM75B

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