This library controls the WNC. There is a derived class for usage from the K64F board.
Fork of WncControllerLibrary by
Diff: WncController.cpp
- Revision:
- 0:affdbb35faa4
- Child:
- 1:ac2de545b981
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WncController.cpp Wed Aug 31 02:06:26 2016 +0000
@@ -0,0 +1,1708 @@
+/*
+ Copyright (c) 2016 Fred Kellerman
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ @file WncController.cpp
+ @purpose Controls WNC 14A2A Cellular Modem
+ @version 1.0
+ @date July 2016
+ @author Fred Kellerman
+*/
+
+
+#include <cstdlib>
+#include <cctype>
+#include "WncController.h"
+
+namespace WncController_fk {
+
+/////////////////////////////////////////////////////
+// Static initializers
+/////////////////////////////////////////////////////
+WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS] = {
+ { false, "192.168.0.1", 80, 0, 25, true, 30 } // ,
+// { false, "192.168.0.1", 80, 0, 25, true, 30 }
+};
+WncController::WncState_e WncController::m_sState = WNC_OFF;
+uint16_t WncController::m_sCmdTimeoutMs = WNC_CMD_TIMEOUT_MS;
+string WncController::m_sApnStr = "NULL";
+string WncController::m_sWncStr;
+uint8_t WncController::m_sPowerUpTimeoutSecs = MAX_POWERUP_TIMEOUT;
+bool WncController::m_sDebugEnabled = false;
+bool WncController::m_sMoreDebugEnabled = false;
+bool WncController::m_sCheckNetStatus = false; // Turn on internet status check between every command
+const char * const WncController::INVALID_IP_STR = "";
+bool WncController::m_sReadyForSMS = false;
+
+
+/**
+ * C++ version 0.4 char* style "itoa":
+ * Written by Lukás Chmela
+ * Released under GPLv3.
+*/
+
+static char* itoa(int64_t value, char* result, int base)
+{
+ // check that the base is valid
+ if ( base < 2 || base > 36 ) {
+ *result = '\0';
+ return result;
+ }
+
+ char* ptr = result, *ptr1 = result, tmp_char;
+ int64_t tmp_value;
+
+ do {
+ tmp_value = value;
+ value /= base;
+ *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
+ } while ( value );
+
+ // Apply negative sign
+ if ( tmp_value < 0 )
+ *ptr++ = '-';
+
+ *ptr-- = '\0';
+
+ while ( ptr1 < ptr ) {
+ tmp_char = *ptr;
+ *ptr-- = *ptr1;
+ *ptr1++ = tmp_char;
+ }
+
+ return result;
+}
+
+const char * WncController::_to_string(int64_t value)
+{
+ static char str[21]; // room for signed 64-bit + null
+ itoa(value, str, 10);
+ return (str);
+}
+
+
+/**
+ * \brief Constructor for UART controlled WNC
+ *
+ * \param [in] wnc_uart - Reference to a SerialBuffered object which will
+ * be used as the bus to control the WNC.
+ *
+ * \return None.
+ *
+ * \details Adding another way to talk to the WNC, like I2C or USB,
+ * a constructor should be added for each type just like the SerialBuffered
+ * constructor below.
+ */
+WncController::WncController(const char * const apnStr)
+{
+ m_sApnStr = apnStr;
+}
+
+void WncController::enableDebug(bool on, bool moreDebugOn)
+{
+ m_sDebugEnabled = on;
+ m_sMoreDebugEnabled = moreDebugOn;
+}
+
+/**
+ * \brief Used internally but also make public for a user of the Class to interrogate state as well.
+ *
+ * \param [in] None.
+ *
+ * \return The state of the WNC Modem.
+ *
+ * \details None.
+ */
+WncController::WncState_e WncController::getWncStatus(void)
+{
+ return (m_sState);
+}
+
+/**
+ * \brief Return signal quality dBm level
+ *
+ * \param [in] None.
+ *
+ * \return The dBm signal level at the time of the request.
+ *
+ * \details This polls (at the time of the call) the cell signal.
+ */
+int16_t WncController::getDbmRssi(void)
+{
+ int16_t rssi, ber;
+ if (at_getrssiber_wnc(&rssi, &ber) == true)
+ return (rssi);
+ else
+ return (99);
+}
+
+int16_t WncController::get3gBer(void)
+{
+ int16_t rssi, ber;
+ if (at_getrssiber_wnc(&rssi, &ber) == true)
+ return (ber);
+ else
+ return (99);
+}
+
+
+/**
+ * \brief Power up and down (down not implemented yet)
+ *
+ * \param [in] on - set true to power on, otherwise false
+ *
+ * \return None.
+ *
+ * \details Power-on works but not power-down. This will manipulate WNC Shield hardware
+ * and bring it to life. It will also initialize the WNC enough to get it to be able to open sockets
+ * (with AT commands)
+ */
+bool WncController::powerWncOn(uint8_t powerUpTimeoutSecs)
+{
+ dbgPuts("Waiting for WNC to Initialize...");
+ m_sPowerUpTimeoutSecs = powerUpTimeoutSecs;
+ m_sState = WNC_ON_NO_CELL_LINK; // Turn soft on to allow "AT" for init to be sent!
+ if (initWncModem(powerUpTimeoutSecs) == true) {
+ // Set the Apn
+ setApnName(m_sApnStr.c_str());
+ if (false == softwareInitMdm()) {
+ dbgPuts("Software init failed!");
+ m_sState = WNC_OFF;
+ }
+ }
+ else {
+ dbgPuts("Power up failed!");
+ m_sState = WNC_OFF;
+ }
+
+ return (m_sState == WNC_ON_NO_CELL_LINK);
+}
+
+size_t WncController::sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout)
+{
+ string * respStr;
+
+ if (sizeRespBuf > 0) {
+ AtCmdErr_e r = at_send_wnc_cmd(cmd, &respStr, ms_timeout);
+ if (respStr->size() < sizeRespBuf) {
+ strcpy(resp, respStr->c_str());
+ return (respStr->size());
+ }
+ }
+
+ dbgPuts("sendCustomCmd: would have overrun!");
+
+ return (0);
+}
+
+bool WncController::pingUrl(const char * url)
+{
+ string ipAddr;
+
+ if (true == at_dnsresolve_wnc(url, &ipAddr))
+ return (pingIp(ipAddr.c_str()));
+ else
+ dbgPuts("pingUrl DNS resolve: failed!");
+
+ return (false);
+}
+
+bool WncController::pingIp(const char * ip)
+{
+ if (true == at_ping_wnc(ip))
+ return (true);
+ else
+ dbgPuts("pingIp: failed!");
+
+ return (false);
+}
+
+bool WncController::getWncNetworkingStats(WncIpStats * s)
+{
+ return (at_get_wnc_net_stats(s));
+}
+
+bool WncController::getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR])
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ strncpy(myIpAddr, m_sSock[numSock].myIpAddressStr.c_str(), MAX_LEN_IP_STR);
+ myIpAddr[MAX_LEN_IP_STR - 1] = '\0';
+ return (true);
+ }
+ else {
+ myIpAddr[0] = '\0';
+ return (false);
+ }
+}
+
+bool WncController::setApnName(const char * const apnStr)
+{
+ if (at_setapn_wnc(apnStr) == true)
+ {
+ m_sApnStr = apnStr;
+ return (true);
+ }
+ else
+ return (false);
+}
+
+
+/**
+ * \brief Look-up a URL text string and convert into an IP Address string.
+ *
+ * \param [in] url - the URL to lookup. numSock - the socket number to resolve.
+ *
+ * \return true - if the IP address has been resolved. false - if the URL could not be resolved.
+ *
+ * \details None.
+ */
+bool WncController::resolveUrl(uint16_t numSock, const char * url)
+{
+ bool cmdRes;
+
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ if (strlen(url) > 0) {
+ cmdRes = at_dnsresolve_wnc(url, &m_sSock[numSock].myIpAddressStr);
+ if (cmdRes == false)
+ dbgPuts("Cannot resolve URL!");
+ return (cmdRes);
+ }
+ else
+ dbgPuts("Invalid URL");
+ }
+ else
+ dbgPuts("Invalid Sock num!");
+
+ return (false);
+}
+
+/**
+ * \brief Set IP Address string
+ *
+ * \param [in] numSock - socket reference to set the string for. ipStr - text string of the IP
+ * address you want to talk to. There is no sanity check - beware!!!
+ *
+ * \return true - if the IP address has been set. false - if the IP could not be set.
+ *
+ * \details None.
+ */
+bool WncController::setIpAddr(uint16_t numSock, const char * ipStr)
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ m_sSock[numSock].myIpAddressStr = ipStr;
+ return (true);
+ }
+ else {
+ dbgPuts("Bad socket num!");
+ return (false);
+ }
+}
+
+/**
+ * \brief Opens a WNC socket.
+ *
+ * \param [in] sockNum - the number of the socket to open. ipAddr - a string containing
+ * the IP address. port - the IP port number to open the socket connection.
+ *
+ * \return true - if the socket is/was opened. false otherwise.
+ *
+ * \details None.
+ */
+bool WncController::openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec)
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ // IPV4 ip addr sanity check!
+ size_t lenIpStr = m_sSock[numSock].myIpAddressStr.size();
+ if (lenIpStr < 7 || lenIpStr > 15) {
+ dbgPuts("Invalid IP Address!");
+ return (false);
+ }
+
+ // Already open ? Must close if want to re-open with new settings.
+ if (m_sSock[numSock].open == true) {
+ dbgPuts("Socket already open, close then re-open!");
+ at_sockclose_wnc(numSock);
+ m_sSock[numSock].open = false;
+ }
+
+ m_sSock[numSock].myPort = port;
+ m_sSock[numSock].isTcp = tcp;
+ m_sSock[numSock].timeOutSec = timeOutSec;
+ m_sSock[numSock].open = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr, port, numSock, tcp, timeOutSec);
+ if (m_sSock[numSock].open == false) {
+ dbgPuts("Socket open fail!!!!");
+ // Work-around. If the sock open fails it needs to be told
+ // to close. If 6 sock opens happen with a fail, it further
+ // crashes the WNC. Not sure why the sock won't open.
+ at_sockclose_wnc(numSock);
+ }
+ }
+ else {
+ dbgPuts("Bad socket num or IP!");
+ return (false);
+ }
+
+ return (m_sSock[numSock].open);
+}
+
+/**
+ * \brief Write bytes of data to an open socket
+ *
+ * \param [in] sockNum - the number of the socket to write. s - a string containing
+ * the byte data to send must be less than = 1500.
+ *
+ * \return true - if the write was successful. false otherwise.
+ *
+ * \details The results of the write do not have anything to do with the data
+ * arriving at the endpoint.
+ */
+
+bool WncController::sockWrite(const char * s, uint32_t n, uint16_t numSock, bool isTcp)
+{
+ bool result = true;;
+
+ AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, numSock, isTcp);
+ if (cmdRes != WNC_AT_CMD_OK) {
+ if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_TIMEOUT) || (cmdRes == WNC_AT_CMD_ERRCME))
+ {
+ // This may throw away any data that hasn't been wrote out of the WNC
+ // but at this point with the way the WNC currently works we have
+ // no choice.
+ closeOpenSocket(numSock);
+ }
+ result = false;
+ }
+
+ return (result);
+}
+
+bool WncController::write(uint16_t numSock, const char * s, uint32_t n)
+{
+ bool result;
+
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ if (m_sSock[numSock].open == true) {
+ if (n <= MAX_WNC_WRITE_BYTES) {
+ result = sockWrite(s, n, numSock, m_sSock[numSock].isTcp);
+ }
+ else {
+ uint16_t rem = n % MAX_WNC_WRITE_BYTES;
+ while (n > MAX_WNC_WRITE_BYTES) {
+ n -= MAX_WNC_WRITE_BYTES;
+ result = sockWrite(s, MAX_WNC_WRITE_BYTES, numSock, m_sSock[numSock].isTcp);
+ if (result == false) {
+ n = 0;
+ rem = 0;
+ dbgPuts("Sock write fail!");
+ }
+ else
+ s += MAX_WNC_WRITE_BYTES;
+ }
+ if (rem > 0)
+ result = sockWrite(s, rem, numSock, m_sSock[numSock].isTcp);
+ }
+ }
+ else {
+ dbgPuts("Socket is closed for write!");
+ result = false;
+ }
+ }
+ else {
+ dbgPuts("Bad socket num!");
+ result = false;
+ }
+
+ return (result);
+}
+
+/**
+ * \brief Poll and read back data from the WNC (if it has any)
+ * If auto poll is enabled this read might fail (return with no data).
+ *
+ * \param [in] sockNum - the number of the socket to read. result - a string pointer containing
+ * the byte data readback from the WNC.
+ *
+ * \return The number of bytes/chars that are read from the socket.
+ *
+ * \details DO NOT use the same string as is passed to the auto poll setup method!
+ */
+size_t WncController::read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen)
+{
+ uint32_t numCopied = 0;
+
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+ if (m_sSock[numSock].open == true) {
+ uint8_t i = m_sSock[numSock].readRetries;
+ uint16_t to = m_sSock[numSock].readRetryWaitMs;
+ bool foundData = false;
+ size_t numRead;
+ do {
+ AtCmdErr_e cmdRes;
+ if (maxReadBufLen < MAX_WNC_READ_BYTES) {
+ cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, numSock, m_sSock[numSock].isTcp);
+ dbgPutsNoTime("Warning: read back buffer to small?");
+ }
+ else
+ cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, numSock, m_sSock[numSock].isTcp);
+
+ if (WNC_AT_CMD_OK == cmdRes) {
+ // This will let this loop read until the socket data is
+ // empty. If no data, then wait the retry amount of time.
+ if (numRead > 0) {
+ foundData = true;
+ i = 1;
+ if (numRead < maxReadBufLen) {
+ maxReadBufLen -= numRead;
+ numCopied += numRead;
+ readBuf += numRead;
+ }
+ else {
+ i = 0; // No more room for data!
+ dbgPutsNoTime("No more room for read data!");
+ }
+ }
+ else {
+ // Once data is found start returning it asap
+ if (foundData == false)
+ waitMs(to);
+ }
+ }
+ else {
+ dbgPuts("Sockread failed!");
+ if (cmdRes == WNC_AT_CMD_ERREXT || cmdRes == WNC_AT_CMD_TIMEOUT)
+ {
+ // This may throw away any data that hasn't been read out of the WNC
+ // but at this point with the way the WNC currently works we have
+ // no choice.
+ closeOpenSocket(numSock);
+ i = 0;
+ }
+ else
+ waitMs(to);
+ }
+ } while (i-- > 0);
+ }
+ else {
+ dbgPuts("Socket is closed for read");
+ }
+ }
+ else {
+ dbgPuts("Bad socket num!");
+ }
+
+ return (numCopied);
+}
+
+/**
+ * \brief Set how many times the above read method will retry if data is not returned.
+ *
+ * \param [in] sockNum - the number of the socket to set. retries - how many times to
+ * poll until data is found.
+ *
+ * \return None.
+ *
+ * \details None.
+ */
+void WncController::setReadRetries(uint16_t numSock, uint16_t retries)
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS)
+ m_sSock[numSock].readRetries = retries;
+ else
+ dbgPuts("Bad socket num!");
+}
+
+/**
+ * \brief Set how long between retries to wait.
+ *
+ * \param [in] sockNum - the number of the socket to set. readRetryWaitMs - how long to wait
+ * before doing the read poll (calling read(...)).
+ *
+ * \return None.
+ *
+ * \details None.
+ */
+void WncController::setReadRetryWait(uint16_t numSock, uint16_t readRetryWaitMs)
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS)
+ m_sSock[numSock].readRetryWaitMs = readRetryWaitMs;
+ else
+ dbgPuts("Bad socket num!");
+}
+
+/**
+ * \brief Close the socket.
+ *
+ * \param [in] sockNum - the number of the socket to close.
+ *
+ * \return None.
+ *
+ * \details None.
+ */
+bool WncController::closeSocket(uint16_t numSock)
+{
+ if (numSock < MAX_NUM_WNC_SOCKETS) {
+
+ if (false == at_sockclose_wnc(numSock))
+ dbgPuts("Sock close may not have closed!");
+
+ // Even with an error the socket could have closed,
+ // can't tell for sure so just soft close it for now.
+ m_sSock[numSock].open = false;
+ }
+ else {
+ dbgPuts("Bad socket num!");
+ }
+
+ return (m_sSock[numSock].open == false);
+}
+
+// Note: If you want to make it more portable, create a
+// arecharsavailable() and readchar()
+size_t WncController::mdmGetline(string & buff, int timeout_ms)
+{
+ char chin = '\0';
+ char chin_last;
+ size_t len = 0;
+
+ startTimerB();
+ while ((len < (MAX_LEN_WNC_CMD_RESPONSE - 1)) && (getUsTimerTicksB() < timeout_ms)) {
+ if (byteReady()) {
+ chin_last = chin;
+ chin = (char)getc();
+ if (isprint(chin)) {
+ buff += (char)chin;
+ len++; // Bound the copy length to something reaonable just in case
+ continue;
+ }
+ else if ((('\r' == chin_last) && ('\n' == chin)) || (('\n' == chin_last) && ('\r' == chin))) {
+ break;
+ }
+ }
+ rx_char_wait();
+ }
+ stopTimerB();
+
+ if (len >= (MAX_LEN_WNC_CMD_RESPONSE - 1))
+ dbgPuts("Max cmd length reply exceeded!");
+
+ return (len);
+}
+
+// Eventually this should try to reinstate the sockets open
+bool WncController::softwareInitMdm(void)
+{
+ static bool reportStatus = true;
+ unsigned i;
+
+ if (checkCellLink() == true) {
+ if (reportStatus == false) {
+ dbgPuts("Re-connected to cellular network!");
+ reportStatus = true;
+ }
+
+ // WNC has SIM and registered on network so
+ // soft initialize the WNC.
+ for (i = 0; i < WNC_SOFT_INIT_RETRY_COUNT; i++)
+ if (at_init_wnc() == true)
+ break;
+
+ // If it did not respond try a hardware init
+ if (i == WNC_SOFT_INIT_RETRY_COUNT)
+ {
+ at_reinitialize_mdm();
+ return (at_init_wnc(true)); // Hard reset occurred so make it go through the software init();
+ }
+ else
+ return (true);
+ }
+ else
+ {
+ if (reportStatus == true) {
+ dbgPuts("Not connected to cellular network!");
+ reportStatus = false;
+ }
+ return (false);
+ }
+}
+
+
+// Sets a global with failure or success, assumes 1 thread all the time
+WncController::AtCmdErr_e WncController::sendWncCmd(const char * const s, string ** r, int ms_timeout)
+{
+ if (checkCellLink() == false) {
+ static string noRespStr;
+
+ // Save some run-time!
+ if (m_sDebugEnabled)
+ {
+ dbgPuts("FAIL send cmd: ", false);
+ if (m_sMoreDebugEnabled && m_sDebugEnabled) {
+ dbgPutsNoTime(s);
+ }
+ else {
+ size_t n = strlen(s);
+ if (n <= WNC_TRUNC_DEBUG_LENGTH) {
+ dbgPutsNoTime(s);
+ }
+ else {
+ string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2);
+ truncStr += "..";
+ truncStr += &s[n-(WNC_TRUNC_DEBUG_LENGTH/2)];
+ dbgPutsNoTime(truncStr.c_str());
+ }
+ }
+ }
+
+ noRespStr.erase();
+ *r = &noRespStr;
+
+ return (WNC_AT_CMD_NO_CELL_LINK);
+ }
+
+ if (m_sCheckNetStatus)
+ {
+ if (m_sMoreDebugEnabled)
+ dbgPuts("[---------- Network Status -------------");
+ string * pRespStr;
+ at_send_wnc_cmd("AT@SOCKDIAL?", &pRespStr, m_sCmdTimeoutMs);
+ if (m_sMoreDebugEnabled)
+ dbgPuts("---------------------------------------]");
+ }
+
+ // If WNC ready, send user command
+ return (at_send_wnc_cmd(s, r, ms_timeout));
+}
+
+WncController::AtCmdErr_e WncController::at_send_wnc_cmd(const char * s, string ** r, int ms_timeout)
+{
+ // Save some run-time!
+ if (m_sDebugEnabled)
+ {
+ if (m_sMoreDebugEnabled) {
+ dbgPuts("TX: ", false); dbgPutsNoTime(s);
+ }
+ else {
+ if (m_sDebugEnabled) { // Save some run-time!
+ size_t n = strlen(s);
+ if (n <= WNC_TRUNC_DEBUG_LENGTH) {
+ dbgPuts("TX: ", false); dbgPutsNoTime(s);
+ }
+ else {
+ string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2);
+ truncStr += "..";
+ truncStr += &s[n - (WNC_TRUNC_DEBUG_LENGTH/2)];
+ dbgPuts("TX: ", false); dbgPutsNoTime(truncStr.c_str());
+ }
+ }
+ }
+ }
+
+ AtCmdErr_e atResult = mdmSendAtCmdRsp(s, ms_timeout, &m_sWncStr);
+ *r = &m_sWncStr; // Return a pointer to the static string
+
+ if (atResult != WNC_AT_CMD_TIMEOUT) {
+ // Save some run-time!
+ if (m_sDebugEnabled)
+ {
+ dbgPuts("RX: ", false);
+ if (m_sMoreDebugEnabled) {
+ dbgPutsNoTime(m_sWncStr.c_str());
+ }
+ else {
+ if (m_sWncStr.size() <= WNC_TRUNC_DEBUG_LENGTH) {
+ dbgPutsNoTime(m_sWncStr.c_str());
+ }
+ else {
+ string truncStr = m_sWncStr.substr(0,WNC_TRUNC_DEBUG_LENGTH/2) + "..";
+ truncStr += m_sWncStr.substr(m_sWncStr.size() - (WNC_TRUNC_DEBUG_LENGTH/2), WNC_TRUNC_DEBUG_LENGTH/2);
+ dbgPutsNoTime(truncStr.c_str());
+ }
+ }
+ }
+ }
+ else {
+ dbgPuts("AT Cmd TIMEOUT!");
+ dbgPuts("RX: ", false); dbgPutsNoTime(m_sWncStr.c_str());
+ }
+
+ return (atResult);
+}
+
+void WncController::closeOpenSocket(uint16_t numSock)
+{
+ // Try to open and close the socket
+ do {
+ dbgPuts("Try to close and re-open socket");
+ at_sockclose_wnc(numSock);
+ m_sSock[numSock].open = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr, m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec);
+ if (m_sSock[numSock].open == false)
+ dbgPuts("Failed to re-open socket!");
+ } while (m_sSock[numSock].open == false);
+}
+
+bool WncController::sendSMSText(const char * const phoneNum, const char * const text)
+{
+ if (at_sendSMStext_wnc(phoneNum, text) == true)
+ return (true);
+ else {
+ dbgPuts("sendSMSText: Failed!");
+ return (false);
+ }
+}
+
+size_t WncController::readSMSLog(const char ** log)
+{
+ size_t n;
+
+ n = at_readSMSlog_wnc(log);
+ if (n == 0)
+ dbgPuts("readSMSLog: Failed!");
+
+ return (n);
+}
+
+size_t WncController::getSignalQuality(const char ** log)
+{
+ size_t n;
+
+ n = at_getSignalQuality_wnc(log);
+ if (n == 0)
+ dbgPuts("readSMSText: Failed!");
+
+ return (n);
+}
+
+size_t WncController::at_getSignalQuality_wnc(const char ** log)
+{
+ string * pRespStr;
+ static string logStr;
+
+ logStr.erase();
+
+ if (at_send_wnc_cmd("AT%MEAS=\"0\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr = *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=0: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"1\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=1: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"2\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=2: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"3\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=3: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"4\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=4: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"5\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=5: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=8: failed!");
+
+ if (at_send_wnc_cmd("AT%MEAS=\"98\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ logStr += *pRespStr;
+ logStr += "\r\n";
+ }
+ else
+ dbgPuts("AT%MEAS=98: failed!");
+
+ *log = logStr.c_str();
+
+ return (logStr.size());
+}
+
+bool WncController::getTimeDate(struct WncDateTime * tod)
+{
+ if (at_gettimedate_wnc(tod) == true)
+ return (true);
+ else {
+ dbgPuts("Get time date failed!");
+ return (false);
+ }
+}
+
+bool WncController::at_ping_wnc(const char * ip)
+{
+ string * pRespStr;
+ string cmdStr = "AT@PINGREQ=\"";
+ cmdStr += ip;
+ cmdStr += "\"";
+ return (at_send_wnc_cmd(cmdStr.c_str(), &pRespStr, WNC_PING_CMD_TIMEOUT_MS) == WNC_AT_CMD_OK);
+}
+
+bool WncController::at_gettimedate_wnc(struct WncDateTime * tod)
+{
+ string * pRespStr;
+ char * pEnd;
+
+ if (at_send_wnc_cmd("AT+CCLK?", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) {
+ if (pRespStr->size() > 0) {
+ size_t pos1 = pRespStr->find("+CCLK:");
+ if (pos1 != string::npos) {
+ pEnd = (char *)pRespStr->c_str() + pos1 + 8;
+ tod->year = strtol(pEnd, &pEnd, 10);
+ tod->month = strtol(pEnd+1, &pEnd, 10);
+ tod->day = strtol(pEnd+1, &pEnd, 10);
+ tod->hour = strtol(pEnd+1, &pEnd, 10);
+ tod->min = strtol(pEnd+1, &pEnd, 10);
+ tod->sec = strtol(pEnd+1, &pEnd, 10);
+ return (true);
+ }
+ }
+ }
+
+ return (false);
+}
+
+size_t WncController::readSMSText(const char ** log)
+{
+ size_t n;
+
+ n = at_readSMStext_wnc(log);
+ if (n == 0)
+ dbgPuts("readSMSText: Failed!");
+
+ return (n);
+}
+
+bool WncController::at_get_wnc_net_stats(WncIpStats * s)
+{
+ string * pRespStr;
+ AtCmdErr_e cmdRes = at_send_wnc_cmd("AT+CGCONTRDP=1", &pRespStr, m_sCmdTimeoutMs);
+
+ if (WNC_AT_CMD_OK == cmdRes) {
+ if (pRespStr->size() > 0) {
+ memset((void*)s, '\0', sizeof(*s)); // Clean-up
+ string ss;
+ unsigned i;
+ size_t pe;
+ size_t ps = pRespStr->rfind("\"");
+ if (ps != string::npos) {
+ ps += 2; // Skip the , after the "
+ pe = ps;
+ for(i=0;i<4;i++)
+ pe = pRespStr->find(".", pe) + 1;
+ ss = pRespStr->substr(ps, pe - 1 - ps);
+ strncpy(s->ip, ss.c_str(), MAX_LEN_IP_STR);
+ s->ip[MAX_LEN_IP_STR - 1] = '\0';
+ ps = pe;
+ for(i=0;i<3;i++)
+ pe = pRespStr->find(".", pe) + 1;
+ pe = pRespStr->find(",", pe);
+ ss = pRespStr->substr(ps, pe - ps);
+ strncpy(s->mask, ss.c_str(), MAX_LEN_IP_STR);
+ s->mask[MAX_LEN_IP_STR - 1] = '\0';
+ ps = pe + 1;
+ for(i=0;i<3;i++)
+ pe = pRespStr->find(".", pe) + 1;
+ pe = pRespStr->find(",", pe);
+ ss = pRespStr->substr(ps, pe - ps);
+ strncpy(s->gateway, ss.c_str(), MAX_LEN_IP_STR);
+ s->gateway[MAX_LEN_IP_STR - 1] = '\0';
+ ps = pe + 1;
+ for(i=0;i<3;i++)
+ pe = pRespStr->find(".", pe) + 1;
+ pe = pRespStr->find(",", pe);
+ ss = pRespStr->substr(ps, pe - ps);
+ strncpy(s->dnsPrimary, ss.c_str(), MAX_LEN_IP_STR);
+ s->dnsPrimary[MAX_LEN_IP_STR - 1] = '\0';
+ ps = pe + 1;
+ for(i=0;i<3;i++)
+ pe = pRespStr->find(".", pe) + 1;
+ pe = pRespStr->find(",", pe);
+ ss = pRespStr->substr(ps, pe - ps);
+ strncpy(s->dnsSecondary, ss.c_str(), MAX_LEN_IP_STR);
+ s->dnsSecondary[MAX_LEN_IP_STR - 1] = '\0';
+
+ dbgPuts("~~~~~~~~~~ WNC IP Stats ~~~~~~~~~~~~");
+ dbgPuts("ip: ", false); dbgPutsNoTime(s->ip);
+ dbgPuts("mask: ", false); dbgPutsNoTime(s->mask);
+ dbgPuts("gateway: ", false); dbgPutsNoTime(s->gateway);
+ dbgPuts("dns pri: ", false); dbgPutsNoTime(s->dnsPrimary);
+ dbgPuts("dns sec: ", false); dbgPutsNoTime(s->dnsSecondary);
+ dbgPuts("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+
+ return (true);
+ }
+ }
+ }
+
+ return (false);
+}
+
+bool WncController::deleteSMSTextFromMem(char msgIdx)
+{
+ const char * err = "deleteSMSTextFromMem: Failed!";
+
+ switch (msgIdx)
+ {
+ case '*':
+ at_deleteSMSTextFromMem_wnc('1');
+ at_deleteSMSTextFromMem_wnc('2');
+ at_deleteSMSTextFromMem_wnc('3');
+ return (true); // WNC may error if slot empty, just ignore!
+
+ case '1':
+ case '2':
+ case '3':
+ if (true == at_deleteSMSTextFromMem_wnc(msgIdx))
+ return (true);
+ else {
+ dbgPuts(err);
+ return (false);
+ }
+
+ default:
+ dbgPuts(err);
+ return (false);
+ }
+}
+
+bool WncController::sendSMSTextFromMem(char msgIdx)
+{
+ const char * err = "deleteSMSTextFromMem: Failed!";
+
+ switch (msgIdx)
+ {
+ case '*':
+ at_sendSMStextMem_wnc('1');
+ at_sendSMStextMem_wnc('2');
+ at_sendSMStextMem_wnc('3');
+ return (true); // WNC may error if slot is empty, just ignore!
+
+ case '1':
+ case '2':
+ case '3':
+ if (at_sendSMStextMem_wnc(msgIdx) == true)
+ return (true);
+ else {
+ dbgPuts(err);
+ return (false);
+ }
+
+ default:
+ dbgPuts(err);
+ return (false);
+ }
+}
+
+bool WncController::at_deleteSMSTextFromMem_wnc(char n)
+{
+ string cmdStr, respStr;
+ // Message is stored in WNC, now send it!
+ cmdStr = "AT+CMGD=";
+ cmdStr += n;
+ cmdStr += "\r\n";
+ dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false);
+ AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr);
+ dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
+ return (r == WNC_AT_CMD_OK);
+}
+
+bool WncController::at_sendSMStextMem_wnc(char n)
+{
+ string cmdStr, respStr;
+ // Message is stored in WNC, now send it!
+ cmdStr = "AT+CMSS=";
+ cmdStr += n;
+ cmdStr += "\r\n";
+ dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false);
+ AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr);
+ dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
+ return (r == WNC_AT_CMD_OK);
+}
+
+bool WncController::at_sendSMStext_wnc(const char * const phoneNum, const char * const text)
+{
+ string respStr;
+ string * pRespStr;
+ size_t l = strlen(text);
+
+ if (l <= MAX_WNC_SMS_LENGTH)
+ {
+ // Check to see if the SMS service is available
+ checkCellLink();
+ if (m_sReadyForSMS == true) {
+ at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
+ string cmdStr("AT+CMGS=\"");
+ cmdStr += phoneNum;
+ cmdStr += "\"";
+ dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
+ cmdStr += "\x0d"; // x0d = <ENTER>
+ // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet!
+ // And we want a delay before sending the actual text part of the string!
+ mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here)
+ dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
+ if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) {
+ // Part 2 of the text, this is the actual text part:
+ cmdStr = text;
+ dbgPuts("TX: ", false); dbgPuts(cmdStr.c_str());
+ cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to send!
+ AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr);
+ dbgPuts("RX: ", false); dbgPuts(respStr.c_str());
+ if (respStr.size() == 0)
+ return (false);
+ else
+ return (r == WNC_AT_CMD_OK);
+ }
+ }
+ }
+
+ return (false);
+}
+
+bool WncController::saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx)
+{
+ if (at_saveSMStext_wnc(phoneNum, text, msgIdx) == true)
+ return (true);
+ else {
+ dbgPuts("saveSMSTextToMem: failed!\r\n");
+ return (false);
+ }
+}
+
+bool WncController::at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx)
+{
+ string respStr;
+ string * pRespStr;
+ size_t l = strlen(text);
+
+ if (l <= MAX_WNC_SMS_LENGTH)
+ {
+ // Check to see if the SMS service is available
+ checkCellLink();
+ if (m_sReadyForSMS == true) {
+ at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
+ at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs);
+ string cmdStr("AT+CMGW=\"");
+ cmdStr += phoneNum;
+ cmdStr += "\"";
+ dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
+ cmdStr += "\x0d"; // x0d = <ENTER>
+ // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet!
+ // And we want a delay before sending the actual text part of the string!
+ mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here)
+ dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
+ if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) {
+ // Part 2 of the text, this is the actual text part:
+ cmdStr = text;
+ dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str());
+ cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to save!
+ AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr);
+ dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str());
+ if (respStr.size() > 0) {
+ // respStr will have the SMS index
+ size_t pos1 = respStr.find("+CMGW: ");
+ size_t pos2 = respStr.rfind("OK");
+ if (pos1 != string::npos && pos2 != string::npos) {
+ *msgIdx = *string(respStr.substr(pos1+7, 1)).c_str();
+ return (true);
+ }
+ else {
+ *msgIdx = '!';
+ }
+ }
+ }
+ }
+ }
+
+ return (false);
+}
+
+
+size_t WncController::at_readSMSlog_wnc(const char ** log)
+{
+ string * pRespStr;
+
+ if (at_send_wnc_cmd("AT+CMGL", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK)
+ *log = pRespStr->c_str();
+ else
+ *log = "\0";
+
+ return (pRespStr->size());
+}
+
+size_t WncController::at_readSMStext_wnc(const char ** log)
+{
+ string * pRespStr;
+
+ if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK)
+ *log = pRespStr->c_str();
+ else
+ *log = "\0";
+
+ return (pRespStr->size());
+}
+
+bool WncController::at_at_wnc(void)
+{
+ string * pRespStr;
+ return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat?
+}
+
+bool WncController::at_init_wnc(bool hardReset)
+{
+ string * pRespStr;
+ AtCmdErr_e cmdRes;
+
+ if (hardReset == true)
+ dbgPuts("Hard Soft Reset!");
+
+ dbgPuts("Start AT init of WNC:");
+
+ // Kick it twice to perhaps remove cued responses from an incomplete
+ // power cycle.
+ at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
+ at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS);
+
+ // Quick commands below do not need to check cellular connectivity
+ at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Echo Off
+ at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs); // 2 - verbose error, 1 - numeric error, 0 - just ERROR
+ cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Heartbeat?
+
+ // If the simple commands are not working, no chance of more complex.
+ // I have seen re-trying commands make it worse.
+ if (cmdRes != WNC_AT_CMD_OK)
+ return (false);
+
+ cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs);
+ if (cmdRes != WNC_AT_CMD_OK)
+ return (false);
+
+ cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs);
+ if (cmdRes != WNC_AT_CMD_OK)
+ return (false);
+
+ dbgPuts("SUCCESS: AT init of WNC!");
+
+ return (true);
+}
+
+
+bool WncController::at_sockopen_wnc(const string & ipStr, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec)
+{
+ string * pRespStr;
+ string cmd_str("AT@SOCKCREAT=");
+ AtCmdErr_e res;
+
+ if (tcp) cmd_str += "1"; // TCP
+ else cmd_str += "2"; // else UDP
+
+ cmd_str += ",0";
+ res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
+ if (res == WNC_AT_CMD_OK)
+ {
+ cmd_str = "AT@SOCKCONN=";
+ cmd_str += _to_string(numSock + 1);
+ cmd_str += ",\"";
+ cmd_str += ipStr;
+ cmd_str += "\",";
+ cmd_str += _to_string(port);
+ cmd_str += ",";
+ if (timeOutSec < 30)
+ timeOutSec = 30;
+ else if (timeOutSec > 360)
+ timeOutSec = 360;
+ cmd_str += _to_string(timeOutSec);
+ res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000);
+ if (m_sMoreDebugEnabled) {
+ at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs);
+ at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs);
+ }
+ return (res == WNC_AT_CMD_OK);
+ }
+ else
+ return (false);
+}
+
+bool WncController::at_sockclose_wnc(uint16_t numSock)
+{
+ string * pRespStr;
+ string cmd_str("AT@SOCKCLOSE=");
+
+ cmd_str += _to_string(numSock + 1);
+ // Don't check the cell status to close the socket
+ return (WNC_AT_CMD_OK == at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs));
+}
+
+bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr)
+{
+ string * pRespStr;
+ string str(s);
+
+ ipStr->erase(); // Clear out string until resolved!
+ str = "AT@DNSRESVDON=\"" + str;
+ str += "\"";
+ if (sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS) == WNC_AT_CMD_OK) {
+ size_t pos_start = pRespStr->find(":\"") + 2;
+ if (pos_start != string::npos) {
+ size_t pos_end = pRespStr->find("\"", pos_start) - 1;
+ if (pos_end != string::npos) {
+ if (pos_end > pos_start) {
+ // Make a copy for use later (the source string is re-used)
+ *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1);
+ return (true);
+ }
+ }
+ }
+ }
+
+ *ipStr = INVALID_IP_STR;
+
+ return (false);
+}
+
+bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs)
+{
+ // Now, give the modem x seconds to start responding by
+ // sending simple 'AT' commands to modem once per second.
+ if (timeoutSecs > 0) {
+ do {
+ dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
+ timeoutSecs--;
+ AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
+ if (rc == WNC_AT_CMD_OK) {
+ dbgPutsNoTime(""); // CR LF
+ return true; //timer.read();
+ }
+ waitMs(500);
+ }
+ while (timeoutSecs > 0);
+ }
+
+ return (false);
+}
+
+WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const char * s, uint32_t n, uint16_t numSock, bool isTcp)
+{
+ AtCmdErr_e result;
+
+ if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) {
+ string * pRespStr;
+ char num2str[10];
+ string cmd_str;
+
+ if (isTcp == true)
+ cmd_str="AT@SOCKWRITE=";
+ else
+ cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND=";
+
+ cmd_str += _to_string(numSock + 1);
+ cmd_str += ",";
+ _to_string(n);
+ cmd_str += num2str;
+ cmd_str += ",\"";
+ while(*s != '\0') {
+ _to_string((int8_t)*s++);
+ // Always 2-digit ascii hex:
+ if (strlen(num2str) == 1) {
+ num2str[2] = '\0';
+ num2str[1] = num2str[0];
+ num2str[0] = '0';
+ }
+ cmd_str += num2str;
+ }
+ cmd_str += "\"";
+ result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
+ }
+ else {
+ dbgPuts("sockwrite Err, string len bad!");
+ result = WNC_AT_CMD_ERR;
+ }
+
+ return (result);
+}
+
+WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint32_t * numRead, uint16_t n, uint16_t numSock, bool isTcp)
+{
+ AtCmdErr_e result = WNC_AT_CMD_OK;
+ *numRead = 0;
+
+ if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) {
+ string * pRespStr;
+ string cmd_str;
+ size_t pos_start, pos_end;
+ int i;
+
+ if (isTcp == true)
+ cmd_str="AT@SOCKREAD=";
+ else
+ cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
+
+ cmd_str += _to_string(numSock + 1);
+ cmd_str += ",";
+ cmd_str += _to_string(n);
+
+ // Experimental: read should not need to check cell net status
+ result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
+ if (result == WNC_AT_CMD_OK) {
+ pos_start = pRespStr->find("\"") + 1;
+ pos_end = pRespStr->rfind("\"") - 1;
+
+ // Make sure search finds what it's looking for!
+ if (pos_start != string::npos && pos_end != string::npos)
+ i = (pos_end - pos_start + 1); // Num hex chars, 2 per byte
+ else
+ i = 0;
+
+ // If data convert the hex string into byte values
+ if (i > 0 && i <= (2*MAX_WNC_READ_BYTES))
+ {
+ string byte;
+ while (pos_start < pos_end) {
+ byte = pRespStr->substr(pos_start, 2);
+ *pS += (char)strtol(byte.c_str(), NULL, 16);
+ pos_start += 2;
+ (*numRead)++;
+ }
+ }
+ }
+ }
+ else {
+ dbgPuts("sockread Err, to many to read!");
+ result = WNC_AT_CMD_ERR;
+ }
+
+ return (result);
+}
+
+bool WncController::at_reinitialize_mdm(void)
+{
+ // Atempt to re-register
+// string * pRespStr;
+// dbgPuts("Force re-register!");
+// at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs);
+// waitMs(31000);
+// at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs);
+// waitMs(31000);
+
+ // Initialize the modem
+ dbgPuts("Modem RE-initializing with SOFT Reset...");
+
+ string * pRespStr;
+ at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs);
+ waitMs(5000);
+
+ // Now, give the modem time to start responding by
+ // sending simple 'AT' commands to the modem once per second.
+ int timeoutSecs = WNC_REINIT_MAX_TIME_MS;
+ do {
+ dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
+ AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr);
+ if (rc == WNC_AT_CMD_OK) {
+ dbgPutsNoTime(""); // CR LF
+ break;
+ }
+ waitMs(500);
+ timeoutSecs--;
+ }
+ while (timeoutSecs > 0);
+
+ if (timeoutSecs <= 0)
+ dbgPuts("\r\nModem RE-init FAILED!");
+ else
+ dbgPuts("\r\nModem RE-init complete!");
+
+ return (timeoutSecs > 0);
+}
+
+WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf)
+{
+ rsp->erase(); // Clean up from possible prior cmd response
+
+ // Don't bother the WNC if user hasn't turned it on.
+ if (m_sState == WNC_OFF)
+ return (WNC_AT_CMD_WNC_NOT_ON);
+
+ size_t n = strlen(cmd);
+
+ // Wait per WNC advise
+ waitMs(WNC_WAIT_FOR_AT_CMD_MS);
+
+ if (cmd && n > 0) {
+ sendCmd(cmd, crLf);
+// sendCmdSlow(cmd, n, 500); // 3rd arg is micro seconds between chars sent
+ }
+
+ startTimerA();
+ while (getUsTimerTicksA() < timeout_ms) {
+ n = mdmGetline(*rsp, timeout_ms - getUsTimerTicksA());
+
+ if (n == 0)
+ continue;
+
+ if (rsp->rfind("OK") != string::npos) {
+ stopTimerA();
+ return (WNC_AT_CMD_OK);
+ }
+
+ if (rsp->rfind("+CME ERROR") != string::npos) {
+ stopTimerA();
+ return (WNC_AT_CMD_ERRCME);
+ }
+
+ if (rsp->rfind("@EXTERR") != string::npos) {
+ stopTimerA();
+ return (WNC_AT_CMD_ERREXT);
+ }
+
+ if (rsp->rfind("ERROR") != string::npos) {
+ stopTimerA();
+ return (WNC_AT_CMD_ERR);
+ }
+ }
+ stopTimerA();
+
+ return (WNC_AT_CMD_TIMEOUT);
+}
+
+bool WncController::at_setapn_wnc(const char * const apnStr)
+{
+ string * pRespStr;
+
+ string cmd_str("AT%PDNSET=1,");
+ cmd_str += apnStr;
+ cmd_str += ",IP";
+ 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
+ return (true);
+ else
+ return (false);
+}
+
+bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber)
+{
+ string * pRespStr;
+ AtCmdErr_e cmdRes;
+ cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER
+ if (cmdRes != WNC_AT_CMD_OK)
+ return (false);
+
+ if (pRespStr->size() == 0) {
+ dbgPuts("Strange RSSI result!");
+ return (false);
+ }
+ else {
+ size_t pos1 = pRespStr->find("SQ:");
+ size_t pos2 = pRespStr->rfind(",");
+ // Sanity check
+ if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) {
+ string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 );
+ int rawRssi = atoi(subStr.c_str());
+
+ // Convert WNC RSSI into dBm range:
+ // 0 - -113 dBm
+ // 1 - -111 dBm
+ // 2..30 - -109 to -53 dBm
+ // 31 - -51dBm or >
+ // 99 - not known or not detectable
+ if (rawRssi == 99)
+ *dBm = -199;
+ else if (rawRssi == 0)
+ *dBm = -113;
+ else if (rawRssi == 1)
+ *dBm = -111;
+ else if (rawRssi == 31)
+ *dBm = -51;
+ else if (rawRssi >= 2 && rawRssi <= 30)
+ *dBm = -113 + 2 * rawRssi;
+ else {
+ dbgPuts("Invalid RSSI!");
+ return (false);
+ }
+ // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4
+ // 99 - unknown or undetectable
+ subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1));
+ *ber = atoi(subStr.c_str());
+ }
+ else {
+ dbgPuts("Strange RSSI result2!");
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+bool WncController::checkCellLink(void)
+{
+ string * pRespStr;
+ size_t pos;
+ int regSts;
+ int cmdRes1, cmdRes2;
+
+ if (m_sState == WNC_OFF)
+ return (false);
+
+ m_sState = WNC_ON_NO_CELL_LINK;
+
+ if (m_sMoreDebugEnabled)
+ dbgPuts("<-------- Begin Cell Status ------------");
+
+ cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER
+ cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs); // Check if SIM locked
+
+ if ((cmdRes1 != WNC_AT_CMD_OK) && (cmdRes2 != WNC_AT_CMD_OK))
+ {
+ if (m_sMoreDebugEnabled)
+ {
+ if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT))
+ dbgPuts("------------ WNC No Response! --------->");
+ else
+ dbgPuts("------------ WNC Cmd Error! ----------->");
+ }
+ return (false);
+ }
+
+ // If SIM Card not ready don't bother with commands!
+ if (pRespStr->find("CPIN: READY") == string::npos)
+ {
+ if (m_sMoreDebugEnabled)
+ dbgPuts("------------ WNC SIM Problem! --------->");
+
+ return (false);
+ }
+
+ // SIM card OK, now check for signal and cellular network registration
+ cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs); // Check if registered on network
+ if (cmdRes1 != WNC_AT_CMD_OK)
+ {
+ if (m_sMoreDebugEnabled)
+ dbgPuts("------------ WNC +CREG? Fail! --------->");
+
+ return (false);
+ }
+ else
+ {
+ pos = pRespStr->find("CREG: ");
+ if (pos != string::npos)
+ {
+ // The registration is the 2nd arg in the comma separated list
+ *pRespStr = pRespStr->substr(pos+8, 1);
+ regSts = atoi(pRespStr->c_str());
+ switch (regSts) {
+ case 1:
+ case 5:
+ case 6:
+ case 7:
+ m_sReadyForSMS = true;
+ break;
+ default:
+ m_sReadyForSMS = false;
+ dbgPuts("SMS Service Down!");
+ }
+
+ // 1 - registered home, 5 - registered roaming
+ if ((regSts != 1) && (regSts != 5))
+ {
+ if (m_sMoreDebugEnabled)
+ dbgPuts("------ WNC Cell Link Down for Data! --->");
+
+ return (false);
+ }
+ }
+
+ if (m_sMoreDebugEnabled)
+ dbgPuts("------------ WNC Ready ---------------->");
+ }
+
+ m_sState = WNC_ON;
+
+ return (true);
+}
+
+int WncController::dbgPutsNoTime(const char * s, bool crlf)
+{
+ if (m_sDebugEnabled == true) {
+ int r = dbgWriteBytes(s);
+ if (crlf == true)
+ return (dbgWriteBytes("\r\n"));
+ else
+ return (r);
+ }
+ else
+ return 0;
+};
+
+int WncController::dbgPuts(const char * s, bool crlf)
+{
+ dbgPutsNoTime("[*] ", false);
+ dbgPutsNoTime(_to_string(getLogTimerTicks()), false);
+ dbgPutsNoTime(" ");
+
+ int r = dbgPutsNoTime(s, false);
+ if (crlf == true)
+ return (dbgPutsNoTime("\r\n", false));
+ else
+ return (r);
+};
+
+void WncController::sendCmd(const char * cmd, bool crLf)
+{
+ puts(cmd);
+ if (crLf == true)
+ puts("\r\n");
+}
+
+// WNC used to have troubles handling full speed, seems to not need this now.
+void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf)
+{
+ while (n--) {
+ putc(*cmd++);
+ waitUs(wait_uS);
+ };
+ if (crLf == true) {
+ putc('\r');
+ waitUs(wait_uS);
+ putc('\n');
+ waitUs(wait_uS);
+ }
+}
+
+
+}; // End namespace WncController_fk
