Demo application for using the AT&T IoT Starter Kit Powered by AWS.

Dependencies:   SDFileSystem

Fork of ATT_AWS_IoT_demo by Anthony Phillips

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