driver for the WNC M14A2A Cellular Data Module

Files at this revision

API Documentation at this revision

Comitter:
JMF
Date:
Tue Feb 06 16:10:48 2018 +0000
Commit message:
Incorporating changes suggested by ARM

Changed in this revision

WNC14A2AInterface/WNC14A2AInterface.cpp Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WNC14A2AInterface.h Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WNCDebug.h Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WncControllerK64F/WncController/WncController.cpp Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WncControllerK64F/WncController/WncController.h Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WncControllerK64F/WncControllerK64F.cpp Show annotated file Show diff for this revision Revisions of this file
WNC14A2AInterface/WncControllerK64F/WncControllerK64F.h Show annotated file Show diff for this revision Revisions of this file
mbed_lib.json Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WNC14A2AInterface.cpp	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,908 @@
+/**
+* copyright (c) 2017-2018, James Flynn
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+ 
+/**
+*   @file   WNC14A2AInterface.cpp
+*   @brief  WNC14A2A implementation of NetworkInterfaceAPI for Mbed OS
+*
+*   @author James Flynn
+*
+*   @date   1-Feb-2018
+*/
+
+#include "WNC14A2AInterface.h"
+#include <Thread.h>
+#include "mbed_events.h"
+
+#include <string> 
+#include <ctype.h>
+
+#define WNC14A2A_READ_TIMEOUTMS        2000              //read this long total until we decide there is no data to receive in MS
+#define WNC14A2A_COMMUNICATION_TIMEOUT 100               //how long (ms) to wait for a WNC14A2A connect response
+#define WNC_BUFF_SIZE                  1500              //total number of bytes in a single WNC call
+#define UART_BUFF_SIZE                 4000              //size of our internal uart buffer
+#define ISR_FREQ                       250               //frequency in ms to run the isr handler
+
+//
+// The WNC device does not generate interrutps on received data, so the module must be polled 
+// for data availablility.  To implement a non-blocking mode, interrupts are simulated using
+// mbed OS Event Queues.  These Constants are used to manage that sequence.
+//
+#define READ_INIT                      10
+#define READ_START                     11
+#define READ_ACTIVE                    12
+#define DATA_AVAILABLE                 13
+#define TX_IDLE                        20
+#define TX_STARTING                    21
+#define TX_ACTIVE                      22
+#define TX_COMPLETE                    23
+
+#if MBED_CONF_APP_WNC_DEBUG == true
+#define debugOutput(...)      WNC14A2AInterface::_dbOut(__VA_ARGS__)
+#define debugDump_arry(...)   WNC14A2AInterface::_dbDump_arry(__VA_ARGS__)
+#else
+#define debugOutput(...)      {/* __VA_ARGS__ */}
+#define debugDump_arry(...)   {/* __VA_ARGS__ */}
+#endif
+
+
+//
+// GPIO Pins used to initialize the WNC parts on the Avnet WNC Shield
+//
+DigitalOut  mdm_uart2_rx_boot_mode_sel(PTC17);  // on powerup, 0 = boot mode, 1 = normal boot
+DigitalOut  mdm_power_on(PTB9);                 // 0 = modem on, 1 = modem off (hold high for >5 seconds to cycle modem)
+DigitalOut  mdm_wakeup_in(PTC2);                // 0 = let modem sleep, 1 = keep modem awake -- Note: pulled high on shield
+DigitalOut  mdm_reset(PTC12);                   // active high
+DigitalOut  shield_3v3_1v8_sig_trans_ena(PTC4); // 0 = disabled (all signals high impedence, 1 = translation active
+DigitalOut  mdm_uart1_cts(PTD0);                // WNC doesn't utilize RTS/CTS but the pin is connected
+
+using namespace WncControllerK64F_fk;            // namespace for the AT controller class use
+
+//! associations for the controller class to use. Order of pins is critical.
+WncGpioPinListK64F wncPinList = { 
+    &mdm_uart2_rx_boot_mode_sel,
+    &mdm_power_on,
+    &mdm_wakeup_in,
+    &mdm_reset,
+    &shield_3v3_1v8_sig_trans_ena,
+    &mdm_uart1_cts
+};
+
+Thread smsThread, isrThread;                          //SMS thread for receiving SMS, recv is for simulated rx-interrupt
+static Mutex _pwnc_mutex;                           //because WNC class is not re-entrant
+
+static WNCSOCKET _sockets[WNC14A2A_SOCKET_COUNT];     //WNC supports 8 open sockets but driver only supports 1 currently
+BufferedSerial   mdmUart(PTD3,PTD2,UART_BUFF_SIZE,1); //UART for WNC Module
+
+/*   Constructor
+*
+*  @brief    May be invoked with or without the debug pointer.
+*  @note     After the constructor has completed, call check 
+*  _errors to determine if any errors occured. Possible values:
+*           NSAPI_ERROR_UNSUPPORTED 
+*           NSAPI_ERROR_DEVICE_ERROR
+*/
+WNC14A2AInterface::WNC14A2AInterface(WNCDebug *dbg) : 
+ m_wncpoweredup(0),
+ _pwnc(NULL),
+ m_active_socket(-1),
+ m_smsmoning(0)
+{
+    _errors = NSAPI_ERROR_OK;  //tracks internal driver errors only
+    m_debug=0;                 //for internal driver debug
+
+    if( _pwnc ) {              //only a single instance allowed
+        _errors =  NSAPI_ERROR_UNSUPPORTED;
+        return;
+        }
+    memset(_mac_address,0x00,sizeof(_mac_address));
+    for( int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) {
+        _sockets[i].socket = i;
+        _sockets[i].addr = NULL;
+        _sockets[i].opened=false;
+        _sockets[i]._wnc_opened=false;
+        _sockets[i].proto=NSAPI_TCP;
+        }
+
+    _debugUart = dbg;           
+    if( dbg != NULL ) 
+        _pwnc = new WncControllerK64F(&wncPinList, &mdmUart, dbg);
+    else 
+        _pwnc = new WncControllerK64F_fk::WncControllerK64F(&wncPinList, &mdmUart, NULL);
+        
+    if( !_pwnc ) {
+        debugOutput("FAILED to open WncControllerK64!");
+        _errors = NSAPI_ERROR_DEVICE_ERROR;
+        }
+
+    isrThread.start(callback(&isr_queue,&EventQueue::dispatch_forever));
+}
+
+//! Standard destructor
+WNC14A2AInterface::~WNC14A2AInterface()
+{
+    delete _pwnc;  //free the existing WncControllerK64F object
+}
+
+
+nsapi_error_t WNC14A2AInterface::connect()   //can be called with no arguments or with arguments
+{
+    debugOutput("ENTER connect(void)");
+    return connect(NULL,NULL,NULL);
+}
+
+nsapi_error_t WNC14A2AInterface::connect(const char *apn, const char *username, const char *password) 
+{
+    debugOutput("ENTER connect(apn,user,pass)");
+    if( !_pwnc )
+        return (_errors=NSAPI_ERROR_NO_CONNECTION);
+
+    if (!apn)
+        apn = "m2m.com.attz";
+
+    _pwnc_mutex.lock();
+    if (!m_wncpoweredup) {
+        debugOutput("call powerWncOn(%s,40)",apn);
+        m_wncpoweredup=_pwnc->powerWncOn(apn,40);
+        _errors = m_wncpoweredup? 1:0;
+        }
+    else {          //powerWncOn already called, set a new APN
+        debugOutput("set APN=%s",apn);
+        _errors = _pwnc->setApnName(apn)? 1:0;
+        }
+
+    _errors |= _pwnc->getWncNetworkingStats(&myNetStats)? 2:0;
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT connect (%02X)",_errors);
+    return (!_errors)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_OK;
+}
+
+const char *WNC14A2AInterface::get_ip_address()
+{
+    const char *ptr=NULL; 
+
+    _pwnc_mutex.lock();
+    if ( _pwnc->getWncNetworkingStats(&myNetStats) ) {
+        _pwnc_mutex.unlock();
+        CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);
+        ptr = &myNetStats.ip[0];
+    }
+    _pwnc_mutex.unlock();
+    _errors=NSAPI_ERROR_NO_CONNECTION;
+    return ptr;
+}
+
+int WNC14A2AInterface::socket_open(void **handle, nsapi_protocol_t proto) 
+{
+    int i;
+    debugOutput("ENTER socket_open()");
+
+    // search through the available sockets (WNC can only support a max amount).
+    for( i=0; i<WNC14A2A_SOCKET_COUNT; i++ )
+        if( !_sockets[i].opened )
+            break;
+
+    if( i == WNC14A2A_SOCKET_COUNT ) {
+        _errors=NSAPI_ERROR_NO_SOCKET;
+        return -1;
+        }
+
+    m_active_socket = i;               //this is the active socket
+    _sockets[i].socket = i;            //also save for later
+    _sockets[i].url="";
+    _sockets[i].opened = true;         
+    _sockets[i]._wnc_opened=false;
+    _sockets[i].addr = NULL;           //not yet open
+    _sockets[i].proto = proto;         //don't know if it is TCP/UDP
+    _sockets[i]._callback = NULL;
+    _sockets[i]._cb_data = NULL;         
+    *handle = &_sockets[i];
+
+    m_recv_wnc_state = READ_START;
+    m_tx_wnc_state = TX_IDLE;
+
+    debugOutput("EXIT socket_open; Socket=%d, OPEN=%s, protocol =%s",
+                i, _sockets[i].opened?"YES":"NO", (_sockets[i].proto==NSAPI_UDP)?"UDP":"TCP");
+    
+    _errors = NSAPI_ERROR_OK;
+    return i;
+}
+
+int WNC14A2AInterface::socket_connect(void *handle, const SocketAddress &address) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;   
+    int rval = 0;
+
+    debugOutput("ENTER socket_connect(); IP=%s; PORT=%d;", address.get_ip_address(), address.get_port());
+    
+    if (!_pwnc || m_active_socket == -1 || !wnc->opened ) {
+        _errors = NSAPI_ERROR_NO_SOCKET;
+        return -1;
+        }
+
+    m_active_socket = wnc->socket;  //in case the user is asking for a different socket
+    wnc->addr = address;
+                                
+    //
+    //try connecting to URL if possible, if no url, try IP address
+    //
+
+    _pwnc_mutex.lock();
+    if( wnc->url.empty() ) {
+        if( !_pwnc->openSocketIpAddr(m_active_socket, address.get_ip_address(), address.get_port(), 
+                                       (wnc->proto==NSAPI_UDP)?0:1, WNC14A2A_COMMUNICATION_TIMEOUT) ) 
+            rval = -1;
+        }
+     else {
+        if( !_pwnc->openSocketUrl(m_active_socket, wnc->url.c_str(), wnc->addr.get_port(), (wnc->proto==NSAPI_UDP)?0:1) ) 
+            rval = -1;
+        }
+    _pwnc_mutex.unlock();
+    if( !rval ) {
+        wnc->_wnc_opened = true;
+        debugOutput("EXIT socket_connect()");
+        }
+
+    m_recv_wnc_state = READ_START;
+    m_tx_wnc_state = TX_IDLE;
+
+    if( wnc->_callback != NULL ) 
+        wnc->_callback( wnc->_cb_data );
+
+    return rval;
+}
+
+nsapi_error_t WNC14A2AInterface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version)
+{
+    nsapi_error_t ret = NSAPI_ERROR_OK;
+    char ipAddrStr[25];
+    int  t_socket = 0;  //use a temporary socket place holder
+
+    debugOutput("ENTER gethostbyname(); IP=%s; PORT=%d; URL=%s;", address->get_ip_address(), address->get_port(), name);
+    memset(ipAddrStr,0x00,sizeof(ipAddrStr));
+    
+    if (!_pwnc) 
+        return (_errors = NSAPI_ERROR_NO_SOCKET);
+        
+    if (m_active_socket != -1)      //we might have been called before a socket was opened
+        t_socket = m_active_socket; //if so, do nothing with the active socket index
+
+    //Execute DNS query.  
+    _pwnc_mutex.lock();
+    if( !_pwnc->resolveUrl(t_socket, name) )  
+        ret = _errors = NSAPI_ERROR_DEVICE_ERROR;
+
+    //Get IP address that the URL was resolved to
+    if( !_pwnc->getIpAddr(t_socket, ipAddrStr) )
+        ret = _errors = NSAPI_ERROR_DEVICE_ERROR;
+    _pwnc_mutex.unlock();
+
+    if( ret != NSAPI_ERROR_OK )
+        return ret;
+
+    address->set_ip_address(ipAddrStr);
+
+    if( t_socket == m_active_socket ) {
+        _sockets[m_active_socket].url=name;
+        _sockets[m_active_socket].addr.set_ip_address(ipAddrStr);
+        }
+
+    debugOutput("EXIT gethostbyname()");
+    return (_errors = ret);
+}
+ 
+
+int WNC14A2AInterface::socket_close(void *handle)
+{
+    WNCSOCKET *wnc = (WNCSOCKET*)handle;
+    int rval = 0;
+
+    debugOutput("ENTER socket_close()");
+
+    if (!_pwnc || m_active_socket == -1) {
+        _errors = NSAPI_ERROR_NO_SOCKET;
+        return -1;
+        }
+    else
+        m_active_socket = wnc->socket; //just in case sending to a socket that wasn't last used
+    
+    m_tx_wnc_state = TX_IDLE;               //reset TX state
+    if( m_recv_wnc_state != READ_START ) {  //reset RX state
+        m_recv_events = 0;  //force a timeout
+        while( m_recv_wnc_state !=  DATA_AVAILABLE ) 
+            wait(1);  //someone called close while a read was happening
+        }
+
+    _pwnc_mutex.lock();
+    if( !_pwnc->closeSocket(m_active_socket) ) {
+        _errors = NSAPI_ERROR_DEVICE_ERROR;
+        rval = -1;
+        }
+    _pwnc_mutex.unlock();
+
+    if( !rval ) {
+        wnc->opened   = false;       //no longer in use
+        wnc->addr     = NULL;        //not open
+        wnc->proto    = NSAPI_TCP;   //assume TCP for now
+        _errors       = NSAPI_ERROR_OK;
+        wnc->_cb_data = NULL;
+        wnc->_callback= NULL;
+        }
+
+    debugOutput("EXIT socket_close()");
+    return rval;
+}
+
+const char *WNC14A2AInterface::get_mac_address()
+{
+    string mac, str;
+    debugOutput("ENTER get_mac_address()");
+
+    _pwnc_mutex.lock();
+    if( _pwnc->getICCID(&str) ) {
+        _pwnc_mutex.unlock();
+        CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), null);
+        mac = str.substr(3,20);
+        mac[2]=mac[5]=mac[8]=mac[11]=mac[14]=':';
+        strncpy(_mac_address, mac.c_str(), mac.length());
+        debugOutput("EXIT get_mac_address() - %s",_mac_address);
+        return _mac_address;
+    }
+    _pwnc_mutex.unlock();
+    debugOutput("EXIT get_mac_address() - NULL");
+    return NULL;
+}
+
+NetworkStack *WNC14A2AInterface::get_stack() {
+    debugOutput("ENTER/EXIT get_stack()");
+    return this;
+}
+
+nsapi_error_t WNC14A2AInterface::disconnect() 
+{
+    debugOutput("ENTER/EXIT disconnect()");
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_error_t WNC14A2AInterface::set_credentials(const char *apn, const char *username, const char *password) 
+{
+
+    _errors=NSAPI_ERROR_OK;
+    debugOutput("ENTER set_credentials()");
+    if( !_pwnc ) 
+        return (_errors=NSAPI_ERROR_NO_CONNECTION);
+        
+    if( !apn )
+        return (_errors=NSAPI_ERROR_PARAMETER);
+
+    _pwnc_mutex.lock();
+    if( !_pwnc->setApnName(apn) )
+        _errors=NSAPI_ERROR_DEVICE_ERROR;
+    _pwnc_mutex.unlock();
+    debugOutput("EXIT set_credentials()");
+    return _errors;
+}
+
+bool WNC14A2AInterface::registered()
+{
+    debugOutput("ENTER registered()");
+    _errors=NSAPI_ERROR_OK;
+
+    if( !_pwnc ) {
+        _errors=NSAPI_ERROR_NO_CONNECTION;
+        return false;
+        }
+
+    _pwnc_mutex.lock();
+    if ( _pwnc->getWncStatus() != WNC_GOOD )
+        _errors=NSAPI_ERROR_NO_CONNECTION;
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT registered()");
+    return (_errors==NSAPI_ERROR_OK);
+}
+
+char* WNC14A2AInterface::getSMSnbr( void ) 
+{
+    char * ret=NULL;
+    string iccid_str;
+    static string msisdn_str;
+
+    if( !_pwnc ) {
+        _errors=NSAPI_ERROR_NO_CONNECTION;
+        return NULL;
+        }
+
+    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);
+
+    _pwnc_mutex.lock();
+    if( !_pwnc->getICCID(&iccid_str) ) {
+        _pwnc_mutex.unlock();
+        return ret;
+        }
+    _pwnc_mutex.unlock();
+ 
+    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);
+
+    _pwnc_mutex.lock();
+    if( _pwnc->convertICCIDtoMSISDN(iccid_str, &msisdn_str) )
+         ret = (char*)msisdn_str.c_str();    
+    _pwnc_mutex.unlock();
+    return ret;
+}
+
+void WNC14A2AInterface::sms_attach(void (*callback)(IOTSMS *))
+{
+    debugOutput("ENTER/EXIT sms_attach()");
+    _sms_cb = callback;
+}
+
+void WNC14A2AInterface::sms_start(void)
+{
+    _pwnc_mutex.lock();                     
+    _pwnc->deleteSMSTextFromMem('*');       
+    _pwnc_mutex.unlock();
+}
+
+void WNC14A2AInterface::sms_listen(uint16_t pp)
+{
+    debugOutput("ENTER sms_listen(%d)",pp);
+    if( !_pwnc ) {
+        _errors=NSAPI_ERROR_NO_CONNECTION;
+        return;
+        }
+
+    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), fail);
+
+    if( m_smsmoning )
+        m_smsmoning = false;
+    if( pp < 1)
+        pp = 30;
+
+
+    debugOutput("setup sms_listen event queue");
+    smsThread.start(callback(&sms_queue,&EventQueue::dispatch_forever));
+
+    sms_start();
+    sms_queue.call_every(pp*1000, mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::handle_sms_event));
+
+    m_smsmoning = true;
+    debugOutput("EXIT sms_listen()");
+}
+
+void WNC14A2AInterface::handle_sms_event()
+{
+    int msgs_available;
+    debugOutput("ENTER handle_sms_event()");
+
+    if ( _sms_cb && m_smsmoning ) {
+        CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), fail);
+        _pwnc_mutex.lock();
+        msgs_available = _pwnc->readUnreadSMSText(&m_smsmsgs, true);
+        _pwnc_mutex.unlock();
+        if( msgs_available ) {
+            debugOutput("Have %d unread texts present",m_smsmsgs.msgCount);
+            for( int i=0; i< m_smsmsgs.msgCount; i++ ) {
+                m_MsgText.number = m_smsmsgs.e[i].number;
+                m_MsgText.date = m_smsmsgs.e[i].date;
+                m_MsgText.time = m_smsmsgs.e[i].time;
+                m_MsgText.msg = m_smsmsgs.e[i].msg;
+                _sms_cb(&m_MsgText);
+                }
+            }
+        }
+    debugOutput("EXIT handle_sms_event");
+}
+
+
+int WNC14A2AInterface::getSMS(IOTSMS **pmsg) 
+{
+    int msgs_available;
+
+    debugOutput("ENTER getSMS()");
+    CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), fail);
+
+    _pwnc_mutex.lock();
+    msgs_available = _pwnc->readUnreadSMSText(&m_smsmsgs, true);
+    _pwnc_mutex.unlock();
+
+    if( msgs_available ) {
+        debugOutput("Have %d unread texts present",m_smsmsgs.msgCount);
+        for( int i=0; i< m_smsmsgs.msgCount; i++ ) {
+            m_MsgText_array[i].number = m_smsmsgs.e[i].number;
+            m_MsgText_array[i].date   = m_smsmsgs.e[i].date;
+            m_MsgText_array[i].time   = m_smsmsgs.e[i].time;
+            m_MsgText_array[i].msg    = m_smsmsgs.e[i].msg;
+            pmsg[i] = (IOTSMS*)&m_MsgText_array[i];
+            }
+        msgs_available = m_smsmsgs.msgCount;
+        }
+    debugOutput("EXIT getSMS");
+    return msgs_available;
+}
+
+
+int WNC14A2AInterface::sendIOTSms(const string& number, const string& message) 
+{
+
+    debugOutput("ENTER sendIOTSms(%s,%s)",number.c_str(), message.c_str());
+    _pwnc_mutex.lock();
+    int i =  _pwnc->sendSMSText((char*)number.c_str(), message.c_str());
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT sendIOTSms(%s,%s)",number.c_str(), message.c_str());
+    return i;
+}
+
+
+void WNC14A2AInterface::socket_attach(void *handle, void (*callback)(void *), void *data)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+
+    debugOutput("ENTER/EXIT socket_attach()");
+    wnc->_callback = callback;
+    wnc->_cb_data = data;
+}
+
+int WNC14A2AInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    
+    debugOutput("ENTER socket_sendto()");
+    
+    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), fail);
+   
+    if (!wnc->_wnc_opened) {
+       int err = socket_connect(wnc, address);
+       if (err < 0) 
+           return err;
+       }
+    wnc->addr = address;
+
+    debugOutput("EXIT socket_sendto()");
+    return socket_send(wnc, data, size);
+}
+
+int WNC14A2AInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    debugOutput("ENTER socket_recvfrom()");
+
+    if (!wnc->_wnc_opened) {
+       debugOutput("need to open a WNC socket first");
+       int err = socket_connect(wnc, *address);
+       if (err < 0) 
+           return err;
+       }
+
+    int ret = socket_recv(wnc, (char *)buffer, size);
+    if (ret >= 0 && address) 
+        *address = wnc->addr;
+    debugOutput("EXIT socket_recvfrom()");
+    return ret;
+}
+
+
+int inline WNC14A2AInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) 
+{
+    debugOutput("ENTER/EXIT socket_accept()");
+    _errors = NSAPI_ERROR_UNSUPPORTED;
+    return -1;
+}
+
+int inline WNC14A2AInterface::socket_bind(void *handle, const SocketAddress &address) 
+{
+    debugOutput("ENTER/EXIT socket_bind()");
+    _errors = NSAPI_ERROR_UNSUPPORTED;
+    return -1;
+}
+
+
+int inline WNC14A2AInterface::socket_listen(void *handle, int backlog)
+{
+   debugOutput("ENTER/EXIT socket_listen()");
+    _errors = NSAPI_ERROR_UNSUPPORTED;
+    return -1;
+}
+
+void WNC14A2AInterface::doDebug( int v )
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    if( !_pwnc )
+        _errors = NSAPI_ERROR_DEVICE_ERROR;
+    else {
+        _pwnc_mutex.lock();
+        _pwnc->enableDebug( (v&1), (v&2) );
+        _pwnc_mutex.unlock();
+        }
+
+    m_debug= v;
+
+    debugOutput("SET debug flag to 0x%02X",v);
+    #endif
+}
+
+/** function to dump a user provided array.
+*
+* @author James Flynn
+* @param  data    pointer to the data array to dump
+* @param  size    number of bytes to dump
+* @return void
+* @date 1-Feb-2018
+*/
+void WNC14A2AInterface::_dbDump_arry( const uint8_t* data, unsigned int size )
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    char buffer[256];
+    unsigned int i, k;
+
+    if( _debugUart != NULL && (m_debug & 0x08) ) {
+        for (i=0; i<size; i+=16) {
+            sprintf(buffer,"[WNC Driver]:0x%04X: ",i);
+            _debugUart->puts(buffer);
+            for (k=0; k<16; k++) {
+                sprintf(buffer, "%02X ", data[i+k]);
+                _debugUart->puts(buffer);
+                }
+            _debugUart->puts(" -- ");
+            for (k=0; k<16; k++) {
+                sprintf(buffer, "%2c", isprint(data[i+k])? data[i+k]:'.');
+                _debugUart->puts(buffer);
+                }
+            _debugUart->puts("\n\r");
+            }
+        }
+    #endif
+}
+
+void WNC14A2AInterface::_dbOut(const char *format, ...) 
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    char buffer[256];
+
+    sprintf(buffer,"[WNC Driver]: ");
+    if( _debugUart != NULL && (m_debug & 0x0c) ) {
+        va_list args;
+        va_start (args, format);
+        _debugUart->puts(buffer);
+        vsnprintf(buffer, sizeof(buffer), format, args);
+        _debugUart->puts(buffer);
+        _debugUart->putc('\n');
+        va_end (args);
+        }
+    #endif
+}
+
+int WNC14A2AInterface::socket_recv(void *handle, void *data, unsigned size) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+
+    debugOutput("ENTER socket_recv(), request %d bytes",size);
+
+    if (!_pwnc || m_active_socket == -1) 
+        return (_errors = NSAPI_ERROR_NO_SOCKET);
+
+    if( size < 1 || data == NULL )  // should never happen
+        return 0; 
+
+    switch( m_recv_wnc_state ) {
+        case READ_START:  //need to start a read sequence of events
+            m_recv_wnc_state = READ_INIT;
+            m_recv_socket   = wnc->socket; //just in case sending to a socket that wasn't last used
+            m_recv_dptr     = (uint8_t*)data;
+            m_recv_orig_size= size;
+            m_recv_total_cnt= 0;
+            m_recv_timer    = 0;
+            m_recv_events   = 1;
+            m_recv_req_size = (uint32_t)size;
+            m_recv_return_cnt=0;
+
+            if( m_recv_req_size > WNC_BUFF_SIZE) {
+                m_recv_events  =  ((uint32_t)size/WNC_BUFF_SIZE);
+                m_recv_req_size= WNC_BUFF_SIZE;
+                }
+            m_recv_callback = wnc->_callback;
+            m_recv_cb_data  = wnc->_cb_data;
+
+            if( !rx_event() ){
+                m_recv_wnc_state = READ_ACTIVE;
+                isr_queue.call_in(ISR_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_isr_event));
+                return NSAPI_ERROR_WOULD_BLOCK;
+                }
+            //was able to get the requested data in a single transaction so fall thru and finish
+            //no need to schedule the background task
+
+        case DATA_AVAILABLE:
+            debugOutput("EXIT socket_recv(), return %d bytes",m_recv_return_cnt);
+            debugDump_arry((const uint8_t*)data,m_recv_return_cnt);
+            m_recv_wnc_state = READ_START;
+            return m_recv_return_cnt;
+
+        case READ_INIT:
+        case READ_ACTIVE:
+            return NSAPI_ERROR_WOULD_BLOCK;
+
+        default:
+            debugOutput("EXIT socket_recv(), NSAPI_ERROR_DEVICE_ERROR");
+            return (_errors = NSAPI_ERROR_DEVICE_ERROR);
+        }
+}
+
+
+int WNC14A2AInterface::socket_send(void *handle, const void *data, unsigned size) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+
+    debugOutput("ENTER socket_send() send %d bytes",size);
+
+    if (!_pwnc || m_active_socket == -1) {
+        _errors = NSAPI_ERROR_NO_SOCKET;
+        return 0;
+        }
+    else
+        m_active_socket = wnc->socket; //just in case sending to a socket that wasn't last used
+
+    if( size < 1 || data == NULL )  // should never happen
+        return 0; 
+
+    switch( m_tx_wnc_state ) {
+        case TX_IDLE:
+            m_tx_wnc_state = TX_STARTING;
+            debugDump_arry((const uint8_t*)data,size);
+            m_tx_socket    = wnc->socket; //just in case sending to a socket that wasn't last used
+            m_tx_dptr      = (uint8_t*)data;
+            m_tx_orig_size = size;
+            m_tx_req_size  = (uint32_t)size;
+            m_tx_total_sent= 0;
+            m_tx_callback  = wnc->_callback;
+            m_tx_cb_data   = wnc->_cb_data;
+
+            if( m_tx_req_size > UART_BUFF_SIZE ) 
+                m_tx_req_size= UART_BUFF_SIZE;
+
+            if( !tx_event() ) {   //if we didn't sent all the data at once, continue in background
+                m_tx_wnc_state = TX_ACTIVE;
+                isr_queue.call_in(ISR_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_isr_event));
+                return NSAPI_ERROR_WOULD_BLOCK;
+                }
+            //all data sent so fall through to TX_COMPLETE
+
+        case TX_COMPLETE:
+            debugOutput("EXIT socket_send(), sent %d bytes", m_tx_total_sent);
+            m_tx_wnc_state = TX_IDLE;
+            return m_tx_total_sent;
+
+        case TX_ACTIVE:
+        case TX_STARTING:
+            return NSAPI_ERROR_WOULD_BLOCK;
+
+        default:
+            debugOutput("EXIT socket_send(), NSAPI_ERROR_DEVICE_ERROR");
+            return (_errors = NSAPI_ERROR_DEVICE_ERROR);
+        }
+}
+
+
+void WNC14A2AInterface::wnc_isr_event()
+{
+    int done = 1;
+
+    debugOutput("ENTER wnc_isr_event()");
+
+    if( m_recv_wnc_state == READ_ACTIVE ) 
+        done &= rx_event();
+    if( m_tx_wnc_state == TX_ACTIVE )
+        done &= tx_event();
+
+    if( !done ) 
+        isr_queue.call_in(ISR_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_isr_event));
+
+    debugOutput("EXIT wnc_isr_event()");
+}
+
+
+int WNC14A2AInterface::tx_event()
+{
+    debugOutput("ENTER tx_event()");
+
+    _pwnc_mutex.lock();
+    if( _pwnc->write(m_tx_socket, m_tx_dptr, m_tx_req_size) ) 
+        m_tx_total_sent += m_tx_req_size;
+    else
+        debugOutput("tx_event WNC failed to send()");
+    CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), resume);
+    _pwnc_mutex.unlock();
+    
+    if( m_tx_total_sent < m_tx_orig_size ) {
+        m_tx_dptr += m_tx_req_size;
+        m_tx_req_size = m_tx_orig_size-m_tx_total_sent;
+
+        if( m_tx_req_size > UART_BUFF_SIZE) 
+            m_tx_req_size= UART_BUFF_SIZE;
+
+        debugOutput("EXIT tx_event(), need to send %d more bytes.",m_tx_req_size);
+        return 0;
+        }
+    debugOutput("EXIT tx_event, data sent");
+    m_tx_wnc_state = TX_COMPLETE;
+    if( m_tx_callback != NULL ) 
+        m_tx_callback( m_tx_cb_data );
+    m_tx_cb_data = NULL; 
+    m_tx_callback = NULL;
+
+    return 1;
+}
+
+int WNC14A2AInterface::rx_event()
+{
+    uint32_t k;
+
+    debugOutput("ENTER rx_event()");
+    _pwnc_mutex.lock();
+    int cnt = _pwnc->read(m_recv_socket, m_recv_dptr,  m_recv_req_size);
+    CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), resume);
+    _pwnc_mutex.unlock();
+    if( cnt ) {
+        m_recv_dptr += cnt;
+        m_recv_total_cnt += cnt;
+        m_recv_req_size = m_recv_orig_size-m_recv_total_cnt;
+        if( m_recv_req_size > WNC_BUFF_SIZE )
+            m_recv_req_size = WNC_BUFF_SIZE;
+        --m_recv_events;
+        m_recv_timer = 0;  //restart the timer
+        }
+    else if( ++m_recv_timer > (WNC14A2A_READ_TIMEOUTMS/ISR_FREQ) ) {
+        //didn't get all requested data and we timed out waiting
+        CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), resume);
+        debugOutput("EXIT rx_event(), TIME-OUT!");
+        k = m_recv_return_cnt = m_recv_total_cnt;
+        m_recv_wnc_state = DATA_AVAILABLE;
+        if( m_recv_callback != NULL ) 
+            m_recv_callback( m_recv_cb_data );
+        m_recv_cb_data = NULL; 
+        m_recv_callback = NULL;
+        m_recv_return_cnt = k;
+        return 1;
+        }
+
+    if( m_recv_events > 0 ) {
+        debugOutput("EXIT rx_event() but sechedule for more data.");
+        return 0;
+        }
+    else if( m_recv_total_cnt >= m_recv_orig_size ){
+        k = m_recv_return_cnt = m_recv_total_cnt;  //save because the callback func may call RX again on us
+        debugOutput("EXIT rx_event(), data available.");
+        m_recv_wnc_state = DATA_AVAILABLE;  
+        if( m_recv_callback != NULL ) 
+            m_recv_callback( m_recv_cb_data );
+        m_recv_cb_data = NULL;
+        m_recv_callback = NULL;
+        m_recv_return_cnt = k;
+        return 1;
+        }
+    else{
+        debugOutput("EXIT rx_event() but sechedule for more data.");
+        return 0;
+        }
+     
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WNC14A2AInterface.h	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,443 @@
+/**
+* copyright (c) 2017-2018, James Flynn
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/**
+*  @file WNC14A2AInterface.h
+*  @brief Implements a standard NetworkInterface class for use with WNC M14A2A 
+*  data module. 
+*
+*  @author James Flynn
+* 
+*  @date 1-Feb-2018
+*  
+*/
+ 
+#ifndef WNC14A2A_INTERFACE_H
+#define WNC14A2A_INTERFACE_H
+
+#include <stdint.h>
+
+#include "mbed.h"
+#include "Callback.h"
+#include "WNCDebug.h"
+#include "WncControllerK64F/WncControllerK64F.h"
+
+#define WNC14A2A_SOCKET_COUNT 5
+
+typedef struct smsmsg_t {
+        string number;
+        string date;
+        string time;
+        string msg;
+    } IOTSMS;
+
+typedef struct socket_t {
+        int socket;                         //index of this socket
+        string url;
+        SocketAddress addr;                 //hold info for this socket
+        bool opened;                        //has the socket been opened
+        bool _wnc_opened;                   //has the socket been opened
+        int proto;                          //this is a TCP or UDP socket
+        void (*_callback)(void*);           //callback used with attach
+        void *_cb_data;                     //callback data to be returned
+    } WNCSOCKET;
+
+#define WNC_DEBUG   0           //1=enable the WNC startup debug output
+                                //0=disable the WNC startup debug output
+#define STOP_ON_FE  1           //1=hang forever if a fatal error occurs
+                                //0=simply return failed response for all socket calls
+#define DISPLAY_FE  1           //1 to display the fatal error when it occurs
+                                //0 to NOT display the fatal error
+#define RESETON_FE  0           //1 to cause the MCU to reset on fatal error
+                                //0 to NOT reset the MCU
+
+#define APN_DEFAULT             "m2m.com.attz"
+                        
+/** Error Handling macros & data
+*   @brief  The macros CHK_WNCFE is used to check if a fatal error has occured. If it has
+*           then execute the action specified: fail, void, null, resume
+*
+*    CHK_WNCFE( condition-to-check, fail|void|null|resume )
+*
+*     'fail'   if you want to FATAL_WNC_ERROR will be called.  
+*     'void'   if you want to execute a void return
+*     'null'   if you want to execute a null return
+*     'resume' if you simply want to resume program execution
+*
+*  There are several settings that control how FATAL_WNC_ERROR behaves:
+*      1) RESETON_FE determines if the system will reset or hang.
+*      2) DISPLAY_FE determine if an error message is generated or not
+*
+*  The DISPLAY_FE setting determines if a failure message is displayed. 
+*  If set to 1, user sees this messageo:
+*
+*      WNC FAILED @ source-file-name:source-file-line-number
+*
+*  if not set, nothing is displayed.
+*/
+
+#define FATAL_FLAG  WncController::WNC_NO_RESPONSE
+#define WNC_GOOD    WncController::WNC_ON
+
+#define RETfail return -1
+#define RETvoid return
+#define RETnull return NULL
+#define RETresume   
+
+#define DORET(x) RET##x
+
+#define TOSTR(x) #x
+#define INTSTR(x) TOSTR(x)
+#define FATAL_STR (char*)(__FILE__ ":" INTSTR(__LINE__))
+
+#if RESETON_FE == 1   //reset on fatal error
+#define MCURESET     ((*((volatile unsigned long *)0xE000ED0CU))=(unsigned long)((0x5fa<<16) | 0x04L))
+#define RSTMSG       "RESET MCU! "
+#else
+#define MCURESET
+#define RSTMSG       ""
+#endif
+
+#if DISPLAY_FE == 1  //display fatal error message
+#define PFE     {if(_debugUart)_debugUart->printf((char*)RSTMSG "\r\n>>WNC FAILED @ %s\r\n", FATAL_STR);}
+#else
+#define PFE
+#endif
+
+#if STOP_ON_FE == 1  //halt cpu on fatal error
+#define FATAL_WNC_ERROR(v)  {_fatal_err_loc=FATAL_STR;PFE;MCURESET;while(1);}
+#else
+#define FATAL_WNC_ERROR(v)  {_fatal_err_loc=FATAL_STR;PFE;DORET(v);}
+#endif
+
+#define CHK_WNCFE(x,y)    if( x ){FATAL_WNC_ERROR(y);}
+
+#define MAX_SMS_MSGS    3
+
+using namespace WncController_fk;
+ 
+/** WNC14A2AInterface class
+ *  Implementation of the NetworkInterface for WNC14A2A 
+ */
+class WNC14A2AInterface : public NetworkStack, public CellularInterface
+{
+public:
+    /** WNC14A2AInterface Constructor.
+     * @param optionally include a pointer to WNCDEBUG object for 
+     * debug information to be displayed.
+     */
+    WNC14A2AInterface(WNCDebug *_dbgUart = NULL);
+    virtual ~WNC14A2AInterface();
+
+    /** Set the cellular network credentials
+     *
+     *  @param apn      Optional, APN of network
+     *  @param user     Optional, username --not used--
+     *  @param pass     Optional, password --not used--
+     *  @return         nsapi_error_t
+     */
+    virtual nsapi_error_t set_credentials(const char *apn = 0,
+            const char *username = 0, const char *password = 0);
+ 
+    /** Connect to the network
+     *
+     *  @param apn      Optional, APN of network
+     *  @param user     Optional, username --not used--
+     *  @param pass     Optional, password --not used--
+     *  @return         nsapi_error_t
+     */
+    virtual nsapi_error_t connect(const char *apn,
+            const char *username = 0, const char *password = 0);
+ 
+    /** Connect to the network (no parameters)
+     *
+     *  @return         nsapi_error_t
+     */
+    virtual nsapi_error_t connect();
+
+    /** disconnect from the network
+     *
+     *  provided for completness, but function does nothing becase
+     *  WNC part can not disconnect from network once connected.
+     *
+     *  @return         nsapi_error_t
+     */
+    virtual nsapi_error_t disconnect();
+ 
+    /** Get the IP address of WNC device. From NetworkStack Class
+     *
+     *  @return         IP address string or null 
+     */
+    virtual const char *get_ip_address();
+ 
+    /** Get the network assigned IP address.
+     *
+     *  @return         IP address or null 
+     */
+    const char *get_my_ip_address();
+
+    /** Get the MAC address of the WNC device.  
+     *
+     *  @return         MAC address of the interface
+     */
+    virtual const char *get_mac_address();
+ 
+    /** Attach a callback function for when a SMS is recevied
+     *
+     *  @param          function pointer to call
+     */
+    void sms_attach(void (*callback)(IOTSMS *));
+
+    /** Set the level of Debug output
+     *
+     *  @param          bit field 
+     *         basic AT command info= 0x01
+     *         more AT command info = 0x02
+     *         mbed driver info     = 0x04
+     *         dump buffers         = 0x08
+     *         all debug            = 0x0f
+     */
+    void doDebug(int v);
+
+    /** Query registered state of WNC
+     *
+     *  @return         true if registerd, false if not 
+     */
+    bool registered();
+
+    /** Start the SMS monitoring service
+     */
+    void sms_start(void);
+
+    /** start listening for incomming SMS messages
+     *
+     *  @param time in msec to check
+     */
+    void sms_listen(uint16_t=1000);         // Configure device to listen for text messages 
+        
+    /** retrieve a SMS message
+     *
+     *  @param          pointer to an array of IOTSMS messages
+     */
+    int getSMS(IOTSMS **msg);
+
+    /** send a SMS message
+     *
+     *  @param          a string containing number to send message to
+     *  @param          a string containing message to send
+     *  @return         true on success, 0 on failure
+     */
+    int sendIOTSms(const string&, const string&);
+
+    /** return this devices SMS number
+     *
+     *  @brief The IOTSMS number used, isn't phone number, it is device ICCID.  
+     *
+     *  @return          this devices IOTSMS number
+     */
+    char* getSMSnbr();
+
+
+protected:
+
+    /** Get Host IP by name. 
+     *
+     *  @return         nsapi_error_t
+     */
+    virtual nsapi_error_t gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version);
+
+
+    /** return a pointer to the NetworkStack object
+     *
+     *  @return          The underlying NetworkStack object
+     */
+    virtual NetworkStack *get_stack();
+
+    /** Open a socket. 
+     *
+     *  @param handle       Handle in which to store new socket
+     *  @param proto        Type of socket to open, NSAPI_TCP or NSAPI_UDP
+     *  @return             0 on success, negative on failure
+     */
+    virtual int socket_open(void **handle, nsapi_protocol_t proto);
+ 
+    /** Close the socket. 
+     *
+     *  @param handle       Socket handle
+     *  @return             0 on success, negative on failure
+     */
+    virtual int socket_close(void *handle);
+ 
+    /** Bind a server socket to a specific port.
+     *
+     *  @brief              NOT SUPPORTED
+     *  @param handle       Socket handle
+     *  @param address      address to listen for incoming connections on 
+     *  @return             NSAPI_ERROR_UNSUPPORTED;
+     */
+    virtual int socket_bind(void *handle, const SocketAddress &address);
+ 
+    /** Start listening for incoming connections.
+     *
+     *  @brief              NOT SUPPORTED
+     *  @param handle       Socket handle
+     *  @param backlog      Number of pending connections that can be queued up at any
+     *                      one time [Default: 1]
+     *  @return             NSAPI_ERROR_UNSUPPORTED;
+     */
+    virtual int socket_listen(void *handle, int backlog);
+ 
+    /** Accept a new connection.
+     *
+     *  @brief              NOT SUPPORTED
+     *  @return             NSAPI_ERROR_UNSUPPORTED;
+     */
+    virtual int socket_accept(nsapi_socket_t server,
+            nsapi_socket_t *handle, SocketAddress *address=0);
+ 
+    /** Connects this socket to the server.
+     *
+     *  @param handle       Socket handle
+     *  @param address      SocketAddress 
+     *  @return             0 on success, negative on failure
+     */
+    virtual int socket_connect(void *handle, const SocketAddress &address);
+ 
+    /** Send data to the remote host.
+     *
+     *  @param handle       Socket handle
+     *  @param data         buffer to send
+     *  @param size         length of buffer
+     *  @return             Number of bytes written or negative on failure
+     *
+     *  @note This call is blocking. 
+     */
+    virtual int socket_send(void *handle, const void *data, unsigned size);
+ 
+    /** Receive data from the remote host.
+     *
+     *  @param handle       Socket handle
+     *  @param data         buffer to store the recived data
+     *  @param size         bytes to receive
+     *  @return             received bytes received, negative on failure
+     *
+     *  @note This call is not-blocking 
+     */
+    virtual int socket_recv(void *handle, void *data, unsigned size);
+ 
+    /** Send a packet to a remote endpoint.
+     *
+     *  @param handle       Socket handle
+     *  @param address      SocketAddress
+     *  @param data         data to send
+     *  @param size         number of bytes to send
+     *  @return the         number of bytes sent or negative on failure
+     *
+     *  @note This call is blocking.
+     */
+    virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size);
+ 
+    /** Receive packet remote endpoint
+     *
+     *  @param handle       Socket handle
+     *  @param address      SocketAddress 
+     *  @param buffer       buffer to store data to
+     *  @param size         number of bytes to receive
+     *  @return the         number bytes received or negative on failure
+     *
+     *  @note This call is not-blocking.
+     */
+    virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size);
+ 
+    /** Register a callback on state change of the socket
+     *
+     *  @param handle       Socket handle
+     *  @param callback     Function to call on state change
+     *  @param data         Argument to pass to callback
+     *
+     *  @note Callback may be called in an interrupt context.
+     */
+    virtual void socket_attach(void *handle, void (*callback)(void *), void *data);
+    
+    /** get the status of internal errors
+     *
+     *  @brief Called after any WNC14A2A operation to determine error specifics 
+     *  @param none.
+     */
+    uint16_t wnc14a2a_chk_error(void) { return _errors; }
+
+
+private:
+    //! WncController Class for managing the 14A2a hardware
+    friend class WncControllerK64F;  
+
+    bool     m_wncpoweredup;                //track if WNC has been power-up
+    unsigned m_debug;
+
+    WncIpStats myNetStats;                  //maintaint the network statistics
+    WncControllerK64F_fk::WncControllerK64F *_pwnc; //pointer to the WncController instance
+
+    int m_active_socket;                    // a 'pseudo' global to track the active socket
+    WNCDebug *_debugUart;                     // Serial object for parser to communicate with radio
+    char *_fatal_err_loc;                   // holds string containing location of fatal error
+    nsapi_error_t _errors;
+
+    bool m_smsmoning;                       // Track if the SMS monitoring thread is running
+    EventQueue sms_queue;                   // Queue used to schedule for SMS checks
+    EventQueue isr_queue;                   // Queue used to schedule for receiving data
+    void (*_sms_cb)(IOTSMS *);              // Callback when text message is received. User must define this as 
+                                            // a static function because I'm not handling an object offset
+    IOTSMS m_MsgText, m_MsgText_array[MAX_SMS_MSGS];       // Used to pass SMS message to the user
+    struct WncController::WncSmsList m_smsmsgs;            //use the WncSmsList structure to hold messages
+
+    void handle_sms_event();                // SMS tx/rx handler
+    void wnc_isr_event();                   // Simulated ISR
+    int  rx_event();                        // receive data handler
+    int  tx_event();                        // tx data handler
+
+    char _mac_address[NSAPI_MAC_SIZE];      // local Mac
+    void _dbOut(const char *format, ...);
+    void _dbDump_arry( const uint8_t* data, unsigned int size );
+
+    // Receive Interrupt simulation to enabled non-blocking operation
+    uint8_t *m_recv_dptr;
+    int      m_recv_wnc_state;
+    int      m_recv_events;
+    int      m_recv_socket;
+    int      m_recv_timer;
+    unsigned m_recv_orig_size;
+    uint32_t m_recv_req_size, m_recv_total_cnt;
+    uint32_t m_recv_return_cnt;
+    void    (*m_recv_callback)(void*);
+    void     *m_recv_cb_data;
+
+    // Transmit Interrupt simulation to enabled non-blocking operation
+    uint8_t *m_tx_dptr;
+    int      m_tx_wnc_state;
+    int      m_tx_socket;
+    unsigned m_tx_orig_size;
+    uint32_t m_tx_req_size, m_tx_total_sent;
+    void    (*m_tx_callback)(void*);
+    void     *m_tx_cb_data;
+
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WNCDebug.h	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,99 @@
+/**
+* copyright (c) 2017-2018, James Flynn
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/**
+*  @file WNCDebug.h
+*  @brief A debug class that coordinates the output of debug messages to 
+*  either stdio or a serial port based on instantiation.
+*
+*  @author James Flynn
+* 
+*  @date 1-Feb-2018
+*  
+*/
+ 
+#ifndef __WNCDEBUG__
+#define __WNCDEBUG__
+#include <stdio.h>
+#include "BufferedSerial.h"
+
+/** WNCDebug class
+* Used to write debug data to the user designated IO.  Currently
+* The class expects either a stdio element (defaults to stderr) or
+* a pointer to a BufferedSerial object.
+*/
+
+class WNCDebug
+{
+public:
+    //! Create class with either stdio or a pointer to a uart
+    WNCDebug( FILE * fd = stderr ): m_puart(NULL) {m_stdiofp=fd;};
+    WNCDebug( BufferedSerial * uart): m_stdiofp(NULL) {m_puart=uart;};
+    ~WNCDebug() {};
+
+    //! standard printf() functionallity
+    int printf( char * fmt, ...) {
+            char buffer[256];
+            int ret=0;
+            va_list args;
+            va_start (args, fmt);
+            vsnprintf(buffer, sizeof(buffer), fmt, args);
+            prt.lock();
+            if( m_stdiofp )
+                ret=fputs(buffer,m_stdiofp);
+            else
+                ret=m_puart->puts(buffer);
+            prt.unlock();
+            va_end (args);
+            return ret;
+            }
+
+    //! standard putc() functionallity
+    int putc( int c ) {
+            int ret=0;
+            prt.lock();
+            if( m_stdiofp )
+                ret=fputc(c, m_stdiofp);
+            else
+                ret=m_puart->putc(c);
+            prt.unlock();
+            return ret;
+            }
+
+    //! standard puts() functionallity
+    int puts( const char * str ) {
+            int ret=0;
+            prt.lock();
+            if( m_stdiofp )
+                ret=fputs(str,m_stdiofp);
+            else
+                ret=m_puart->puts(str);
+            prt.unlock();
+            return ret;
+            }
+
+private:
+    std::FILE *m_stdiofp;
+    BufferedSerial *m_puart;
+    Mutex prt;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WncControllerK64F/WncController/WncController.cpp	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,2146 @@
+/*
+    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
+    
+    
+    An Example of usage:
+    
+    WncControllerK64F mdm(&wncPinList, &mdmUart, &debugUart);
+
+    mdm.enableDebug(true, true);
+
+    if (false == mdm.powerWncOn("m2m.com.attz", 60)) {
+        while(1);
+    }
+
+    // ICCID and MSISDN
+    string iccid; string msisdn;
+    if (mdm.getICCID(&iccid) == true) {
+        if (mdm.convertICCIDtoMSISDN(iccid, &msisdn) == true) {
+           // Send an SMS message (must use 15-digit MISDN number!)
+           mdm.sendSMSText(msisdn.c_str(), "Hello from WNC Kit -> from WNC");
+        }
+    }
+
+    // Get an IP address setup for the socket #1 (0 indexed))
+    if (true == mdm.resolveUrl(0, "www.att.com"))
+    {
+        // Report server IP
+        if (true == mdm.getIpAddr(0, ipAddrStr)) {
+            debugUart.puts("Server IP: ");
+            debugUart.puts(ipAddrStr);
+            debugUart.puts("\r\n");
+        }
+
+        // Open Socket #1, TCP=true resolved IP on port 80:
+        if (true == mdm.openSocket(0, 80, true)) {
+            // Write some data
+            const uint8_t * dataStr = "GET /index.html HTTP/1.0\r\nFrom: someuser@someuser.com\r\nUser-Agent: HTTPTool/1.0\r\n\r\n";
+            if (true == mdm.write(0, dataStr, strlen((const char *)dataStr)))
+            {
+                const uint8_t * myBuf;
+                mdm.setReadRetries(0, 20);
+                uint32_t n = mdm.read(0, &myBuf);
+                if (n > 0)
+                    debugUart.printf("Read %d chars: %s\r\n", n, myBuf);
+                else
+                    debugUart.puts("No read data!\r\n");
+            }
+        }
+    }
+    
+*/
+
+
+#include <cstdlib>
+#include <cctype>
+#include <string.h>
+#include "WncController.h"
+
+namespace WncController_fk {
+
+/////////////////////////////////////////////////////
+// Static initializers
+/////////////////////////////////////////////////////
+WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS];
+const WncController::WncSocketInfo_s WncController::defaultSockStruct = { 0, 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);
+}
+
+const char * WncController::_to_hex_string(uint8_t value)
+{
+    static char str[3];   // room for 8-bit + null
+    itoa(value, str, 16);
+    return (str);
+}
+
+WncController::WncController(void)
+{
+    for(unsigned i=0; i<MAX_NUM_WNC_SOCKETS; i++)
+        m_sSock[i] = defaultSockStruct;
+}
+
+WncController::~WncController(void) {};
+
+void WncController::enableDebug(bool on, bool moreDebugOn)
+{
+    m_sDebugEnabled = on;
+    m_sMoreDebugEnabled = moreDebugOn;
+}
+
+WncController::WncState_e WncController::getWncStatus(void)
+{
+    return (m_sState);
+}
+
+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);
+}
+
+bool WncController::powerWncOn(const char * const apn, 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(apn);
+        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) || (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) {
+        at_send_wnc_cmd(cmd, &respStr, ms_timeout);
+        strncpy(resp, respStr->c_str(), sizeRespBuf);
+        if (respStr->size() > sizeRespBuf)
+            dbgPuts("sendCustomCmd truncated!");
+            
+        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);
+}
+
+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);
+}
+
+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);
+    }
+}
+
+void WncController::setWncCmdTimeout(uint16_t toMs)
+{
+    m_sCmdTimeoutMs = toMs;
+}
+
+bool WncController::openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec)
+{
+    if (resolveUrl(numSock, url) == true)
+        return (openSocket(numSock, port, tcp, timeOutSec));
+    
+    return (false);
+}
+
+bool WncController::openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec)
+{
+    if (setIpAddr(numSock, ipAddr) == true)
+        return (openSocket(numSock, port, tcp, timeOutSec));
+    
+    return (false);
+}
+ 
+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!");
+            if (true == at_sockclose_wnc(m_sSock[numSock].numWncSock))
+                m_sSock[numSock].open = false;
+            else
+                return (false);
+        }
+        
+        m_sSock[numSock].myPort = port;
+        m_sSock[numSock].isTcp = tcp;
+        m_sSock[numSock].timeOutSec = timeOutSec;
+        
+        int16_t numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), port, numSock, tcp, timeOutSec);
+        m_sSock[numSock].numWncSock = numWncSock;
+        if (numWncSock > 0 && numWncSock <= (uint16_t)MAX_NUM_WNC_SOCKETS)
+            m_sSock[numSock].open = true;
+        else {
+            m_sSock[numSock].open = false;
+            dbgPuts("Socket open fail!!!!");
+            
+            // If the modem is not responding don't bother it.
+            if (WNC_NO_RESPONSE != getWncStatus()) {
+                // 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(m_sSock[numSock].numWncSock);
+            }
+        }
+    }
+    else {
+        dbgPuts("Bad socket num or IP!");
+        return (false);
+    }
+
+    return (m_sSock[numSock].open);
+}
+
+bool WncController::sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp)
+{
+    bool result = true;
+    
+    AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, m_sSock[numSock].numWncSock, isTcp);
+    if (cmdRes != WNC_AT_CMD_OK) {
+        if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
+        {
+            // This may throw away any data that hasn't been written 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 uint8_t * 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);
+}
+
+size_t WncController::read(uint16_t numSock, const uint8_t ** readBuf)
+{
+    static string theBuf;
+    string readStr;
+    
+    theBuf.erase();  // Clean-up from last time
+
+    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;
+            do {
+                AtCmdErr_e cmdRes;
+                cmdRes = at_sockread_wnc(&readStr, m_sSock[numSock].numWncSock, 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 (readStr.size() > 0) {
+                        theBuf += readStr;
+                        foundData = true;
+                        i = 1;
+                    }
+                    else {
+                        // Once data is found start returning it asap
+                        if (foundData == false)
+                            waitMs(to);
+                    }
+                }
+                else {
+                    theBuf += readStr; // Append what if any we got before it errored.
+                    dbgPuts("Sockread failed!");
+                    if (WNC_NO_RESPONSE == getWncStatus()) {
+                        i = 0;
+                    }
+                    else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
+                    {
+                        // 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!");
+    }
+
+    *readBuf = (const uint8_t *)theBuf.c_str();
+
+    return (theBuf.size());
+}
+
+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;
+            uint16_t numRead;
+            do {
+                AtCmdErr_e cmdRes;
+                if (maxReadBufLen < MAX_WNC_READ_BYTES)
+                    cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp);
+                else
+                    cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, m_sSock[numSock].numWncSock, 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 (WNC_NO_RESPONSE == getWncStatus()) {
+                        i = 0;
+                    }
+                    else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME))
+                    {
+                        // 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) && (maxReadBufLen > 0));
+        }
+        else {
+            dbgPuts("Socket is closed for read");
+        }
+    }
+    else {
+        dbgPuts("Bad socket num!");
+    }
+
+    return (numCopied);
+}
+
+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!");
+}
+
+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!");
+}
+
+bool WncController::closeSocket(uint16_t numSock)
+{
+    if (numSock < MAX_NUM_WNC_SOCKETS) {
+
+        if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock))
+            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);
+}
+
+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) && (getTimerTicksB_mS() < timeout_ms)) {
+        if (charReady()) {
+            chin_last = chin;
+            chin = getc();
+            if (isprint(chin)) {
+                *buff += 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;
+            }
+        }
+    }
+    stopTimerB();
+    
+    if (len > MAX_LEN_WNC_CMD_RESPONSE)
+        dbgPuts("Max cmd length reply exceeded!");
+
+    return (len);
+}
+
+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);
+  }
+}
+
+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) {
+        // If a prior command timed out but a new one works then
+        //  change the state back to ON.  We don't know here in this 
+        //  method if the Cell Link is good so assume it is. When a command
+        //  that depends on the cell link is made it will update the state.
+        if (m_sState == WNC_NO_RESPONSE)
+            m_sState = WNC_ON;
+            
+        // 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 {
+        m_sState = WNC_NO_RESPONSE;
+        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");
+        if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) {
+            if (WNC_NO_RESPONSE == getWncStatus()) {
+                dbgPuts("No response for closeOpenSocket1");
+                return ;
+            }
+        }        
+
+        int numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec);
+        m_sSock[numSock].numWncSock = numWncSock;
+        if (numWncSock > 0 && numWncSock <= (int)MAX_NUM_WNC_SOCKETS)
+            m_sSock[numSock].open = true;
+        else {
+            m_sSock[numSock].open = false;
+            dbgPuts("Failed to re-open socket!");
+        }
+        
+        if (WNC_NO_RESPONSE == getWncStatus()) {
+            dbgPuts("No response for closeOpenSocket2");
+            return ;
+        }
+    } while (m_sSock[numSock].open == false);
+}
+
+bool WncController::getICCID(string * iccid)
+{
+    if (at_geticcid_wnc(iccid) == false) {
+        dbgPuts("getICCID error!");
+        return (false);
+    }
+    
+    return (true);
+}
+
+bool WncController::at_geticcid_wnc(string * iccid)
+{
+    string * respStr;
+    
+    iccid->erase();
+    
+    AtCmdErr_e r = at_send_wnc_cmd("AT%CCID", &respStr, m_sCmdTimeoutMs);
+
+    if (r != WNC_AT_CMD_OK || respStr->size() == 0)
+        return (false);
+
+    // New Firmware versions respond to the %CCID command with "%CCID:"
+    // but old version respond with "AT%CCID", so check to see which we have
+    size_t pos = respStr->find(":");
+    if (pos == string::npos) 
+        pos = respStr->find("AT%CCID");
+    else 
+        pos = respStr->find("%CCID");
+
+    if (pos == string::npos)
+        return (false);
+
+    pos += 7; // Advanced to the number
+
+    size_t posOK = respStr->rfind("OK");
+    if (posOK == string::npos)
+        return (false);
+
+    *iccid = respStr->substr(pos, posOK - pos);
+    
+    return (true);
+}
+
+bool WncController::convertICCIDtoMSISDN(const string & iccid, string * msisdn)
+{
+    msisdn->erase();
+    
+    if (iccid.size() != 20 && iccid.size() != 19) {
+        dbgPuts("Invalid ICCID length!");
+        return (false);
+    }
+ 
+    *msisdn = "882350";
+    
+    if (iccid.size() == 20)
+        *msisdn += iccid.substr(10,iccid.size() - 11);
+    else
+        *msisdn += iccid.substr(10,iccid.size() - 10);
+    
+    return (true);
+}
+
+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);
+    }
+}
+
+bool WncController::readSMSLog(struct WncSmsList * log)
+{
+    string * logStr;
+    uint16_t i;
+    
+    if (at_readSMSlog_wnc(&logStr) == false) {
+        dbgPuts("readSMSLog: Failed!");
+        return (false);
+    }
+    
+    // Clean slate    
+    log->msgCount = 0;
+
+    if (logStr->size() == 0)
+        return (false);
+
+    // Pick out the stuff from the string and convert to struct
+    string s;
+    size_t pos2;
+    size_t pos = logStr->find("+CMGL:");
+        
+    for(i=0; i<MAX_WNC_SMS_MSG_SLOTS; i++) {
+        // Start with a clean slate, let parsing fill out later.
+        log->e[i].unread = false;
+        log->e[i].incoming = false;
+        log->e[i].unsent = false;
+        log->e[i].pduMode = false;
+        log->e[i].msgReceipt = false;
+
+        log->e[i].idx = logStr->at(pos + 7);
+        if (pos == string::npos)
+            return (false);
+        pos2 = logStr->find(",\"", pos);
+        if (pos2 == string::npos) {
+            // If the WNC acts wrong and receives a PDU mode
+            //  SMS there will not be any quotes in the response,
+            //  just take the whole reply and make it the message body for
+            //  now, mark it as an unread message, set the pdu flag!
+            log->e[log->msgCount].unread = true;
+            log->e[log->msgCount].pduMode = true;
+            log->msgCount++;
+
+            pos2 = logStr->find("+CMGL", pos + 5);
+            if (pos2 == string::npos) {
+                pos2 = logStr->find("OK", pos + 5);
+                if (pos2 == string::npos) {
+                    dbgPuts("Strange SMS Log Ending!");
+                    return (false);
+                }
+                i = MAX_WNC_SMS_MSG_SLOTS;
+            }
+            log->e[log->msgCount].msg = logStr->substr(0, pos2 - pos);
+            pos = pos2;  // for loop starts off expecting pos to point to next log msg
+            continue;
+        }
+        pos += 2;  // Advance to the text we want
+        pos2 = logStr->find("\",", pos);
+        if ((pos2 == string::npos) || (pos >= pos2))
+            return (false);
+                    
+        // Setup attributes
+        s = logStr->substr(pos, pos2 - pos);
+        if (s.find("REC READ") != string::npos)
+            log->e[i].incoming = true;
+        if (s.find("REC UNREAD") != string::npos) {
+            log->e[i].unread = true;
+            log->e[i].incoming = true;
+        }
+        if (s.find("STO UNSENT") != string::npos)
+            log->e[i].unsent = true;
+        if (logStr->find(",,") == string::npos)
+            log->e[i].msgReceipt = true;
+            
+        // Tele number
+        pos2 = logStr->find(",\"", pos2);
+        if (pos2 == string::npos)
+            return (false);  
+        pos2 += 2;  // Advance to next field
+        pos = logStr->find("\",", pos2);
+        if ((pos == string::npos) || (pos2 > pos))
+            return (false);
+        if (pos == pos2)
+            log->e[i].number.erase();
+        else    
+            log->e[i].number = logStr->substr(pos2, pos - pos2);
+        
+        // Date
+        pos = logStr->find(",\"", pos);
+        if (pos == string::npos)
+            return (false);
+        pos += 2; // Beginning of date field
+        pos2 = logStr->find(",", pos); // End of timestamp field
+        if ((pos2 == string::npos) || (pos > pos2))
+            return (false);
+        if (pos == pos2)
+            log->e[i].date.erase();
+        else
+            log->e[i].date = logStr->substr(pos, pos2 - pos);
+
+        // Timestamp
+        pos = logStr->find("\",", pos2); // End of timestamp
+        if (pos == string::npos)
+            return (false);
+        pos2 += 1; // Beginning of time field
+        if (pos < pos2)
+            return (false);
+        if (pos == pos2)
+            log->e[i].time.erase();
+        else
+            log->e[i].time = logStr->substr(pos2, pos - pos2);
+        
+        // Message field
+        
+        // We don't know how many messages we have so the next search
+        // could end with +CMGL or OK.
+        pos += 2;  // Advanced to message text
+        pos2 = logStr->find("+CMGL", pos);
+        if (pos2 == string::npos) {
+            pos2 = logStr->find("OK", pos);
+            if (pos2 == string::npos) {
+                dbgPuts("Strange SMS Log Ending!");
+                return (false);
+            }
+            i = MAX_WNC_SMS_MSG_SLOTS; // break
+        }
+        if (pos > pos2)
+            return (false);
+        if (pos == pos2)
+            log->e[log->msgCount].msg.erase();
+        else
+            log->e[log->msgCount].msg = logStr->substr(pos, pos2 - pos);
+
+        log->msgCount++;  // Message complete
+    }    
+    
+    return (true);
+}
+
+bool WncController::readUnreadSMSText(struct WncSmsList * w, bool deleteRead)
+{
+    struct WncController::WncSmsList tmp;
+    
+    if (readSMSLog(&tmp) == false)
+        return (false);
+    
+    w->msgCount = 0;
+    for(uint16_t i = 0; i < tmp.msgCount; i++) {
+        if (tmp.e[i].unread == true) {
+            w->e[w->msgCount] = tmp.e[i];
+            w->msgCount++;
+            if (deleteRead == true) {
+                // Clean up message that was copied out and read
+                deleteSMSTextFromMem(w->e[i].idx);
+            }
+        }
+    }
+    
+    return (w->msgCount > 0);
+}
+
+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);
+}
+
+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;
+            size_t pe;
+            size_t ps = pRespStr->rfind("\"");
+            if (ps != string::npos) {
+                ps += 2;  // Skip the , after the "
+                pe = ps;
+
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    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;
+
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    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;
+
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    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;
+
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    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;
+
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    pe += 1;
+                pe = pRespStr->find(".", pe);
+                if (pe == string::npos)
+                    return (false);
+                else
+                    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); dbgPutsNoTime(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); dbgPutsNoTime(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;
+    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) {
+            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!
+                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);
+}
+
+bool WncController::at_readSMSlog_wnc(string ** log)
+{
+    return (at_send_wnc_cmd("AT+CMGL", log, m_sCmdTimeoutMs) == WNC_AT_CMD_OK);
+}
+
+size_t WncController::at_readSMStext_wnc(const char n, const char ** log)
+{
+    static string smsReadTxtStr;
+    string * pRespStr;
+    string cmdStr;
+        
+    smsReadTxtStr.erase();
+    cmdStr = "AT+CMGR";
+    cmdStr += '1';
+    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);
+  
+  // Dump the firmware revision on the debug log:
+  at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs);
+
+  // 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
+
+  // Setup 3 memory slots in the WNC SIM for SMS usage.
+  at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs);
+  at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs);
+
+  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);
+
+  // Disable unsolicited RRCSTATE responses. These are supposed to be off
+  // by default but have been found to be active.
+  // This problem introduced in: NQ_MPSS_IMA3_v10.58.174043 LTE-M firmware
+  cmdRes = at_send_wnc_cmd("AT%NOTIFYEV=\"ALL\",0", &pRespStr, m_sCmdTimeoutMs);
+  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);
+}
+
+int16_t WncController::at_sockopen_wnc(const char * const ip, 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 && pRespStr->size() > 0)
+    {
+        size_t pos1 = pRespStr->find("T:");
+        size_t pos2 = pRespStr->rfind("OK");
+        if ((pos1 != string::npos) && (pos2 != string::npos)) {
+            size_t numLen = pos2 - (pos1 + 2);
+            string sockStr = pRespStr->substr(pos1 + 2, numLen);
+            cmd_str = "AT@SOCKCONN=";
+            cmd_str += sockStr;
+            cmd_str += ",\"";
+            cmd_str += ip;
+            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 (strtol(sockStr.c_str(), NULL, 10));
+        }
+        else {
+            dbgPuts("Invalid sockcreat response!");
+            return (0);
+        }
+    }
+    else
+        return (0);
+}
+
+bool WncController::at_sockclose_wnc(uint16_t numSock)
+{
+    string * pRespStr;
+    string cmd_str("AT@SOCKCLOSE=");
+
+    cmd_str += _to_string(numSock);
+ 
+    // Don't check the cell status to close the socket
+    AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
+    
+    if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) {
+        for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) {
+            res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs);
+            if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK))
+                break;
+        }
+    }
+    
+    return (res == WNC_AT_CMD_OK); 
+}
+
+bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr)
+{
+    string * pRespStr;
+    string str(s);
+    AtCmdErr_e r;
+
+    ipStr->erase(); // Clear out string until resolved!
+    str = "AT@DNSRESVDON=\"" + str;
+    str += "\"";
+    r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS);
+    if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) {
+        size_t pos_start = pRespStr->find("ON:\""); 
+        size_t pos_end = pRespStr->find("\"", (pos_start + 4));
+        if ((pos_start !=  string::npos) && (pos_end != string::npos)) {
+            pos_start += 4;
+            pos_end -= 1;
+  
+            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 {
+            timeoutSecs--;
+            dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false);
+            dbgPutsNoTime(" ", false);
+            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);    
+        dbgPutsNoTime(""); // CR LF
+    }
+    
+    return (false);
+}
+
+WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp)
+{
+    AtCmdErr_e result;
+
+    if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) {
+        string * pRespStr;
+        const char * num2str;
+        string cmd_str;
+
+        if (isTcp == true)
+            cmd_str="AT@SOCKWRITE=";
+        else
+            cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND=";
+
+        cmd_str += _to_string(numSock);
+        cmd_str += ",";
+        cmd_str += _to_string(n);
+        cmd_str += ",\"";
+        while(n > 0) {
+            n--;
+            num2str = _to_hex_string(*s++);
+            // Always 2-digit ascii hex:
+            if (num2str[1] == '\0')
+                cmd_str += '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(string * pS, uint16_t numSock, bool isTcp)
+{
+    AtCmdErr_e result = WNC_AT_CMD_OK;
+
+    string * pRespStr;
+    string cmd_str;
+    size_t pos_start=0, pos_end=0;
+    int i;
+    
+    pS->erase();  // Start with a fresh string
+
+    if (isTcp == true)
+        cmd_str="AT@SOCKREAD=";
+    else
+        cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
+
+    cmd_str += _to_string(numSock);
+    cmd_str += ",";
+    cmd_str += _to_string(MAX_WNC_READ_BYTES);
+            
+    // 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) {
+        if (pRespStr->size() > 0) {
+            pos_start = pRespStr->find("\"");
+            pos_end   = pRespStr->rfind("\"");
+            // Make sure search finds what it's looking for!
+            if (pos_start != string::npos && pos_end != string::npos) {
+                pos_start++;
+                i = pos_end - pos_start;  // Num hex chars, 2 per byte
+            }
+            else
+                i = 0;
+        }
+        else
+            i = 0;
+            
+        if ((i < 0) || ((i % 2) == 1))
+            dbgPuts("Invalid READ string!");
+        
+        if (i > 2*MAX_WNC_READ_BYTES) {
+            i = 2*MAX_WNC_READ_BYTES;
+            dbgPuts("DANGER WNC read data does not match length!");
+        }
+            
+        // If data, convert the hex string into byte values
+        while (i > 0) {
+            i -= 2;
+            *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
+            pos_start += 2;
+        }
+    }
+
+    return (result);
+}
+
+WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_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=0, pos_end=0;
+        int i;
+
+        if (isTcp == true)
+            cmd_str="AT@SOCKREAD=";
+        else
+            cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV=";
+
+        cmd_str += _to_string(numSock);
+        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) {
+            if (pRespStr->size() > 0) {
+                pos_start = pRespStr->find("\"");
+                pos_end   = pRespStr->rfind("\"");
+                // Make sure search finds what it's looking for!
+                if (pos_start != string::npos && pos_end != string::npos) {
+                    pos_start++;
+                    i = pos_end - pos_start;  // Num hex chars, 2 per byte
+                }
+                else
+                    i = 0;
+            }
+            else
+                i = 0;
+                
+            if ((i < 0) || ((i % 2) == 1))
+                dbgPuts("Invalid READ string!");
+                
+            if (i > 2*n) {
+                // Bound the ill formated WNC read string!
+                i = 2*n;
+                dbgPuts("TRUNCATING read data!");
+            }
+
+            // If data, convert the hex string into byte values
+            i /= 2;
+            *numRead = i;
+            while (i > 0) {
+                i--;
+                *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16);
+                pos_start += 2;
+            }
+        }
+    }
+    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);
+//        sendCmd(cmd, n, 1000, crLf);  // 3rd arg is micro seconds between chars sent
+    }
+
+    startTimerA();
+    while (getTimerTicksA_mS() < timeout_ms) {
+        n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS());
+
+        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
+
+    // If no response, don't bother with more commands
+    if (cmdRes1 != WNC_AT_CMD_TIMEOUT)
+        cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs);     // Check if SIM locked
+    else {
+        if (m_sMoreDebugEnabled)
+            dbgPuts("------------ WNC No Response! --------->");
+
+        return (false);
+    }
+    
+    if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0))
+    {
+        if (m_sMoreDebugEnabled)
+        {
+            if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT))
+                dbgPuts("------------ WNC No Response! --------->");
+            else
+                dbgPuts("------------ WNC Cmd Error! ----------->");
+        }
+        
+        // If by a miracle it responds to the 2nd after the 1st, keep going
+        if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0))
+            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 || pRespStr->size() == 0)
+    {
+        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 ---------------->");
+    }
+    
+    // If we made it this far and the WNC did respond, keep the ON state
+    if (m_sState != WNC_NO_RESPONSE)
+        m_sState = WNC_ON;
+    
+    return (true);
+}
+
+int WncController::dbgPutsNoTime(const char * s, bool crlf)
+{
+    if (m_sDebugEnabled == true) {
+        int r = dbgWriteChars(s);
+        if (crlf == true)
+            return (dbgWriteChars("\r\n"));
+        else
+            return (r);
+    }
+    else
+        return 0;
+};
+
+int WncController::dbgPuts(const char * s, bool crlf)
+{
+    dbgPutsNoTime("[*] ", false);
+    dbgPutsNoTime(_to_string(getLogTimerTicks()), false);
+    dbgPutsNoTime(" ", false);
+
+    int r = dbgPutsNoTime(s, false);
+    
+    if (crlf == true)
+        return (dbgPutsNoTime("", true));
+    else
+        return (r);
+};
+    
+void WncController::sendCmd(const char * cmd, bool crLf)
+{
+    puts(cmd);
+    if (crLf == true)
+        puts("\r\n");
+}
+
+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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WncControllerK64F/WncController/WncController.h	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,626 @@
+/** 
+    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.h
+    @purpose       Controls WNC Cellular Modem
+    @version       1.0
+    @date          July 2016
+    @author        Fred Kellerman
+    
+    Notes: This code originates from the following mbed repository:
+    
+    https://developer.mbed.org/teams/Avnet/code/WncControllerLibrary/
+*/
+
+
+#ifndef __WNCCONTROLLER_H_
+#define __WNCCONTROLLER_H_
+
+#include <string>
+#include <stdint.h>
+
+namespace WncController_fk {
+
+using namespace std;
+
+/** @defgroup API The WncControllerLibrary API */
+/** @defgroup MISC Misc WncControllerLibrary functions */
+/** @defgroup INTERNALS WncControllerLibrary Internals */
+
+static const uint8_t  MAX_LEN_IP_STR = 16;         // Length includes room for the extra NULL
+
+/** \brief  Contains info fields for the WNC Internet Attributes */
+struct WncIpStats
+{
+    string wncMAC;
+    char ip[MAX_LEN_IP_STR];
+    char mask[MAX_LEN_IP_STR];
+    char gateway[MAX_LEN_IP_STR];
+    char dnsPrimary[MAX_LEN_IP_STR];
+    char dnsSecondary[MAX_LEN_IP_STR];
+};
+
+
+/**
+ * @author Fred Kellerman
+ * @see API 
+ *
+ * <b>WncController</b> This mbed C++ class is for controlling the WNC
+ *  Cellular modem via the serial AT command interface.  This was
+ *  developed with respect to version 1.3 of the WNC authored
+ *  unpublished spec.  This class is only designed to have 1 instantiation,
+ *  it is also not multi-thread safe.  There are no OS specific
+ *  entities being used, there are pure virtual methods that an 
+ *  inheriting class must fulfill.  That inheriting class will have
+ *  OS and platform specific entities.  See WncControllerK64F for an
+ *  example for the NXP K64F Freedom board.
+ */
+class WncController
+{
+public:
+
+    static const unsigned MAX_NUM_WNC_SOCKETS = 5;  // Max number of simultaneous sockets that the WNC supports
+    static const unsigned MAX_POWERUP_TIMEOUT = 60; // How long the powerUp method will try to turn on the WNC Shield
+                                                    //  (this is the default if the user does not over-ride on power-up
+
+    /** Tracks mode of the WNC Shield hardware */
+    enum WncState_e {
+        WNC_OFF = 0,
+        WNC_ON, // This is intended to mean all systems go, including cell link up but socket may not be open
+        WNC_ON_NO_CELL_LINK,
+        WNC_NO_RESPONSE
+    };
+
+    /**
+     *
+     * Constructor for WncController class, sets up internals.
+     * @ingroup API
+     * @return none.
+     */
+    WncController(void);
+    virtual ~WncController()=0;
+
+    /**
+     *
+     * Used internally but also make public for a user of the Class to 
+     * interrogate state as well.
+     * @ingroup API
+     * @return the current state of the Wnc hardware.
+     */
+    WncState_e getWncStatus(void);
+    
+    /**
+     *
+     * Allows a user to set the WNC modem to use the given Cellular APN 
+     * @ingroup API
+     * @param apnStr - a null terminated c-string
+     * @return true if the APN set was succesful, else false
+     */
+    bool setApnName(const char * const apnStr);
+
+    /**
+     *
+     * Queries the WNC modem for the current RX RSSI in units of coded dBm
+     * @ingroup API
+     * @return 0 – -113 dBm or less
+     *         1 – -111 dBm
+     *         2...30 – -109 dBm to –53 dBm
+     *        31 – -51 dBm or greater
+     *        99 – not known or not detectable
+     */
+    int16_t getDbmRssi(void);
+    
+    /**
+     *
+     * Queries the WNC modem for the current Bit Error Rate
+     * @ingroup API
+     * @return 0...7 – as RXQUAL values in the table in 3GPP TS 45.008
+     *            subclause 8.2.4
+     *         99 – not known or not detectable
+     */
+    int16_t get3gBer(void);
+
+    /**
+     *
+     * Powers up the WNC modem
+     * @ingroup API
+     * @param apn - the apn c-string to set the WNC modem to use
+     * @param powerUpTimeoutSecs - the amount of time to wait for the WNC modem to turn on
+     * @return true if powerup was a success, else false.
+     */
+    bool powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs = MAX_POWERUP_TIMEOUT);
+
+    /**
+     *
+     * Returns the NAT Self, gateway, masks and dns IP
+     * @ingroup API
+     * @param s - a pointer to a struct that will contain the IP info.
+     * @return true if success, else false.
+     */
+    bool getWncNetworkingStats(WncIpStats * s);
+
+    /**
+     *
+     * Takes a text URL and converts it internally to an IP address for the
+     * socket number given.
+     * @ingroup API
+     * @param numSock - The number of the socket to lookup the IP address for.
+     * @param url - a c-string text URL
+     * @return true if success, else false.
+     */
+    bool resolveUrl(uint16_t numSock, const char * url);
+
+    /**
+     *
+     * If you know the IP address you can set the socket up to use it rather
+     * than using a text URL.
+     * @ingroup API
+     * @param numSock - The number of the socket to use the IP address for.
+     * @param ipStr - a c-string text IP addrese like: 192.168.0.1
+     * @return true if success, else false.
+     */
+    bool setIpAddr(uint16_t numSock, const char * ipStr);
+
+    /**
+     *
+     * Opens a socket for the given number, port and IP protocol.  Before
+     * using open, you must use either resolveUrl() or setIpAddr().
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @param port - the IP port to open
+     * @param tcp - set true for TCP, false for UDP
+     * @param timeoutSec - the amount of time in seconds to wait for the open to complete
+     * @return true if success, else false.
+     */
+    bool openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec = 30);
+
+    /**
+     *
+     * Opens a socket for the given text URL, number, port and IP protocol.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @param url - a c-string text URL, the one to open a socket for.
+     * @param port - the IP port to open.
+     * @param tcp - set true for TCP, false for UDP.
+     * @param timeoutSec - the amount of time in seconds to wait for the open to complete.
+     * @return true if success, else false.
+     */
+    bool openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec = 30);
+
+    /**
+     *
+     * Opens a socket for the given text IP address, number, port and IP protocol.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @param ipAddr - a c-string text IP address like: "192.168.0.1".
+     * @param port - the IP port to open.
+     * @param tcp - set true for TCP, false for UDP.
+     * @param timeoutSec - the amount of time in seconds to wait for the open to complete.
+     * @return true if success, else false.
+     */
+    bool openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec = 30);
+
+
+    /**
+     *
+     * Write data bytes to a Socket, the Socket must already be open.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @parma s - an array of bytes to write to the socket.
+     * @param n - the number of bytes to write.
+     * @return true if success, else false.
+     */
+     bool write(uint16_t numSock, const uint8_t * s, uint32_t n);
+
+    /**
+     *
+     * Poll to read available data bytes from an already open Socket.  This method
+     * will retry reads to what setReadRetries() sets it to and the delay in between
+     * retries that is set with setReadRetryWait()
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @parma readBuf - a pointer to where read will put the data.
+     * @param maxReadBufLen - The number of bytes readBuf has room for.
+     * @return the number of bytes actually read into readBuf.  0 is a valid value if no data is available.
+     */
+    size_t read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen);
+    
+    /**
+     *
+     * Poll to read available data bytes from an already open Socket.  This method
+     * will retry reads to what setReadRetries() sets it to and the delay in between
+     * retries that is set with setReadRetryWait()
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @parma readBuf - a pointer to pointer that will be set to point to an internal byte buffer that contains any read data.
+     * @return the number of bytes actually read into the pointer that readBuf points to.  0 is a valid value if no data is available.
+     */
+    size_t read(uint16_t numSock, const uint8_t ** readBuf);
+
+    /**
+     *
+     * Set the number of retries that the read methods will use.  If a read returns 0 data this setting will have the read
+     * re-read to see if new data is available.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @parma retries - the number of retries to perform.
+     * @return none.
+     */
+    void setReadRetries(uint16_t numSock, uint16_t retries);
+
+    /**
+     *
+     * Set the time between retires that the read methods will use.  If a read returns 0 data this setting will have the read
+     * re-read and use this amount of delay in between the re-reads.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @parma waitMs - the amount of time in mS to wait between retries.
+     * @return none.
+     */
+    void setReadRetryWait(uint16_t numSock, uint16_t waitMs);
+
+    /**
+     *
+     * Closes an already open Socket.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @return true if success else false.
+     */
+    bool closeSocket(uint16_t numSock);
+
+    /**
+     *
+     * Sets the amount of time to wait between the raw AT commands that are sent to the WNC modem.
+     * Generally you don't want to use this but it is here just in case.
+     * @ingroup API
+     * @param toMs - num mS to wait between the AT cmds.
+     * @return none.
+     */
+    void setWncCmdTimeout(uint16_t toMs);
+
+    /**
+     *
+     * Gets the IP address of the given socket number.
+     * @ingroup API
+     * @param numSock - The number of the socket to open.
+     * @param myIpAddr - a c-string that contains the socket's IP address.
+     * @return true if success else false.
+     */
+    bool getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR]);
+    
+    /**
+     *
+     * Enables debug output from this class.
+     * @ingroup API
+     * @param on - true enables debug output, false disables
+     * @param moreDebugOn - true enables verbose debug, false truncates debug output.
+     * @return none.
+     */
+    void enableDebug(bool on, bool moreDebugOn);
+    
+    ///////////////////////////////////////////
+    //  SMS messaging
+    ///////////////////////////////////////////
+
+    static const uint16_t MAX_WNC_SMS_MSG_SLOTS = 3;   // How many SMS messages the WNC can store and receive at a time.
+    static const uint16_t MAX_WNC_SMS_LENGTH    = 160; // The maximum length of a 7-bit SMS message the WNC can send and receive.
+    
+    /** Struct for SMS messages */
+    struct WncSmsInfo
+    {
+        // Content
+        char   idx;
+        string number;
+        string date;
+        string time;
+        string msg;
+        
+        // Attributes
+        bool incoming;
+        bool unsent;
+        bool unread;
+        bool pduMode;
+        bool msgReceipt;
+    };
+
+    /** Struct to contain a list of SMS message structs */
+    struct WncSmsList
+    {
+        uint8_t msgCount;
+        WncSmsInfo e[MAX_WNC_SMS_MSG_SLOTS];
+    };
+
+    /**
+     *
+     * Sends an SMS text message to someone.
+     * @ingroup API
+     * @param phoneNum - c-string 15 digit MSISDN number or ATT Jasper number (standard phone number not supported because ATT IoT SMS does not support it).
+     * @param text - the c-string text to send to someone.
+     * @return true if success else false.
+     */
+    bool sendSMSText(const char * const phoneNum, const char * const text);
+
+    /**
+     *
+     * Incoming messages are stored in a log in the WNC modem, this will read that
+     * log.
+     * @ingroup API
+     * @param log - the log contents if reading it was successful.
+     * @return true if success else false.
+     */
+    bool readSMSLog(struct WncSmsList * log);
+
+    /**
+     *
+     * Incoming messages are stored in a log in the WNC modem, this will read out
+     * messages that are unread and also then mark them read.
+     * @ingroup API
+     * @param w - a list of SMS messages that unread messages will be put into.
+     * @param deleteRead - if a message is read and this is set true the message will be deleted from the WNC modem log.
+     * If it is false the message will remain in the internal log but be marked as read.
+     * @return true if success else false.
+     */
+    bool readUnreadSMSText(struct WncSmsList * w, bool deleteRead = true);
+    
+    /**
+     *
+     * Saves a text message into internal SIM card memory of the WNC modem.
+     * There are only 3 slots available this is for unread, read and saved.
+     * @ingroup API
+     * @param phoneNum - c-string 15 digit MSISDN number or ATT Jasper number (standard phone number not supported because ATT IoT SMS does not support it).
+     * @param text - the c-string text to send to someone.
+     * @param msgIdx - the slot position to save the message: '1', '2', '3'
+     * @return true if success else false.
+     */
+    bool saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx);
+
+    /**
+     *
+     * Sends a prior stored a text message from internal SIM card memory of the WNC modem.
+     * If no messages are stored the behaviour of this method is undefined.
+     * @ingroup API
+     * @param msgIdx - the slot position to save the message: '1', '2', '3'
+     * @return true if success else false.
+     */
+    bool sendSMSTextFromMem(char msgIdx);
+
+    /**
+     *
+     * Deletes a prior stored a text message from internal SIM card memory of the WNC modem.
+     * If no messages are stored the behaviour of this method is undefined.
+     * @ingroup API
+     * @param msgIdx - the slot position to save the message: '1', '2', '3' or '*' deletes them all.
+     * @return true if success else false.
+     */
+    bool deleteSMSTextFromMem(char msgIdx);
+    
+    /**
+     *
+     * Retreives the SIM card ICCID number.
+     * @ingroup API
+     * @param iccid - a pointer to C++ string that contains the retrieved number.
+     * @return true if success else false.
+     */
+    bool getICCID(string * iccid);
+
+    /**
+     *
+     * Converts an ICCID number into a MSISDN number.  The ATT SMS system for IoT only allows use of the 15-digit MSISDN number.
+     * @ingroup API
+     * @param iccid - the number to convert.
+     * @param msisdn - points to a C++ string that has the converted number.
+     * @return true if success else false.
+     */
+    bool convertICCIDtoMSISDN(const string & iccid, string * msisdn);
+    
+    ///////////////////////////////////////////
+    // Neighborhood Cell Info
+    ///////////////////////////////////////////
+    
+    /**
+     *
+     * Fetches the signal quality log from the WNC modem.
+     * @ingroup API
+     * @param log - a pointer to an internal buffer who's contents contain the signal quality metrics.
+     * @return The number of chars in the log.
+     */
+    size_t getSignalQuality(const char ** log);
+    
+    /**  A struct for the WNC modem Date and Time */
+    struct WncDateTime
+    {
+        uint8_t  year;
+        uint8_t  month;
+        uint8_t  day;
+        uint8_t  hour;
+        uint8_t  min;
+        uint8_t  sec;
+    };
+
+    /**
+     *
+     * Fetches the cell tower's time and date.  The time is accurate when read
+     * but significant delays exist between the time it is read and returned.
+     * @ingroup API
+     * @param tod - User supplies a pointer to a tod struct and this method fills it in.
+     * @return true if success else false.
+     */
+    bool getTimeDate(struct WncDateTime * tod);
+    
+    /**
+     *
+     * ICMP Pings a URL, the results are only output to the debug log for now!
+     * @ingroup API
+     * @param url - a c-string whose URL is to be pinged.
+     * @return true if success else false.
+     */
+    bool pingUrl(const char * url);
+
+    /**
+     *
+     * ICMP Pings an IP, the results are only output to the debug log for now!
+     * @ingroup API
+     * @param ip - a c-string whose IP is to be pinged.
+     * @return true if success else false.
+     */
+    bool pingIp(const char * ip);
+    
+    /**
+     *
+     * Allows a user to send a raw AT command to the WNC modem.
+     * @ingroup API
+     * @param cmd - the c-string cmd to send like: "AT"
+     * @param resp - a pointer to the c-string cmd's response.
+     * @param sizeRespBuf - how large the command response buffer is, sets the max response length.
+     * @param ms_timeout - how long to wait for the WNC to respond to your command.
+     * @return the number of characters in the response from the WNC modem.
+     */
+    size_t sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout);
+
+protected:
+
+    // Debug output methods
+    int dbgPutsNoTime(const char * s, bool crlf = true);
+    int dbgPuts(const char * s, bool crlf = true);
+    const char * _to_string(int64_t value);
+    const char * _to_hex_string(uint8_t value);    
+
+    // Sends commands to WNC via
+    enum AtCmdErr_e {
+        WNC_AT_CMD_OK,
+        WNC_AT_CMD_ERR,
+        WNC_AT_CMD_ERREXT,
+        WNC_AT_CMD_ERRCME,
+        WNC_AT_CMD_INVALID_RESPONSE,
+        WNC_AT_CMD_TIMEOUT,
+        WNC_AT_CMD_NO_CELL_LINK,
+        WNC_AT_CMD_WNC_NOT_ON
+    };
+
+    bool waitForPowerOnModemToRespond(uint8_t powerUpTimeoutSecs);    
+    AtCmdErr_e sendWncCmd(const char * const s, string ** r, int ms_timeout);
+
+    // Users must define these functionalities in the inheriting class:
+        // General I/O and timing:
+    virtual int putc(char c)              = 0;
+    virtual int puts(const char * s)      = 0;
+    virtual char getc(void)               = 0;
+    virtual int charReady(void)           = 0;
+    virtual int dbgWriteChar(char b)      = 0;
+    virtual int dbgWriteChars(const char *b) = 0;
+    virtual void waitMs(int t)            = 0;
+    virtual void waitUs(int t)            = 0;
+    virtual bool initWncModem(uint8_t powerUpTimeoutSecs) = 0;
+    
+        // Isolate OS timers
+    virtual int  getLogTimerTicks(void)  = 0;
+    virtual void startTimerA(void)       = 0;
+    virtual void stopTimerA(void)        = 0;
+    virtual int  getTimerTicksA_mS(void) = 0;
+    virtual void startTimerB(void)       = 0;
+    virtual void stopTimerB(void)        = 0;
+    virtual int  getTimerTicksB_mS(void) = 0;
+    
+private:
+
+    bool softwareInitMdm(void);
+    bool checkCellLink(void);
+    AtCmdErr_e mdmSendAtCmdRsp(const char * cmd, int timeout_ms, string * rsp, bool crLf = true);
+    size_t mdmGetline(string * buff, int timeout_ms);
+    bool at_at_wnc(void);
+    bool at_init_wnc(bool hardReset = false);
+    int16_t at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec);
+    bool at_sockclose_wnc(uint16_t numSock);
+    bool at_dnsresolve_wnc(const char * s, string * ipStr);
+    AtCmdErr_e at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp);
+    AtCmdErr_e at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp);
+    AtCmdErr_e at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp);
+    bool at_reinitialize_mdm(void);
+    AtCmdErr_e at_send_wnc_cmd(const char * s, string ** r, int ms_timeout);
+    bool at_setapn_wnc(const char * const apnStr);
+    bool at_sendSMStext_wnc(const char * const phoneNum, const char * const text);
+    bool at_get_wnc_net_stats(WncIpStats * s);
+    bool at_readSMSlog_wnc(string ** log);
+    size_t at_readSMStext_wnc(const char ** log);
+    size_t at_readSMStext_wnc(const char n, const char ** log);
+    bool at_getrssiber_wnc(int16_t * dBm, int16_t * ber3g);
+    void closeOpenSocket(uint16_t numSock);
+    bool sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp);
+    bool at_sendSMStextMem_wnc(char n);
+    bool at_deleteSMSTextFromMem_wnc(char n);
+    bool at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx);
+    size_t at_getSignalQuality_wnc(const char ** log);
+    bool at_gettimedate_wnc(struct WncDateTime * tod);
+    bool at_ping_wnc(const char * ip);
+    bool at_geticcid_wnc(string * iccid);
+    
+    // Utility methods
+    void sendCmd(const char * cmd, bool crLf);
+    void sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf);    
+    inline void rx_char_wait(void) {
+        // waitUs(1000);
+    }
+    
+    // Important constants
+    static const uint16_t MAX_WNC_READ_BYTES        = 1500;                            // This bounds the largest amount of data that the WNC read from a socket will return
+    static const uint16_t MAX_WNC_WRITE_BYTES       = MAX_WNC_READ_BYTES;              // This is the largest amount of data that the WNC can write per sockwrite.
+    static const uint16_t MAX_LEN_WNC_CMD_RESPONSE  = (MAX_WNC_READ_BYTES * 2 + 100);  // Max number of text characters in a WNC AT response *2 because bytes are converted into 2 hex-digits +100 for other AT@ chars.
+    static const uint16_t WNC_AUTO_POLL_MS          = 250;   // Sets default (may be overriden with method) poll interval (currently not used, future possible feature.
+    static const uint16_t WNC_CMD_TIMEOUT_MS        = 40000; // Sets default (may be overriden) time that the software waits for an AT response from the WNC.
+    static const uint16_t WNC_QUICK_CMD_TIMEOUT_MS  = 2000;  // Used for simple commands that should immediately respond such as "AT", cmds that are quicker than WNC_CMD_TIMEOUT_MS.
+    static const uint16_t WNC_WAIT_FOR_AT_CMD_MS    = 0;     // Wait this much between multiple in a row AT commands to the WNC.
+    static const uint16_t WNC_SOFT_INIT_RETRY_COUNT = 10;    // How many times the WNC will be tried to revive if it stops responding.
+    static const uint16_t WNC_DNS_RESOLVE_WAIT_MS   = 60000; // How much time to wait for the WNC to respond to a DNS resolve/lookup.
+    static const uint16_t WNC_TRUNC_DEBUG_LENGTH    = 80;    // Always make this an even number, how many chars for the debug output before shortening the debug ouput, this is used when moreDebug = false. 
+    static const uint16_t WNC_APNSET_TIMEOUT_MS     = 60000; // How long to wait for the WNC to respond to setting the APN string.
+    static const uint16_t WNC_PING_CMD_TIMEOUT_MS   = 60000; // Amount of time to wait for the WNC to respond to AT@PINGREQ (with cmd default params for timeout, does not change WNC cmd's timeout) 
+    static const int      WNC_REINIT_MAX_TIME_MS    = 60000; // How long to wait for the WNC to reset after it was already up and running after power-up.
+    static const uint16_t WNC_SOCK_CLOSE_RETRY_CNT  = 3;     // How many times to try to close the socket if the WNC gives an error.
+    static const char * const INVALID_IP_STR;                // Just a string set to an IP address when DNS resolve fails.
+        
+    struct WncSocketInfo_s {
+        int16_t numWncSock;
+        bool open;
+        string myIpAddressStr;
+        uint16_t myPort;
+        uint8_t readRetries;
+        uint16_t readRetryWaitMs;
+        bool isTcp;
+        uint16_t timeOutSec;
+    };
+
+    static WncSocketInfo_s m_sSock[MAX_NUM_WNC_SOCKETS];
+    static const WncSocketInfo_s defaultSockStruct;
+    static WncState_e m_sState;
+    static uint16_t m_sCmdTimeoutMs;
+    static string m_sApnStr;
+    static string m_sWncStr;
+    static uint8_t m_sPowerUpTimeoutSecs;
+    static bool m_sDebugEnabled;
+    static bool m_sMoreDebugEnabled;
+    static bool m_sCheckNetStatus;
+    static bool m_sReadyForSMS;
+};
+
+};  // End namespace WncController_fk
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WncControllerK64F/WncControllerK64F.cpp	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,206 @@
+/*
+    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          WncControllerK64F.cpp
+    @purpose       Contains K64F and mbed specifics to control the WNC modem using the WncController base class.
+    @version       1.0
+    @date          July 2016
+    @author        Fred Kellerman
+*/
+
+#include "WncControllerK64F.h"
+
+using namespace WncControllerK64F_fk;
+
+WncControllerK64F::WncControllerK64F(struct WncGpioPinListK64F * pPins, BufferedSerial * wnc_uart, WNCDebug * debug_uart)
+{
+    m_logTimer.start(); // Start the log timer now!    
+    m_pDbgUart = debug_uart;
+    m_pWncUart = wnc_uart;
+    m_gpioPinList = *pPins;
+}
+
+bool WncControllerK64F::enterWncTerminalMode(BufferedSerial * pUart, bool echoOn)
+{
+    if (pUart == NULL)
+        return (false);  // Need a uart!
+        
+    string * resp;
+    AtCmdErr_e r = sendWncCmd("AT", &resp, 500);
+    if (r == WNC_AT_CMD_TIMEOUT)
+        return (false);
+    
+    pUart->puts("\r\nEntering WNC Terminal Mode - press <CTRL>-Q to exit!\r\n");
+    
+    while (1) {
+        if (pUart->readable()) {
+            char c = pUart->getc();
+            if (c == '\x11') {
+                pUart->puts("\r\nExiting WNC Terminal Mode!\r\n");
+                // Cleanup in case user doesn't finish command:
+                sendWncCmd("AT", &resp, 300);
+                // Above AT may fail but should get WNC back in sync
+                return (sendWncCmd("AT", &resp, 500) == WNC_AT_CMD_OK);
+            }
+            if (echoOn == true) {
+                pUart->putc(c);
+            }
+            m_pWncUart->putc(c);
+        }
+        if (m_pWncUart->readable())
+            pUart->putc(m_pWncUart->getc());
+    }
+}
+
+int WncControllerK64F::putc(char c)
+{
+    return (m_pWncUart->putc(c));
+}
+
+int WncControllerK64F::puts(const char * s)
+{
+    return (m_pWncUart->puts(s));
+}
+
+char WncControllerK64F::getc(void)
+{
+    return (m_pWncUart->getc());
+}
+
+int WncControllerK64F::charReady(void)
+{
+    return (m_pWncUart->readable());
+}
+
+int WncControllerK64F::dbgWriteChar(char b)
+{
+    if (m_pDbgUart != NULL)
+        return (m_pDbgUart->putc(b));
+    else
+        return (0);
+}
+
+int WncControllerK64F::dbgWriteChars(const char * b)
+{
+    if (m_pDbgUart != NULL)
+        return (m_pDbgUart->puts(b));
+    else
+        return (0);
+}
+
+bool WncControllerK64F::initWncModem(uint8_t powerUpTimeoutSecs)
+{
+    // Hard reset the modem (doesn't go through
+    // the signal level translator)
+    *m_gpioPinList.mdm_reset = 0;
+
+    // disable signal level translator (necessary
+    // for the modem to boot properly).  All signals
+    // except mdm_reset go through the level translator
+    // and have internal pull-up/down in the module. While
+    // the level translator is disabled, these pins will
+    // be in the correct state.
+    *m_gpioPinList.shield_3v3_1v8_sig_trans_ena = 0;
+
+    // While the level translator is disabled and ouptut pins
+    // are tristated, make sure the inputs are in the same state
+    // as the WNC Module pins so that when the level translator is
+    // enabled, there are no differences.
+    *m_gpioPinList.mdm_uart2_rx_boot_mode_sel = 1;   // UART2_RX should be high
+    *m_gpioPinList.mdm_power_on = 0;                 // powr_on should be low
+    *m_gpioPinList.mdm_wakeup_in = 1;                // wake-up should be high
+    *m_gpioPinList.mdm_uart1_cts = 0;                // indicate that it is ok to send
+
+    // Now, wait for the WNC Module to perform its initial boot correctly
+    waitMs(1000);
+
+    // The WNC module initializes comms at 115200 8N1 so set it up
+    m_pWncUart->baud(115200);
+
+    //Now, enable the level translator, the input pins should now be the
+    //same as how the M14A module is driving them with internal pull ups/downs.
+    //When enabled, there will be no changes in these 4 pins...
+    *m_gpioPinList.shield_3v3_1v8_sig_trans_ena = 1;
+    
+    bool res = waitForPowerOnModemToRespond(powerUpTimeoutSecs);
+    
+    // Toggle wakeup to prevent future dropped 'A' of "AT", this was
+    //  suggested by ATT.
+    if (res == true) {
+        dbgPuts("\r\nToggling Wakeup...");
+        waitMs(20);
+        *m_gpioPinList.mdm_wakeup_in = 0;
+        waitMs(2000);
+        *m_gpioPinList.mdm_wakeup_in = 1;
+        waitMs(20);
+        dbgPuts("Toggling complete.");
+    }
+
+    return (res);
+}
+
+void WncControllerK64F::waitMs(int t)
+{
+    wait_ms(t);
+}
+
+void WncControllerK64F::waitUs(int t)
+{
+    wait_ms(t);
+}
+
+int  WncControllerK64F::getLogTimerTicks(void)
+{
+    return (m_logTimer.read_us());
+}
+
+void WncControllerK64F::startTimerA(void)
+{
+    m_timerA.start();
+    m_timerA.reset();
+}
+
+void WncControllerK64F::stopTimerA(void)
+{
+    m_timerA.stop();
+}
+
+int  WncControllerK64F::getTimerTicksA_mS(void)
+{
+    return (m_timerA.read_ms());
+}
+
+void WncControllerK64F::startTimerB(void)
+{
+    m_timerB.start();
+    m_timerB.reset();
+}
+
+void WncControllerK64F::stopTimerB(void)
+{
+    m_timerB.stop();
+}
+
+int  WncControllerK64F::getTimerTicksB_mS(void)
+{
+    return (m_timerB.read_ms());
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WNC14A2AInterface/WncControllerK64F/WncControllerK64F.h	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,130 @@
+/*
+    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          WncControllerK64F.h
+    @purpose       Contains K64F and mbed specifics to control the WNC modem using the WncController base class.
+    @version       1.0
+    @date          July 2016
+    @author        Fred Kellerman
+*/
+
+#ifndef __WNCCONTROLLERK64F_H_
+#define __WNCCONTROLLERK64F_H_
+
+#include <string>
+#include <stdint.h>
+#include "mbed.h"
+#include "WNCDebug.h"
+#include "WncController.h"
+
+namespace WncControllerK64F_fk {
+
+using namespace WncController_fk;
+using namespace std;
+
+/** List of K64F pins that are used to control and setup the ATT IoT Kit WNC Shield */
+struct WncGpioPinListK64F {
+    /////////////////////////////////////////////////////
+    // NXP GPIO Pins that are used to initialize the WNC Shield
+    /////////////////////////////////////////////////////
+    DigitalOut * mdm_uart2_rx_boot_mode_sel;   // on powerup, 0 = boot mode, 1 = normal boot
+    DigitalOut * mdm_power_on;                 // 0 = turn modem on, 1 = turn modem off (should be held high for >5 seconds to cycle modem)
+    DigitalOut * mdm_wakeup_in;                // 0 = let modem sleep, 1 = keep modem awake -- Note: pulled high on shield
+    DigitalOut * mdm_reset;                    // active high
+    DigitalOut * shield_3v3_1v8_sig_trans_ena; // 0 = disabled (all signals high impedence, 1 = translation active
+    DigitalOut * mdm_uart1_cts;
+};    
+
+
+/**
+ * @author Fred Kellerman
+ * @see API 
+ *
+ * <b>WncControllerK64F</b> This mbed C++ class is for controlling the WNC
+ * Cellular modem from the NXP K64F Freedom board.  It uses the control code
+ * from it's base class WncController to handle the WNC Modem AT cmds.  This
+ * class fulfills various pure virtual methods of the base class.  The point of
+ * this class is to have the platform specific code in it thus isolating the
+ * control code logic from any particular platform or OS.
+ */
+class WncControllerK64F : public WncController
+{
+public:
+
+    /**
+     *
+     * Sets up the resources to control the WNC modem shield.
+     * @ingroup API
+     * @param pPins - pointer to a list of K64F pins that are used to setup and control the ATT IoT Kit's WNC Shield.
+     * @param wnc_uart - a pointer to the serial uart that is used to communicate with the WNC modem.
+     * @param debug_uart - a pointer to a serial uart for the debug output to go out of, if NULL debug will not be output.
+     */
+    WncControllerK64F(struct WncGpioPinListK64F * pPins, BufferedSerial * wnc_uart, WNCDebug * debug_uart = NULL);
+    
+    /**
+     *
+     *  Activates a mode where the user can send text to and from the K64F
+     *  debug Serial port directly to the WNC.  The mode is entered via this
+     *  call.  The mode is exited when the user types CTRL-Q.  While in this
+     *  mode all text to and from the WNC is consumed by the debug Serial port.
+     *  No other methods in the class will receive any of the WNC output.
+     * @ingroup API
+     * @param pUart - a pointer to a uart to use to collect the user input and put the output from the WNC.
+     * @param echoOn - set to true to echo what is input back to the output of pUart.
+     */
+    bool enterWncTerminalMode(BufferedSerial *pUart, bool echoOn);
+    
+private:
+
+    // Disallow copy
+    WncControllerK64F operator=(WncControllerK64F lhs);
+
+    // Users must define these functionalities:
+    virtual int putc(char c);
+    virtual int puts(const char * s);
+    virtual char getc(void);
+    virtual int charReady(void);
+    virtual int dbgWriteChar(char b);
+    virtual int dbgWriteChars(const char *b);
+    virtual bool initWncModem(uint8_t powerUpTimeoutSecs);
+    virtual void waitMs(int t);
+    virtual void waitUs(int t);
+    
+    virtual int  getLogTimerTicks(void);
+    virtual void startTimerA(void);
+    virtual void stopTimerA(void);
+    virtual int  getTimerTicksA_mS(void);
+    virtual void startTimerB(void);
+    virtual void stopTimerB(void);
+    virtual int  getTimerTicksB_mS(void);
+
+    WNCDebug * m_pDbgUart;
+    BufferedSerial * m_pWncUart;
+    WncGpioPinListK64F m_gpioPinList;
+    Timer m_logTimer;
+    Timer m_timerA;
+    Timer m_timerB;
+};
+
+};  // End namespace WncController_fk
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_lib.json	Tue Feb 06 16:10:48 2018 +0000
@@ -0,0 +1,12 @@
+{
+    "name": "wnc14a2a",
+    "config": {
+        "wnc_debug": {
+            "help" : "enable or disable WNC debug messages.",
+            "value": false
+        },
+        "wnc_debug_setting": {
+            "help" : "bit value 1 and/or 2 enable WncController debug output, bit value 4 enables mbed driver debug output.",
+            "value": "4"
+        }
+}