![](/media/cache/group/Advantech_logo.jpg.50x50_q85.jpg)
Generic Pelion Device Management example for various Advantech modules.
This example is known to work great on the following platforms:
- WISE-1530 WiFi Module using DB-1505 carrier board and external SD card reader.
Example Functionality
This example showcases the following device functionality:
- On timer button increment, simulate Pelion LWM2M button resource change
Use this example with Mbed CLI
1. Import the application into your desktop:
mbed import https://os.mbed.com/teams/Advantech/code/pelion-example-common cd pelion-example-common
2. Download your developer certificate from pelion portal
3. Compile the program
mbed compile -t <toolchain> -m <TARGET_BOARD>
(supported toolchains : GCC_ARM / ARM / IAR)
4. Copy the binary file pelion-example-common.bin to your mbed device.
drivers/network/COMPONENT_WIFI_IDW04A1/SPWFSAxx.cpp
- Committer:
- chuanga
- Date:
- 2019-03-12
- Revision:
- 0:43ff9e3bc244
File content as of revision 0:43ff9e3bc244:
/* SPWFSAxx Devices * Copyright (c) 2015 ARM Limited * * 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. */ #include "mbed_debug.h" #include "SpwfSAInterface.h" /* must be included first */ #include "SPWFSAxx.h" static const char out_delim[] = {SPWFSAxx::_cr_, '\0'}; SPWFSAxx::SPWFSAxx(PinName tx, PinName rx, PinName rts, PinName cts, SpwfSAInterface &ifce, bool debug, PinName wakeup, PinName reset) : _serial(tx, rx, SPWFXX_DEFAULT_BAUD_RATE), _parser(&_serial, out_delim), _wakeup(wakeup, 1), _reset(reset, 1), _rts(rts), _cts(cts), _timeout(SPWF_INIT_TIMEOUT), _dbg_on(debug), _pending_sockets_bitmap(0), _network_lost_flag(false), _associated_interface(ifce), _call_event_callback_blocked(0), _callback_func(), _packets(0), _packets_end(&_packets) { memset(_pending_pkt_sizes, 0, sizeof(_pending_pkt_sizes)); _serial.sigio(Callback<void()>(this, &SPWFSAxx::_event_handler)); _parser.debug_on(debug); _parser.set_timeout(_timeout); /* unlikely OOBs */ _parser.oob("+WIND:5:WiFi Hardware Failure", callback(this, &SPWFSAxx::_wifi_hwfault_handler)); _parser.oob("+WIND:33:WiFi Network Lost", callback(this, &SPWFSAxx::_network_lost_handler_th)); #if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 _parser.oob("+WIND:24:WiFi Up::", callback(this, &SPWFSAxx::_skip_oob)); #endif _parser.oob("+WIND:8:Hard Fault", callback(this, &SPWFSAxx::_hard_fault_handler)); /* most likely OOBs */ _parser.oob(SPWFXX_OOB_ERROR, callback(this, &SPWFSAxx::_error_handler)); _parser.oob("+WIND:58:Socket Closed", callback(this, &SPWFSAxx::_server_gone_handler)); _parser.oob("+WIND:55:Pending Data", callback(this, &SPWFSAxx::_packet_handler_th)); } bool SPWFSAxx::startup(int mode) { BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ /*Reset module*/ if(!hw_reset()) { debug_if(_dbg_on, "\r\nSPWF> HW reset failed\r\n"); return false; } /* factory reset */ if(!(_parser.send(SPWFXX_SEND_FWCFG) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error restore factory default settings\r\n"); return false; } /*switch off led*/ if(!(_parser.send("AT+S.SCFG=blink_led,0") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error stop blinking led (%d)\r\n", __LINE__); return false; } /*set local echo to 0*/ if(!(_parser.send(SPWFXX_SEND_DISABLE_LE) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error local echo set\r\n"); return false; } /*set the operational rates*/ if(!(_parser.send("AT+S.SCFG=wifi_opr_rate_mask,0x003FFFCF") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error setting ht_mode\r\n"); return false; } /*enable the 802.11n mode*/ if(!(_parser.send("AT+S.SCFG=wifi_ht_mode,1") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error setting operational rates\r\n"); return false; } /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ if(!(_parser.send("AT+S.SCFG=wifi_mode,%d", mode) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set idle (%d)\r\n", __LINE__); return false; } #if defined(MBED_MAJOR_VERSION) #if !DEVICE_SERIAL_FC || (MBED_VERSION < MBED_ENCODE_VERSION(5, 7, 0)) /*disable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); return false; } #else // DEVICE_SERIAL_FC && (MBED_VERSION >= MBED_ENCODE_VERSION(5, 7, 0)) if((_rts != NC) && (_cts != NC)) { /*enable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_ENABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error enabling HW flow control\r\n"); return false; } /*configure pins for HW flow control*/ _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); } else { /*disable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); return false; } } #endif // DEVICE_SERIAL_FC && (MBED_VERSION >= MBED_ENCODE_VERSION(5, 7, 0)) #else // !defined(MBED_MAJOR_VERSION) - Assuming `master` branch #if !DEVICE_SERIAL_FC /*disable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); return false; } #else // DEVICE_SERIAL_FC if((_rts != NC) && (_cts != NC)) { /*enable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_ENABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error enabling HW flow control\r\n"); return false; } /*configure pins for HW flow control*/ _serial.set_flow_control(SerialBase::RTSCTS, _rts, _cts); } else { /*disable HW flow control*/ if(!(_parser.send(SPWFXX_SEND_DISABLE_FC) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error disabling HW flow control\r\n"); return false; } } #endif // DEVICE_SERIAL_FC #endif // !defined(MBED_MAJOR_VERSION) /* Disable selected WINDs */ _winds_on(); /* sw reset */ if(!reset()) { debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); return false; } #ifndef NDEBUG if (!(_parser.send(SPWFXX_SEND_GET_CONS_STATE) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting console state\r\n"); return false; } if (!(_parser.send(SPWFXX_SEND_GET_CONS_SPEED) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting console speed\r\n"); return false; } if (!(_parser.send(SPWFXX_SEND_GET_HWFC_STATE) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting hwfc state\r\n"); return false; } #if (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) || defined(IDW01M1_FW_REL_35X) /* betzw: IDW01M1 FW versions <3.5 seem to have problems with the following two commands. * For the sake of simplicity, just excluding them for IDW01M1 in general. */ if (!(_parser.send(SPWFXX_SEND_GET_CONS_DELIM) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting console delimiter\r\n"); return false; } if (!(_parser.send(SPWFXX_SEND_GET_CONS_ERRS) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting console error setting\r\n"); return false; } #endif // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) || defined(IDW01M1_FW_REL_35X) if (!(_parser.send("AT+S.GCFG=sleep_enabled") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting sleep state enabled\r\n"); return false; } if (!(_parser.send("AT+S.GCFG=wifi_powersave") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting powersave mode\r\n"); return false; } if (!(_parser.send("AT+S.GCFG=standby_enabled") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error getting standby state enabled\r\n"); return false; } #endif return true; } bool SPWFSAxx::_wait_console_active(void) { int trials = 0; while(true) { if (_parser.recv("+WIND:0:Console active\n") && _recv_delim_lf()) { debug_if(_dbg_on, "AT^ +WIND:0:Console active\r\n"); return true; } if(++trials >= SPWFXX_MAX_TRIALS) { debug("\r\nSPWF> ERROR: Should never happen! (%s, %d)\r\n", __func__, __LINE__); empty_rx_buffer(); return false; } } } bool SPWFSAxx::_wait_wifi_hw_started(void) { int trials = 0; while(true) { if (_parser.recv("+WIND:32:WiFi Hardware Started\n") && _recv_delim_lf()) { debug_if(_dbg_on, "AT^ +WIND:32:WiFi Hardware Started\r\n"); return true; } if(++trials >= SPWFXX_MAX_TRIALS) { debug("\r\nSPWF> ERROR: Should never happen! (%s, %d)\r\n", __func__, __LINE__); empty_rx_buffer(); return false; } } } bool SPWFSAxx::hw_reset(void) { #if (MBED_CONF_IDW0XX1_EXPANSION_BOARD != IDW04A1) || !defined(IDW04A1_WIFI_HW_BUG_WA) // betzw: HW reset doesn't work as expected on unmodified X_NUCLEO_IDW04A1 expansion boards _reset.write(0); wait_ms(200); _reset.write(1); #else // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) && defined(IDW04A1_WIFI_HW_BUG_WA): substitute with SW reset _parser.send(SPWFXX_SEND_SW_RESET); #endif // (MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1) && defined(IDW04A1_WIFI_HW_BUG_WA) return _wait_console_active(); } bool SPWFSAxx::reset(void) { bool ret; /* save current setting in flash */ if(!(_parser.send(SPWFXX_SEND_SAVE_SETTINGS) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error saving configuration to flash (%s, %d)\r\n", __func__, __LINE__); return false; } if(!_parser.send(SPWFXX_SEND_SW_RESET)) return false; /* betzw - NOTE: "keep the current state and reset the device". We assume that the module informs us about the eventual closing of sockets via "WIND" asynchronous indications! So everything regarding the clean-up of these situations is handled there. */ /* waiting for HW to start */ ret = _wait_wifi_hw_started(); return ret; } /* Security Mode None = 0, WEP = 1, WPA_Personal = 2, */ bool SPWFSAxx::connect(const char *ap, const char *passPhrase, int securityMode) { int trials; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ //AT+S.SCFG=wifi_wpa_psk_text,%s if(!(_parser.send("AT+S.SCFG=wifi_wpa_psk_text,%s", passPhrase) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error pass set\r\n"); return false; } //AT+S.SSIDTXT=%s if(!(_parser.send("AT+S.SSIDTXT=%s", ap) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error ssid set\r\n"); return false; } //AT+S.SCFG=wifi_priv_mode,%d if(!(_parser.send("AT+S.SCFG=wifi_priv_mode,%d", securityMode) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error security mode set\r\n"); return false; } /*set STA mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ if(!(_parser.send("AT+S.SCFG=wifi_mode,1") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set 1 (STA)\r\n"); return false; } /* sw reset */ if(!reset()) { debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); return false; } trials = 0; while(true) { if(_parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf()) { if(strstr(_msg_buffer, ":24:") != NULL) { // WiFi Up debug_if(_dbg_on, "AT^ %s\n", _msg_buffer); if(strchr(_msg_buffer, '.') != NULL) { // IPv4 address break; } else { continue; } } if(strstr(_msg_buffer, ":40:") != NULL) { // Deauthentication debug_if(_dbg_on, "AT~ %s\n", _msg_buffer); if(++trials < SPWFXX_MAX_TRIALS) { // give it three trials continue; } disconnect(); empty_rx_buffer(); return false; } else { debug_if(_dbg_on, "AT] %s\n", _msg_buffer); } continue; } if(++trials >= SPWFXX_MAX_TRIALS) { debug("\r\nSPWF> ERROR: Should never happen! (%s, %d)\r\n", __func__, __LINE__); empty_rx_buffer(); return false; } } return true; } bool SPWFSAxx::disconnect(void) { BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ #if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 /*disable Wi-Fi device*/ if(!(_parser.send("AT+S.WIFI=0") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error disabling WiFi\r\n"); return false; } #endif // IDW04A1 /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ if(!(_parser.send("AT+S.SCFG=wifi_mode,0") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error WiFi mode set idle (%d)\r\n", __LINE__); return false; } #if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 /*enable Wi-Fi device*/ if(!(_parser.send("AT+S.WIFI=1") && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> error enabling WiFi\r\n"); return false; } #endif // IDW04A1 // reset module if(!reset()) { debug_if(_dbg_on, "\r\nSPWF> SW reset failed (%s, %d)\r\n", __func__, __LINE__); return false; } /* clean up state */ _associated_interface.inner_constructor(); _free_all_packets(); return true; } const char *SPWFSAxx::getIPAddress(void) { unsigned int n1, n2, n3, n4; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ if (!(_parser.send("AT+S.STS=ip_ipaddr") && _parser.recv(SPWFXX_RECV_IP_ADDR, &n1, &n2, &n3, &n4) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> get IP address error\r\n"); return NULL; } debug_if(_dbg_on, "AT^ ip_ipaddr = %u.%u.%u.%u\r\n", n1, n2, n3, n4); sprintf((char*)_ip_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); return _ip_buffer; } const char *SPWFSAxx::getGateway(void) { unsigned int n1, n2, n3, n4; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ if (!(_parser.send("AT+S.STS=ip_gw") && _parser.recv(SPWFXX_RECV_GATEWAY, &n1, &n2, &n3, &n4) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> get gateway error\r\n"); return NULL; } debug_if(_dbg_on, "AT^ ip_gw = %u.%u.%u.%u\r\n", n1, n2, n3, n4); sprintf((char*)_gateway_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); return _gateway_buffer; } const char *SPWFSAxx::getNetmask(void) { unsigned int n1, n2, n3, n4; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ if (!(_parser.send("AT+S.STS=ip_netmask") && _parser.recv(SPWFXX_RECV_NETMASK, &n1, &n2, &n3, &n4) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> get netmask error\r\n"); return NULL; } debug_if(_dbg_on, "AT^ ip_netmask = %u.%u.%u.%u\r\n", n1, n2, n3, n4); sprintf((char*)_netmask_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); return _netmask_buffer; } int8_t SPWFSAxx::getRssi(void) { int ret; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ if (!(_parser.send("AT+S.PEERS=0,rx_rssi") && _parser.recv(SPWFXX_RECV_RX_RSSI, &ret) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> get RX rssi error\r\n"); return 0; } return (int8_t)ret; } const char *SPWFSAxx::getMACAddress(void) { unsigned int n1, n2, n3, n4, n5, n6; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ if (!(_parser.send("AT+S.GCFG=nv_wifi_macaddr") && _parser.recv(SPWFXX_RECV_MAC_ADDR, &n1, &n2, &n3, &n4, &n5, &n6) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> get MAC address error\r\n"); return 0; } debug_if(_dbg_on, "AT^ nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\r\n", n1, n2, n3, n4, n5, n6); sprintf((char*)_mac_buffer,"%02X:%02X:%02X:%02X:%02X:%02X", n1, n2, n3, n4, n5, n6); return _mac_buffer; } bool SPWFSAxx::isConnected(void) { return _associated_interface._connected_to_network; } nsapi_size_or_error_t SPWFSAxx::send(int spwf_id, const void *data, uint32_t amount, int internal_id) { uint32_t sent = 0U, to_send; nsapi_size_or_error_t ret; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ _process_winds(); // perform async indication handling (to early detect eventually closed sockets) /* betzw - WORK AROUND module FW issues: split up big packages in smaller ones */ for(to_send = (amount > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : amount; sent < amount; to_send = ((amount - sent) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (amount - sent)) { { BlockExecuter bh_handler(Callback<void()>(this, &SPWFSAxx::_execute_bottom_halves)); // betzw - TODO: handle different errors more accurately! if (!_associated_interface._socket_is_still_connected(internal_id)) { debug_if(_dbg_on, "\r\nSPWF> Socket not connected anymore: sent=%u, to_send=%u! (%s, %d)\r\n", sent, to_send, __func__, __LINE__); break; } else if(!_parser.send("AT+S.SOCKW=%d,%d", spwf_id, (unsigned int)to_send)) { debug_if(_dbg_on, "\r\nSPWF> Sending command failed: sent=%u, to_send=%u! (%s, %d)\r\n", sent, to_send, __func__, __LINE__); break; } else if(_parser.write(((char*)data)+sent, (int)to_send) != (int)to_send) { debug_if(_dbg_on, "\r\nSPWF> Sending data failed: sent=%u, to_send=%u! (%s, %d)\r\n", sent, to_send, __func__, __LINE__); break; } else if(!_recv_ok()) { debug_if(_dbg_on, "\r\nSPWF> Sending did not receive OK: sent=%u, to_send=%u! (%s, %d)\r\n", sent, to_send, __func__, __LINE__); break; } } sent += to_send; } if(sent > 0) { // `sent == 0` indicates a potential error ret = sent; } else if(amount == 0) { ret = NSAPI_ERROR_OK; } else if(_associated_interface._socket_is_still_connected(internal_id)) { ret = NSAPI_ERROR_DEVICE_ERROR; } else { ret = NSAPI_ERROR_CONNECTION_LOST; } return ret; } int SPWFSAxx::_read_len(int spwf_id) { unsigned int amount; if (!(_parser.send("AT+S.SOCKQ=%d", spwf_id) && _parser.recv(SPWFXX_RECV_DATALEN, &amount) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed\r\n", __func__); return SPWFXX_ERR_LEN; } if(amount > 0) { debug_if(_dbg_on, "\r\nSPWF> %s():\t\t%d:%d\r\n", __func__, spwf_id, amount); } MBED_ASSERT(((int)amount) >= 0); return (int)amount; } #define SPWFXX_WINDS_OFF "0xFFFFFFFF" void SPWFSAxx::_winds_on(void) { MBED_ASSERT(_is_event_callback_blocked()); if(!(_parser.send(SPWFXX_SEND_WIND_OFF_HIGH SPWFXX_WINDS_HIGH_ON) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); } if(!(_parser.send(SPWFXX_SEND_WIND_OFF_MEDIUM SPWFXX_WINDS_MEDIUM_ON) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); } if(!(_parser.send(SPWFXX_SEND_WIND_OFF_LOW SPWFXX_WINDS_LOW_ON) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); } } /* Define beyond macro in case you want to report back failures in switching off WINDs to the caller */ // #define SPWFXX_SOWF /* Note: in case of error blocking has been (tried to be) lifted */ bool SPWFSAxx::_winds_off(void) { MBED_ASSERT(_is_event_callback_blocked()); if (!(_parser.send(SPWFXX_SEND_WIND_OFF_LOW SPWFXX_WINDS_OFF) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); #ifdef SPWFXX_SOWF // betzw: try to continue _winds_on(); return false; #endif } if (!(_parser.send(SPWFXX_SEND_WIND_OFF_MEDIUM SPWFXX_WINDS_OFF) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); #ifdef SPWFXX_SOWF // betzw: try to continue _winds_on(); return false; #endif } if (!(_parser.send(SPWFXX_SEND_WIND_OFF_HIGH SPWFXX_WINDS_OFF) && _recv_ok())) { debug_if(_dbg_on, "\r\nSPWF> %s failed at line #%d\r\n", __func__, __LINE__); #ifdef SPWFXX_SOWF // betzw: try to continue _winds_on(); return false; #endif } return true; } void SPWFSAxx::_execute_bottom_halves(void) { _network_lost_handler_bh(); _packet_handler_bh(); } void SPWFSAxx::_read_in_pending(void) { static int internal_id_cnt = 0; while(_is_data_pending()) { if(_associated_interface._socket_has_connected(internal_id_cnt)) { int spwf_id = _associated_interface._ids[internal_id_cnt].spwf_id; if(_is_data_pending(spwf_id)) { int amount; amount = _read_in_pkt(spwf_id, false); if(amount == SPWFXX_ERR_OOM) { /* consider only 'SPWFXX_ERR_OOM' as non recoverable */ return; } } if(!_is_data_pending(spwf_id)) { internal_id_cnt++; internal_id_cnt %= SPWFSA_SOCKET_COUNT; } } else { internal_id_cnt++; internal_id_cnt %= SPWFSA_SOCKET_COUNT; } } } /* Note: returns * 'SPWFXX_ERR_OK' in case of success * 'SPWFXX_ERR_OOM' in case of "out of memory" * 'SPWFXX_ERR_READ' in case of `_read_in()` error */ int SPWFSAxx::_read_in_packet(int spwf_id, uint32_t amount) { struct packet *packet = (struct packet*)malloc(sizeof(struct packet) + amount); if (!packet) { #ifndef NDEBUG error("\r\nSPWF> %s(%d): Out of memory!\r\n", __func__, __LINE__); #else // NDEBUG debug("\r\nSPWF> %s(%d): Out of memory!\r\n", __func__, __LINE__); #endif debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); return SPWFXX_ERR_OOM; /* out of memory: give up here! */ } /* init packet */ packet->id = spwf_id; packet->len = amount; packet->next = 0; /* read data in */ if(!(_read_in((char*)(packet + 1), spwf_id, amount) > 0)) { free(packet); debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); return SPWFXX_ERR_READ; } else { debug_if(_dbg_on, "\r\nSPWF> %s():\t%d:%d\r\n", __func__, spwf_id, amount); /* append to packet list */ *_packets_end = packet; _packets_end = &packet->next; /* force call of (external) callback */ _call_callback(); } return SPWFXX_ERR_OK; } void SPWFSAxx::_free_packets(int spwf_id) { // check if any packets are ready for `spwf_id` for(struct packet **p = &_packets; *p;) { if ((*p)->id == spwf_id) { struct packet *q = *p; if (_packets_end == &(*p)->next) { _packets_end = p; } *p = (*p)->next; free(q); } else { p = &(*p)->next; } } } void SPWFSAxx::_free_all_packets() { for (int spwf_id = 0; spwf_id < SPWFSA_SOCKET_COUNT; spwf_id++) { _free_packets(spwf_id); } } bool SPWFSAxx::close(int spwf_id) { bool ret = false; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */ MBED_ASSERT(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); // `spwf_id` is valid for(int retry_cnt = 0; retry_cnt < SPWFXX_MAX_TRIALS; retry_cnt++) { Timer timer; timer.start(); // Flush out pending data while(true) { int amount = _read_in_pkt(spwf_id, true); if(amount < 0) { // SPWFXX error /* empty RX buffer & try to close */ empty_rx_buffer(); break; } if(amount == 0) break; // no more data to be read /* Try to work around module API bug: * break out & try to close after 20 seconds */ if(timer.read() > 20) { break; } /* immediately free packet(s) (to avoid "out of memory") */ _free_packets(spwf_id); /* interleave bottom halves */ _execute_bottom_halves(); } // Close socket if (_parser.send("AT+S.SOCKC=%d", spwf_id) && _recv_ok()) { ret = true; break; // finish closing } else { // close failed debug_if(_dbg_on, "\r\nSPWF> %s failed (%d)\r\n", __func__, __LINE__); /* interleave bottom halves */ _execute_bottom_halves(); /* free packets */ _free_packets(spwf_id); } } /* anticipate bottom halves */ _execute_bottom_halves(); if(ret) { /* clear pending data flag (should be redundant) */ _clear_pending_data(spwf_id); /* free packets for this socket */ _free_packets(spwf_id); /* reset pending data sizes */ _reset_pending_pkt_sizes(spwf_id); } else { debug_if(_dbg_on, "\r\nSPWF> SPWFSAxx::close failed (%d)\r\n", __LINE__); int internal_id = _associated_interface.get_internal_id(spwf_id); if(!_associated_interface._socket_is_still_connected(internal_id)) { /* clear pending data flag (should be redundant) */ _clear_pending_data(spwf_id); /* free packets for this socket */ _free_packets(spwf_id); /* reset pending data sizes */ _reset_pending_pkt_sizes(spwf_id); ret = true; } } return ret; } /* * Buffered serial event handler * * Note: executed in IRQ context! * Note: do not call (external) callback in IRQ context while performing critical module operations */ void SPWFSAxx::_event_handler(void) { if(!_is_event_callback_blocked()) { _call_callback(); } } /* * Common error handler */ void SPWFSAxx::_error_handler(void) { if(_parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf()) { debug_if(_dbg_on, "AT^ ERROR:%s (%d)\r\n", _msg_buffer, __LINE__); } else { debug_if(_dbg_on, "\r\nSPWF> Unknown ERROR string in SPWFSAxx::_error_handler (%d)\r\n", __LINE__); } /* force call of (external) callback */ _call_callback(); } /* * Handling oob ("+WIND:33:WiFi Network Lost") */ void SPWFSAxx::_network_lost_handler_th(void) { #ifndef NDEBUG static unsigned int net_loss_cnt = 0; net_loss_cnt++; #endif _recv_delim_cr_lf(); debug_if(_dbg_on, "AT^ +WIND:33:WiFi Network Lost\r\n"); #ifndef NDEBUG debug_if(_dbg_on, "\r\nSPWF> Getting out of SPWFSAxx::_network_lost_handler_th: %d\r\n", net_loss_cnt); #else // NDEBUG debug_if(_dbg_on, "\r\nSPWF> Getting out of SPWFSAxx::_network_lost_handler_th: %d\r\n", __LINE__); #endif // NDEBUG /* set flag to signal network loss */ _network_lost_flag = true; /* force call of (external) callback */ _call_callback(); return; } /* betzw - WORK AROUND module FW issues: split up big packages in smaller ones */ void SPWFSAxx::_add_pending_packet_sz(int spwf_id, uint32_t size) { uint32_t to_add; uint32_t added = _get_cumulative_size(spwf_id); if(size <= added) { // might happen due to delayed WIND delivery debug_if(_dbg_on, "\r\nSPWF> WARNING: %s failed at line #%d\r\n", __func__, __LINE__); return; } for(to_add = ((size - added) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (size - added); added < size; to_add = ((size - added) > SPWFXX_SEND_RECV_PKTSIZE) ? SPWFXX_SEND_RECV_PKTSIZE : (size - added)) { _add_pending_pkt_size(spwf_id, added + to_add); added += to_add; } /* force call of (external) callback */ _call_callback(); /* set that data is pending */ _set_pending_data(spwf_id); } /* * Handling oob ("+WIND:55:Pending Data") */ void SPWFSAxx::_packet_handler_th(void) { int internal_id, spwf_id; int amount; /* parse out the socket id & amount */ if (!(_parser.recv(SPWFXX_RECV_PENDING_DATA, &spwf_id, &amount) && _recv_delim_lf())) { #ifndef NDEBUG error("\r\nSPWF> SPWFSAxx::%s failed!\r\n", __func__); #endif return; } debug_if(_dbg_on, "AT^ +WIND:55:Pending Data:%d:%d\r\n", spwf_id, amount); /* check for the module to report a valid id */ MBED_ASSERT(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); /* set that there is pending data for socket */ /* NOTE: it seems as if asynchronous indications might report not up-to-date data length values * therefore we just record the socket id without considering the `amount` of data reported! */ internal_id = _associated_interface.get_internal_id(spwf_id); if(internal_id != SPWFSA_SOCKET_COUNT) { debug_if(_dbg_on, "AT^ +WIND:55:Pending Data:%d:%d - #2\r\n", spwf_id, amount); _add_pending_packet_sz(spwf_id, amount); MBED_ASSERT(_get_pending_pkt_size(spwf_id) != 0); } else { debug_if(_dbg_on, "\r\nSPWFSAxx::%s got invalid id %d\r\n", __func__, spwf_id); } } void SPWFSAxx::_network_lost_handler_bh(void) { if(!_network_lost_flag) return; _network_lost_flag = false; { bool were_connected; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* do not call (external) callback in IRQ context as long as network is lost */ Timer timer; timer.start(); _parser.set_timeout(SPWF_NETLOST_TIMEOUT); were_connected = isConnected(); _associated_interface._connected_to_network = false; if(were_connected) { unsigned int n1, n2, n3, n4; while(true) { if (timer.read_ms() > SPWF_CONNECT_TIMEOUT) { debug_if(_dbg_on, "\r\nSPWF> SPWFSAxx::_network_lost_handler_bh() #%d\r\n", __LINE__); disconnect(); empty_rx_buffer(); goto nlh_get_out; } if((_parser.recv(SPWFXX_RECV_WIFI_UP, &n1, &n2, &n3, &n4)) && _recv_delim_lf()) { debug_if(_dbg_on, "\r\nSPWF> Re-connected (%u.%u.%u.%u)!\r\n", n1, n2, n3, n4); _associated_interface._connected_to_network = true; goto nlh_get_out; } } } else { debug_if(_dbg_on, "\r\nSPWF> Leaving SPWFSAxx::_network_lost_handler_bh\r\n"); goto nlh_get_out; } nlh_get_out: debug_if(_dbg_on, "\r\nSPWF> Getting out of SPWFSAxx::_network_lost_handler_bh\r\n"); _parser.set_timeout(_timeout); /* force call of (external) callback */ _call_callback(); return; } } void SPWFSAxx::_recover_from_hard_faults(void) { disconnect(); empty_rx_buffer(); /* force call of (external) callback */ _call_callback(); } /* * Handling oob ("+WIND:8:Hard Fault") */ void SPWFSAxx::_hard_fault_handler(void) { _parser.set_timeout(SPWF_RECV_TIMEOUT); if(_parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf()) { #ifndef NDEBUG error("\r\nSPWF> hard fault error:\r\n%s\r\n", _msg_buffer); #else // NDEBUG debug("\r\nSPWF> hard fault error:\r\n%s\r\n", _msg_buffer); #endif // NDEBUG } else { #ifndef NDEBUG error("\r\nSPWF> unknown hard fault error\r\n"); #else // NDEBUG debug("\r\nSPWF> unknown hard fault error\r\n"); #endif // NDEBUG } // This is most likely the best we can do to recover from this module hard fault _parser.set_timeout(SPWF_HF_TIMEOUT); _recover_from_hard_faults(); _parser.set_timeout(_timeout); /* force call of (external) callback */ _call_callback(); } /* * Handling oob ("+WIND:5:WiFi Hardware Failure") */ void SPWFSAxx::_wifi_hwfault_handler(void) { unsigned int failure_nr; /* parse out the socket id & amount */ _parser.recv(":%u\n", &failure_nr); _recv_delim_lf(); #ifndef NDEBUG error("\r\nSPWF> WiFi HW fault error: %u\r\n", failure_nr); #else // NDEBUG debug("\r\nSPWF> WiFi HW fault error: %u\r\n", failure_nr); // This is most likely the best we can do to recover from this module hard fault _parser.set_timeout(SPWF_HF_TIMEOUT); _recover_from_hard_faults(); _parser.set_timeout(_timeout); #endif // NDEBUG /* force call of (external) callback */ _call_callback(); } /* * Handling oob ("+WIND:58:Socket Closed") * when server closes a client connection * * NOTE: When a socket client receives an indication about socket server gone (only for TCP sockets, WIND:58), * the socket connection is NOT automatically closed! */ void SPWFSAxx::_server_gone_handler(void) { int spwf_id, internal_id; if(!(_parser.recv(SPWFXX_RECV_SOCKET_CLOSED, &spwf_id) && _recv_delim_lf())) { #ifndef NDEBUG error("\r\nSPWF> SPWFSAxx::%s failed!\r\n", __func__); #endif goto _get_out; } debug_if(_dbg_on, "AT^ +WIND:58:Socket Closed:%d\r\n", spwf_id); /* check for the module to report a valid id */ MBED_ASSERT(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)); /* only set `server_gone` * user still can receive data & must still explicitly close the socket */ internal_id = _associated_interface.get_internal_id(spwf_id); if(internal_id != SPWFSA_SOCKET_COUNT) { _associated_interface._ids[internal_id].server_gone = true; } _get_out: /* force call of (external) callback */ _call_callback(); } #if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1 /* * Handling oob (currently only for "+WIND:24:WiFi Up::") */ void SPWFSAxx::_skip_oob(void) { if(_parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf()) { debug_if(_dbg_on, "AT^ +WIND:24:WiFi Up::%s\r\n", _msg_buffer); } else { debug_if(_dbg_on, "\r\nSPWF> Invalid string in SPWFSAxx::_skip_oob (%d)\r\n", __LINE__); } } #endif void SPWFSAxx::setTimeout(uint32_t timeout_ms) { _timeout = timeout_ms; _parser.set_timeout(timeout_ms); } void SPWFSAxx::attach(Callback<void()> func) { _callback_func = func; /* do not call (external) callback in IRQ context during critical module operations */ } /** * Recv Function */ int32_t SPWFSAxx::recv(int spwf_id, void *data, uint32_t amount, bool datagram) { BlockExecuter bh_handler(Callback<void()>(this, &SPWFSAxx::_execute_bottom_halves)); while (true) { /* check if any packets are ready for us */ for (struct packet **p = &_packets; *p; p = &(*p)->next) { if ((*p)->id == spwf_id) { debug_if(_dbg_on, "\r\nSPWF> Read done on ID %d and length of packet is %d\r\n",spwf_id,(*p)->len); struct packet *q = *p; MBED_ASSERT(q->len > 0); if(datagram) { // UDP => always remove pkt size // will always consume a whole pending size uint32_t ret; debug_if(_dbg_on, "\r\nSPWF> %s():\t\t\t%d:%d (datagram)\r\n", __func__, spwf_id, q->len); ret = (amount < q->len) ? amount : q->len; memcpy(data, q+1, ret); if (_packets_end == &(*p)->next) { _packets_end = p; } *p = (*p)->next; free(q); return ret; } else { // TCP if (q->len <= amount) { // return and remove full packet memcpy(data, q+1, q->len); if (_packets_end == &(*p)->next) { _packets_end = p; } *p = (*p)->next; uint32_t len = q->len; free(q); return len; } else { // `q->len > amount`, return only partial packet if(amount > 0) { memcpy(data, q+1, amount); q->len -= amount; memmove(q+1, (uint8_t*)(q+1) + amount, q->len); } return amount; } } } } /* check for pending data on module */ { int len; len = _read_in_pkt(spwf_id, false); if(len <= 0) { /* SPWFXX error or no more data to be read */ return -1; } } } } void SPWFSAxx::_process_winds(void) { do { if(_parser.process_oob()) { /* nothing else to do! */; } else { debug_if(_dbg_on, "%s():\t\tNo (more) oob's found!\r\n", __func__); return; // no (more) oob's found } } while(true); } /* Note: returns * '>=0' in case of success, amount of read in data (in bytes) * 'SPWFXX_ERR_OOM' in case of "out of memory" * 'SPWFXX_ERR_READ' in case of other `_read_in_packet()` error * 'SPWFXX_ERR_LEN' in case of `_read_len()` error */ int SPWFSAxx::_read_in_pkt(int spwf_id, bool close) { int pending; uint32_t wind_pending; BlockExecuter netsock_wa_obj(Callback<void()>(this, &SPWFSAxx::_unblock_event_callback), Callback<void()>(this, &SPWFSAxx::_block_event_callback)); /* do not call (external) callback in IRQ context while receiving */ _process_winds(); // perform async indication handling if(close) { // read in all data wind_pending = pending = _read_len(spwf_id); // triggers also async indication handling! if(pending > 0) { /* reset pending data sizes */ _reset_pending_pkt_sizes(spwf_id); /* create new entry for pending size */ _add_pending_pkt_size(spwf_id, (uint32_t)pending); #ifndef NDEBUG wind_pending = _get_pending_pkt_size(spwf_id); MBED_ASSERT(pending == (int)wind_pending); #endif } else if(pending < 0) { debug_if(_dbg_on, "\r\nSPWF> %s(), #%d:`_read_len()` failed (%d)!\r\n", __func__, __LINE__, pending); } } else { // only read in already notified data pending = wind_pending = _get_pending_pkt_size(spwf_id); if(pending == 0) { // special handling for no packets pending (to WORK AROUND missing WINDs)! pending = _read_len(spwf_id); // triggers also async indication handling! if(pending > 0) { _process_winds(); // perform async indication handling (again) wind_pending = _get_pending_pkt_size(spwf_id); if(wind_pending == 0) { /* betzw - WORK AROUND module FW issues: create new entry for pending size */ debug_if(_dbg_on, "%s():\t\tAdd packet w/o WIND (%d)!\r\n", __func__, pending); _add_pending_packet_sz(spwf_id, (uint32_t)pending); pending = wind_pending = _get_pending_pkt_size(spwf_id); MBED_ASSERT(wind_pending > 0); } } else if(pending < 0) { debug_if(_dbg_on, "\r\nSPWF> %s(), #%d:`_read_len()` failed (%d)!\r\n", __func__, __LINE__, pending); } } } if((pending > 0) && (wind_pending > 0)) { int ret = _read_in_packet(spwf_id, wind_pending); if(ret < 0) { /* "out of memory" or `_read_in_packet()` error */ /* we do not know if data is still pending at this point but leaving the pending data bit set might lead to an endless loop */ _clear_pending_data(spwf_id); /* also reset pending data sizes */ _reset_pending_pkt_sizes(spwf_id); return ret; } if((_get_cumulative_size(spwf_id) == 0) && (pending <= (int)wind_pending)) { _clear_pending_data(spwf_id); } } else if(pending < 0) { /* 'SPWFXX_ERR_LEN' error */ MBED_ASSERT(pending == SPWFXX_ERR_LEN); /* we do not know if data is still pending at this point but leaving the pending data bit set might lead to an endless loop */ _clear_pending_data(spwf_id); /* also reset pending data sizes */ _reset_pending_pkt_sizes(spwf_id); return pending; } else if(pending == 0) { MBED_ASSERT(wind_pending == 0); _clear_pending_data(spwf_id); } else if(wind_pending == 0) { // `pending > 0` /* betzw: should never happen! */ MBED_ASSERT(false); } return (int)wind_pending; }