Demo application for using the AT&T IoT Starter Kit Powered by AWS.
Dependencies: SDFileSystem
Fork of ATT_AWS_IoT_demo by
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
Generated on Tue Jul 12 2022 22:13:21 by 1.7.2