Generic Pelion Device Management example for various Advantech modules.

This example is known to work great on the following platforms:

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.

Revision:
0:43ff9e3bc244
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/network/COMPONENT_WIFI_IDW04A1/SPWFSAxx.cpp	Tue Mar 12 13:48:39 2019 +0800
@@ -0,0 +1,1287 @@
+/* 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;
+}