driver for the WNC M14A2A Cellular Data Module
Revision 0:6a2d96c2a520, committed 2018-02-06
- Comitter:
- JMF
- Date:
- Tue Feb 06 16:10:48 2018 +0000
- Commit message:
- Incorporating changes suggested by ARM
Changed in this revision
--- /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" + } +}