test

Dependents:   mbed-os-example-blinky-1stDevDemo

Files at this revision

API Documentation at this revision

Comitter:
karen801
Date:
Wed May 23 14:37:10 2018 +0000
Commit message:
Initial commit

Changed in this revision

BlockExecuter.h Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
SPWFSA01/SPWFSA01.cpp Show annotated file Show diff for this revision Revisions of this file
SPWFSA01/SPWFSA01.h Show annotated file Show diff for this revision Revisions of this file
SPWFSA01/spwfsa01_at_strings.h Show annotated file Show diff for this revision Revisions of this file
SPWFSA04/SPWFSA04.cpp Show annotated file Show diff for this revision Revisions of this file
SPWFSA04/SPWFSA04.h Show annotated file Show diff for this revision Revisions of this file
SPWFSA04/spwfsa04_at_strings.h Show annotated file Show diff for this revision Revisions of this file
SPWFSAxx.cpp Show annotated file Show diff for this revision Revisions of this file
SPWFSAxx.h Show annotated file Show diff for this revision Revisions of this file
SpwfSAInterface.cpp Show annotated file Show diff for this revision Revisions of this file
SpwfSAInterface.h Show annotated file Show diff for this revision Revisions of this file
mbed_app_idw01m1.json Show annotated file Show diff for this revision Revisions of this file
mbed_app_idw04a1.json Show annotated file Show diff for this revision Revisions of this file
mbed_lib.json Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 79ce2b184a43 BlockExecuter.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BlockExecuter.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,22 @@
+#ifndef BLOCK_EXEC_H
+#define BLOCK_EXEC_H
+
+#include "mbed.h"
+
+/* Helper class to execute something whenever entering/leaving a basic block */
+class BlockExecuter {
+public:
+    BlockExecuter(Callback<void()> exit_cb, Callback<void()> enter_cb = Callback<void()>()) :
+        _exit_cb(exit_cb) {
+        if((bool)enter_cb) enter_cb();
+    }
+
+    ~BlockExecuter(void) {
+        _exit_cb();
+    }
+
+private:
+    Callback<void()> _exit_cb;
+};
+
+#endif  //BLOCK_EXEC_H
diff -r 000000000000 -r 79ce2b184a43 README.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,61 @@
+# Prototype Driver for STM Wi-Fi Expansion Boards based on the SPWFxx Module for STM32 Nucleo #
+
+## Currently supported expansion boards
+ * [X-NUCLEO-IDW01M1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw01m1.html), by setting `mbed` configuration variable `idw0xx1.expansion-board` to value `IDW01M1`
+ * [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html), by setting `mbed` configuration variable `idw0xx1.expansion-board` to value `IDW04A1`. You might also need to define macro `IDW04A1_WIFI_HW_BUG_WA` _(see beyond)_.
+
+## Configuration examples
+
+### Generic concepts
+
+For the ones, which might be less familiar with the **"The mbed configuration system"** in general, here is a [link](https://docs.mbed.com/docs/mbed-os-handbook/en/latest/advanced/config_system/) which points to the latest version of the respective _mbed OS 5 handbook tutorial_.
+
+Furthermore, with respect to this driver, pls. refer to files [`mbed_app_idw01m1.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_app_idw01m1.json) and [`mbed_app_idw04a1.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_app_idw04a1.json) regarding additional reference for what is explained beyond.
+
+### IDW01M1
+
+Add the following lines to the `target_overrides`-section of your `mbed_app.json` file.
+
+``` json
+            "idw0xx1.expansion-board": "IDW01M1",
+            "drivers.uart-serial-txbuf-size": 512,
+            "drivers.uart-serial-rxbuf-size": 512
+```
+
+`IDW01M1` is the default value in the [`mbed_lib.json`](https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/blob/master/mbed_lib.json) file, so setting the expansion board is not mandatory for `IDW01M1`, while setting the TX & RX buffer sizes is highly recommended.
+
+### IDW04A1
+
+Add the following lines to the `target_overrides`-section of your `mbed_app.json` file.
+
+``` json
+            "idw0xx1.expansion-board": "IDW04A1",
+            "drivers.uart-serial-txbuf-size": 512,
+            "drivers.uart-serial-rxbuf-size": 512
+```
+
+### Further configuration macros
+
+All configuration options mentioned in this section are optional and when required have to be added to the `macros`-section of your `mbed_app.json` file, e.g.:
+
+``` json
+    "macros": [..., "IDW04A1_WIFI_HW_BUG_WA", "SPWFSAXX_RESET_PIN=D7"]
+```
+
+Beyond you can find the list of available configuration macros each with a short explanation:
+ * `IDW04A1_WIFI_HW_BUG_WA`: activates the HW bug workaround for `IDW04A1`
+ * `SPWFSAXX_WAKEUP_PIN`:    defines module wakeup pin _(requires value)_ 
+ * `SPWFSAXX_RESET_PIN`:     defines module reset pin _(requires value)_ 
+ * `SPWFSAXX_RTS_PIN`:       defines RTS pin of the UART device used _(requires value)_ 
+ * `SPWFSAXX_CTS_PIN`:       defines CTS pin of the UART device used _(requires value)_ 
+
+**Note**: if the values of both `SPWFSAXX_RTS_PIN` and `SPWFSAXX_CTS_PIN` are different from `NC`, hardware flow control - if available on your development board - will be enabled on the used UART device (provided you are using `mbed-os` version greater than or equal to `v5.7.0`).
+
+
+## Firmware upgrade
+
+Please make sure that you are using the latest firmware available for the expansion boards. For information on how to perform a FW upgrade you may refer to [X-CUBE-WIFI1](http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32cube-embedded-software-expansion/x-cube-wifi1.html), especially to document **"X-NUCLEO-IDW0xx1- FW upgrading over UART_v1.2.pdf"** which is contained within folder **"Documentation"** of the X-CUBE-WIFI1 software archive you need to download. 
+
+The actual firmware `.bin` or `.hex` files can be found under 
+- [STSW-WIFI001](http://www.st.com/content/st_com/en/products/embedded-software/wireless-connectivity-software/stsw-wifi001.html) _for what concerns expansion board_ X-NUCLEO-IDW01M1 _and under_
+- [STSW-WIFI004](http://www.st.com/content/st_com/en/products/embedded-software/wireless-connectivity-software/stsw-wifi004.html) _when considering_ X-NUCLEO-IDW04A1.
diff -r 000000000000 -r 79ce2b184a43 SPWFSA01/SPWFSA01.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA01/SPWFSA01.cpp	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,284 @@
+/* SPWFSA01 Device
+ * 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 "SPWFSA01.h"
+#include "SpwfSAInterface.h"
+#include "mbed_debug.h"
+
+#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1
+
+SPWFSA01::SPWFSA01(PinName tx, PinName rx,
+                   PinName rts, PinName cts,
+                   SpwfSAInterface &ifce, bool debug,
+                   PinName wakeup, PinName reset)
+: SPWFSAxx(tx, rx, rts, cts, ifce, debug, wakeup, reset) {
+}
+
+bool SPWFSA01::open(const char *type, int* spwf_id, const char* addr, int port)
+{
+    int socket_id;
+    int value;
+    int trials;
+
+    if(!_parser.send("AT+S.SOCKON=%s,%d,%s,ind", addr, port, type))
+    {
+        debug_if(_dbg_on, "\r\nSPWF> `SPWFSA01::open`: error opening socket (%d)\r\n", __LINE__);
+        return false;
+    }
+
+    /* handle both response possibilities here before returning
+     * otherwise module seems to remain in inconsistent state.
+     */
+
+    /* wait for first character */
+    trials = 0;
+    while((value = _parser.getc()) < 0) {
+        if(trials++ > SPWFXX_MAX_TRIALS) {
+            debug("\r\nSPWF> error opening socket (%d)\r\n", __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+    }
+
+    if(value != _cr_) { // Note: this is different to what the spec exactly says
+        debug_if(_dbg_on, "\r\nSPWF> error opening socket (%d)\r\n", __LINE__);
+        empty_rx_buffer();
+        return false;
+    }
+
+    if(!_recv_delim_lf()) { // Note: this is different to what the spec exactly says
+        debug_if(_dbg_on, "\r\nSPWF> error opening socket (%d)\r\n", __LINE__);
+        empty_rx_buffer();
+        return false;
+    }
+
+    value = _parser.getc();
+    switch(value) {
+        case ' ':
+            if(_parser.recv("ID: %d\n", &socket_id)
+                    && _recv_ok()) {
+                debug_if(_dbg_on, "AT^  ID: %d\r\n", socket_id);
+
+                *spwf_id = socket_id;
+                return true;
+            } else {
+                empty_rx_buffer();
+            }
+            break;
+        case 'E':
+            if(_parser.recv("RROR: %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> error opening socket (%d)\r\n", __LINE__);
+                empty_rx_buffer();
+            }
+            break;
+        default:
+            debug_if(_dbg_on, "\r\nSPWF> error opening socket (value=%d, %d)\r\n", value, __LINE__);
+            break;
+    }
+
+    return false;
+}
+
+int SPWFSA01::_read_in(char* buffer, int spwf_id, uint32_t amount) {
+    int ret = -1;
+
+    MBED_ASSERT(buffer != NULL);
+
+    /* block asynchronous indications */
+    if(!_winds_off()) {
+        return -1;
+    }
+
+    /* read in data */
+    if(_parser.send("AT+S.SOCKR=%d,%u", spwf_id, (unsigned int)amount)) {
+        /* set high timeout */
+        _parser.set_timeout(SPWF_READ_BIN_TIMEOUT);
+        /* read in binary data */
+        int read = _parser.read(buffer, amount);
+        /* reset timeout value */
+        _parser.set_timeout(_timeout);
+        if(read > 0) {
+            if(_recv_ok()) {
+                ret = amount;
+
+                /* remove from pending sizes
+                 * (MUST be done before next async indications handling (e.g. `_winds_on()`)) */
+                _remove_pending_pkt_size(spwf_id, amount);
+            } else {
+                debug_if(_dbg_on, "\r\nSPWF> failed to receive OK (%s, %d)\r\n", __func__, __LINE__);
+                empty_rx_buffer();
+            }
+        } else {
+            debug_if(_dbg_on, "\r\nSPWF> failed to read binary data (%u:%d), (%s, %d)\r\n", amount, read, __func__, __LINE__);
+            empty_rx_buffer();
+        }
+    } else {
+        debug_if(_dbg_on, "\r\nSPWF> failed to send SOCKR (%s, %d)\r\n", __func__, __LINE__);
+    }
+
+    debug_if(_dbg_on, "\r\nSPWF> %s():\t%d:%d\r\n", __func__, spwf_id, amount);
+
+    /* unblock asynchronous indications */
+    _winds_on();
+
+    return ret;
+}
+
+/* betzw - TODO: improve performance! */
+bool SPWFSA01::_recv_ap(nsapi_wifi_ap_t *ap)
+{
+    bool ret;
+    unsigned int channel;
+    int trials;
+
+    ap->security = NSAPI_SECURITY_UNKNOWN;
+
+    /* check for end of list */
+    if(_recv_delim_cr_lf()) {
+        return false;
+    }
+
+    /* run to 'horizontal tab' */
+    trials = 0;
+    while(_parser.getc() != '\x09') {
+        if(trials++ > SPWFXX_MAX_TRIALS) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            return false;
+        }
+    }
+
+    /* read in next line */
+    ret = _parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf();
+
+    /* parse line - first phase */
+    if(ret) {
+        int val = sscanf(_msg_buffer,
+                         " %*s %hhx:%hhx:%hhx:%hhx:%hhx:%hhx CHAN: %u RSSI: %hhd SSID: \'%*255[^\']\'",
+                         &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
+                         &channel, &ap->rssi);
+        if(val < 8) {
+            ret = false;
+        }
+    }
+
+    /* parse line - second phase */
+    if(ret) { // ret == true
+        char value;
+        char *rest, *first, *last;
+
+        /* decide about position of `CAPS:` */
+        first = strchr(_msg_buffer, '\'');
+        if(first == NULL) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            return false;
+        }
+        last = strrchr(_msg_buffer, '\'');
+        if((last == NULL) || (last < (first+1))) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            return false;
+        }
+        rest = strstr(last, "CAPS:");
+        if(rest == NULL) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            return false;
+        }
+
+        /* substitute '\'' with '\0' */
+        *last = '\0';
+
+        /* copy values */
+        memcpy(&ap->ssid, first+1, sizeof(ap->ssid)-1);
+        ap->ssid[sizeof(ap->ssid)-1] = '\0';
+        ap->channel = channel;
+
+        /* skip `CAPS: 0421 ` */
+        if(strlen(rest) < 11) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            return false;
+        }
+        rest += 11;
+
+        /* get next character */
+        value = *rest++;
+        if(value != 'W') { // no security
+            ap->security = NSAPI_SECURITY_NONE;
+            return true;
+        }
+
+        /* determine security */
+        {
+            char buffer[10];
+
+            if(!(sscanf(rest, "%s%*[\x20]", (char*)&buffer) > 0)) { // '\0x20' == <space>
+                return true;
+            } else if(strncmp("EP", buffer, 10) == 0) {
+                ap->security = NSAPI_SECURITY_WEP;
+                return true;
+            } else if(strncmp("PA2", buffer, 10) == 0) {
+                ap->security = NSAPI_SECURITY_WPA2;
+                return true;
+            } else if(strncmp("PA", buffer, 10) != 0) {
+                return true;
+            }
+
+            /* got a "WPA", check for "WPA2" */
+            rest += strlen(buffer);
+            value = *rest++;
+            if(value == '\0') { // no further protocol
+                ap->security = NSAPI_SECURITY_WPA;
+                return true;
+            } else { // assume "WPA2"
+                ap->security = NSAPI_SECURITY_WPA_WPA2;
+                return true;
+            }
+        }
+    } else { // ret == false
+        debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+    }
+
+    return ret;
+}
+
+int SPWFSA01::scan(WiFiAccessPoint *res, unsigned limit)
+{
+    unsigned cnt = 0;
+    nsapi_wifi_ap_t ap;
+
+    if (!_parser.send("AT+S.SCAN=a,s")) {
+        return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    while (_recv_ap(&ap)) {
+        if (cnt < limit) {
+            res[cnt] = WiFiAccessPoint(ap);
+        }
+
+        cnt++;
+        if (limit != 0 && cnt >= limit) {
+            break;
+        }
+    }
+
+    if(!_recv_ok()) {
+        empty_rx_buffer();
+    }
+
+    return cnt;
+}
+
+#endif // MBED_CONF_IDW0XX1_EXPANSION_BOARD
diff -r 000000000000 -r 79ce2b184a43 SPWFSA01/SPWFSA01.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA01/SPWFSA01.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,66 @@
+/* SPWFSA01 Device
+ * 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.
+ */
+ 
+#ifndef SPWFSA01_H
+#define SPWFSA01_H
+
+#include "mbed.h"
+#include "ATCmdParser.h"
+#include "BlockExecuter.h"
+
+#include "./spwfsa01_at_strings.h"
+#include "../SPWFSAxx.h"
+
+class SpwfSAInterface;
+
+/** SPWFSA01 Interface class.
+    This is an interface to a SPWFSA01 module.
+ */
+class SPWFSA01 : public SPWFSAxx
+{
+public:
+    SPWFSA01(PinName tx, PinName rx,
+             PinName rts, PinName cts,
+             SpwfSAInterface &ifce, bool debug,
+             PinName wakeup, PinName reset);
+
+    /**
+     * Open a socketed connection
+     *
+     * @param type the type of socket to open "u" (UDP) or "t" (TCP)
+     * @param id id to get the new socket number, valid 0-7
+     * @param port port to open connection with
+     * @param addr the IP address of the destination
+     * @return true only if socket opened successfully
+     */
+    bool open(const char *type, int* id, const char* addr, int port);
+
+    /** Scan for available networks
+     *
+     * @param  ap    Pointer to allocated array to store discovered AP
+     * @param  limit Size of allocated @a res array, or 0 to only count available AP
+     * @return       Number of entries in @a res, or if @a count was 0 number of available networks, negative on error
+     *               see @a nsapi_error
+     */
+    nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned limit);
+
+private:
+    bool _recv_ap(nsapi_wifi_ap_t *ap);
+
+    virtual int _read_in(char*, int, uint32_t);
+};
+
+#endif // SPWFSA01_H
diff -r 000000000000 -r 79ce2b184a43 SPWFSA01/spwfsa01_at_strings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA01/spwfsa01_at_strings.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,58 @@
+#ifndef SPWFSAXX_AT_STRINGS_H
+#define SPWFSAXX_AT_STRINGS_H
+
+#if defined(TARGET_FF_MORPHO)
+
+#if !defined(SPWFSAXX_WAKEUP_PIN)
+#define SPWFSAXX_WAKEUP_PIN   PC_8                                              // A3
+#endif // !defined(SPWFSAXX_WAKEUP_PIN)
+#if !defined(SPWFSAXX_RESET_PIN)
+#define SPWFSAXX_RESET_PIN    PC_12                                             // D7 / NC
+#endif // !defined(SPWFSAXX_RESET_PIN)
+
+#else // !defined(TARGET_FF_MORPHO)
+
+#if !defined(SPWFSAXX_WAKEUP_PIN)
+#define SPWFSAXX_WAKEUP_PIN   NC                                                // A3
+#endif // !defined(SPWFSAXX_WAKEUP_PIN)
+#if !defined(SPWFSAXX_RESET_PIN)
+#define SPWFSAXX_RESET_PIN    NC                                                // D7 / NC
+#endif // !defined(SPWFSAXX_RESET_PIN)
+
+#endif // !defined(TARGET_FF_MORPHO)
+
+#define SPWFXX_SEND_RECV_PKTSIZE    (730)
+
+#define SPWFXX_OOB_ERROR            "ERROR:"                                            // "AT-S.ERROR:"
+
+#define SPWFXX_RECV_OK              "OK\n"                                              // "AT-S.OK\n"
+#define SPWFXX_RECV_WIFI_UP         "+WIND:24:WiFi Up:%u.%u.%u.%u\n"                    // "+WIND:24:WiFi Up:%*u:%u.%u.%u.%u\n"
+#define SPWFXX_RECV_IP_ADDR         "#  ip_ipaddr = %u.%u.%u.%u\n"                      // "AT-S.Var:ip_ipaddr=%u.%u.%u.%u\n"
+#define SPWFXX_RECV_GATEWAY         "#  ip_gw = %u.%u.%u.%u\n"                          // "AT-S.Var:ip_gw=%u.%u.%u.%u\n"
+#define SPWFXX_RECV_NETMASK         "#  ip_netmask = %u.%u.%u.%u\n"                     // "AT-S.Var:ip_netmask=%u.%u.%u.%u\n"
+#define SPWFXX_RECV_RX_RSSI         "#  0.rx_rssi = %d\n"                               // "AT-S.Var:0.rx_rssi=%d\n"
+#define SPWFXX_RECV_MAC_ADDR        "#  nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\n"          // "AT-S.Var:nv_wifi_macaddr=%x:%x:%x:%x:%x:%x\n"
+#define SPWFXX_RECV_DATALEN         " DATALEN: %u\n"                                    // "AT-S.Query:%u\n"
+#define SPWFXX_RECV_PENDING_DATA    ":%d:%d\n"                                          // "::%u:%*u:%u\n"
+#define SPWFXX_RECV_SOCKET_CLOSED   ":%d\n"                                             // ":%u:%*u\n"
+
+#define SPWFXX_SEND_FWCFG           "AT&F"                                              // "AT+S.FCFG"
+#define SPWFXX_SEND_DISABLE_LE      "AT+S.SCFG=localecho1,0"                            // "AT+S.SCFG=console_echo,0"
+#define SPWFXX_SEND_DSPLY_CFGV      "AT&V"                                              // "AT+S.GCFG"
+#define SPWFXX_SEND_GET_CONS_STATE  "AT+S.GCFG=console1_enabled"                        // "AT+S.GCFG=console_enabled"
+#define SPWFXX_SEND_GET_CONS_SPEED  "AT+S.GCFG=console1_speed"                          // "AT+S.GCFG=console_speed"
+#define SPWFXX_SEND_GET_HWFC_STATE  "AT+S.GCFG=console1_hwfc"                           // "AT+S.GCFG=console_hwfc"
+#define SPWFXX_SEND_GET_CONS_DELIM  "AT+S.GCFG=console1_delimiter"                      // "AT+S.GCFG=console_delimiter"
+#define SPWFXX_SEND_GET_CONS_ERRS   "AT+S.GCFG=console1_errs"                           // "AT+S.GCFG=console_errs"
+#define SPWFXX_SEND_DISABLE_FC      "AT+S.SCFG=console1_hwfc,0"                         // "AT+S.SCFG=console_hwfc,0"
+#define SPWFXX_SEND_ENABLE_FC       "AT+S.SCFG=console1_hwfc,1"                         // "AT+S.SCFG=console_hwfc,1"
+#define SPWFXX_SEND_SW_RESET        "AT+CFUN=1"                                         // "AT+S.RESET"
+#define SPWFXX_SEND_SAVE_SETTINGS   "AT&W"                                              // "AT+S.WCFG"
+#define SPWFXX_SEND_WIND_OFF_HIGH   "AT+S.SCFG=wind_off_high,"                          // "AT+S.SCFG=console_wind_off_high,"
+#define SPWFXX_SEND_WIND_OFF_MEDIUM "AT+S.SCFG=wind_off_medium,"                        // "AT+S.SCFG=console_wind_off_medium,"
+#define SPWFXX_SEND_WIND_OFF_LOW    "AT+S.SCFG=wind_off_low,"                           // "AT+S.SCFG=console_wind_off_low,"
+
+#define SPWFXX_WINDS_HIGH_ON        "0x00000000"                                        // "0x00100000"
+#define SPWFXX_WINDS_MEDIUM_ON      "0x00000000"                                        // "0x80000000"
+
+#endif // SPWFSAXX_AT_STRINGS_H
diff -r 000000000000 -r 79ce2b184a43 SPWFSA04/SPWFSA04.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA04/SPWFSA04.cpp	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,318 @@
+/* SPWFSA04 Device
+ * 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 "SPWFSA04.h"
+#include "SpwfSAInterface.h"
+#include "mbed_debug.h"
+
+#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1
+
+SPWFSA04::SPWFSA04(PinName tx, PinName rx,
+                   PinName rts, PinName cts,
+                   SpwfSAInterface &ifce, bool debug,
+                   PinName wakeup, PinName reset)
+: SPWFSAxx(tx, rx, rts, cts, ifce, debug, wakeup, reset) {
+}
+
+bool SPWFSA04::open(const char *type, int* spwf_id, const char* addr, int port)
+{
+    int socket_id;
+    int value;
+    int trials;
+
+    if(!_parser.send("AT+S.SOCKON=%s,%d,NULL,%s", addr, port, type))
+    {
+        debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__);
+        return false;
+    }
+
+    /* handle both response possibilities here before returning
+     * otherwise module seems to remain in inconsistent state.
+     */
+
+    if(!_parser.recv("AT-S.")) { // get prefix
+        debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__);
+        empty_rx_buffer();
+        return false;
+    }
+
+    /* wait for next character */
+    trials = 0;
+    while((value = _parser.getc()) < 0) {
+        if(++trials >= SPWFXX_MAX_TRIALS) {
+            debug("\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+    }
+
+    switch(value) {
+        case 'O':
+            /* get next character */
+            value = _parser.getc();
+            if(value != 'n') {
+                debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d) (%d, \'%c\')\r\n",
+                         __LINE__, value, value);
+                empty_rx_buffer();
+                return false;
+            }
+
+            /* get socket id */
+            if(!(_parser.recv(":%*u.%*u.%*u.%*u:%d\n", &socket_id)
+                    && _recv_delim_lf()
+                    && _recv_ok())) {
+                debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__);
+                empty_rx_buffer();
+                return false;
+            }
+            debug_if(_dbg_on, "AT^ AT-S.On:%s:%d\r\n", addr, socket_id);
+
+            *spwf_id = socket_id;
+            return true;
+        case 'E':
+            int err_nr;
+            if(_parser.recv("RROR:%d:%255[^\n]\n", &err_nr, _msg_buffer) && _recv_delim_lf()) {
+                debug_if(_dbg_on, "AT^ AT-S.ERROR:%d:%s (%d)\r\n", err_nr, _msg_buffer, __LINE__);
+            } else {
+                debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d)\r\n", __LINE__);
+                empty_rx_buffer();
+            }
+            break;
+        default:
+            debug_if(_dbg_on, "\r\nSPWF> `SPWFSA04::open`: error opening socket (%d) (%d, \'%c\')\r\n",
+                     __LINE__, value, value);
+            empty_rx_buffer();
+            break;
+    }
+
+    return false;
+}
+
+int SPWFSA04::_read_in(char* buffer, int spwf_id, uint32_t amount) {
+    int ret = -1;
+    int received, cumulative;
+
+    MBED_ASSERT(buffer != NULL);
+
+    /* block asynchronous indications */
+    if(!_winds_off()) {
+        return -1;
+    }
+
+    /* read in data */
+    if(_parser.send("AT+S.SOCKR=%d,%d", spwf_id, (unsigned int)amount)) {
+        if(!(_parser.recv("AT-S.Reading:%d:%d\n", &received, &cumulative) &&
+                _recv_delim_lf())) {
+            debug_if(_dbg_on, "\r\nSPWF> failed to receive AT-S.Reading (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+        } else {
+            /* set high timeout */
+            _parser.set_timeout(SPWF_READ_BIN_TIMEOUT);
+            /* read in binary data */
+            int read = _parser.read(buffer, amount);
+            /* reset timeout value */
+            _parser.set_timeout(_timeout);
+            if(read > 0) {
+                if(_recv_ok()) {
+                    ret = amount;
+
+                    /* remove from pending sizes
+                     * (MUST be done before next async indications handling (e.g. `_winds_on()`)) */
+                    _remove_pending_pkt_size(spwf_id, amount);
+                } else {
+                    debug_if(_dbg_on, "\r\nSPWF> failed to receive OK (%s, %d)\r\n", __func__, __LINE__);
+                    empty_rx_buffer();
+                }
+            } else {
+                debug_if(_dbg_on, "\r\nSPWF> failed to read binary data (%s, %d)\r\n", __func__, __LINE__);
+                empty_rx_buffer();
+            }
+        }
+    } else {
+        debug_if(_dbg_on, "%s(%d): failed to send SOCKR\r\n", __func__, __LINE__);
+    }
+
+    debug_if(_dbg_on, "\r\nSPWF> %s():\t%d:%d\r\n", __func__, spwf_id, amount);
+
+    /* unblock asynchronous indications */
+    _winds_on();
+
+    return ret;
+}
+
+/* betzw - TODO: improve performance! */
+bool SPWFSA04::_recv_ap(nsapi_wifi_ap_t *ap)
+{
+    bool ret;
+    int curr;
+    unsigned int channel;
+    int trials;
+
+    ap->security = NSAPI_SECURITY_UNKNOWN;
+
+    /* determine list end */
+    curr = _parser.getc();
+    if(curr == 'A') { // assume end of list ("AT-S.OK")
+        if(!(_parser.recv("T-S.OK\n") && _recv_delim_lf())) {
+            empty_rx_buffer();
+        }
+        return false;
+    }
+
+    /* run to 'horizontal tab' */
+    trials = 0;
+    while(_parser.getc() != '\x09') {
+        if(trials++ > SPWFXX_MAX_TRIALS) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+    }
+
+    /* read in next line */
+    ret = _parser.recv("%255[^\n]\n", _msg_buffer) && _recv_delim_lf();
+
+    /* parse line - first phase */
+    if(ret) {
+        int val = sscanf(_msg_buffer,
+                         " %*s %hhx:%hhx:%hhx:%hhx:%hhx:%hhx CHAN: %u RSSI: %hhd SSID: \'%*255[^\']\'",
+                         &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
+                         &channel, &ap->rssi);
+        if(val < 8) {
+            ret = false;
+        }
+    }
+
+    /* parse line - second phase */
+    if(ret) { // ret == true
+        char value;
+        char *rest, *first, *last;
+
+        /* decide about position of `CAPS:` */
+        first = strchr(_msg_buffer, '\'');
+        if(first == NULL) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+        last = strrchr(_msg_buffer, '\'');
+        if((last == NULL) || (last < (first+1))) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+        rest = strstr(last, "CAPS:");
+        if(rest == NULL) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+
+        /* substitute '\'' with '\0' */
+        *last = '\0';
+
+        /* copy values */
+        memcpy(&ap->ssid, first+1, sizeof(ap->ssid)-1);
+        ap->ssid[sizeof(ap->ssid)-1] = '\0';
+        ap->channel = channel;
+
+        /* skip `CAPS: 0421 ` */
+        if(strlen(rest) < 11) {
+            debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+            empty_rx_buffer();
+            return false;
+        }
+        rest += 11;
+
+        /* get next character */
+        value = *rest++;
+        if(value != 'W') { // no security
+            ap->security = NSAPI_SECURITY_NONE;
+            return true;
+        }
+
+        /* determine security */
+        {
+            char buffer[10];
+
+            if(!(sscanf(rest, "%s%*[\x20]", (char*)&buffer) > 0)) { // '\0x20' == <space>
+                return true;
+            } else if(strncmp("EP", buffer, 10) == 0) {
+                ap->security = NSAPI_SECURITY_WEP;
+                return true;
+            } else if(strncmp("PA2", buffer, 10) == 0) {
+                ap->security = NSAPI_SECURITY_WPA2;
+                return true;
+            } else if(strncmp("PA", buffer, 10) != 0) {
+                return true;
+            }
+
+            /* got a "WPA", check for "WPA2" */
+            rest += strlen(buffer);
+            value = *rest++;
+            if(value == '\0') { // no further protocol
+                ap->security = NSAPI_SECURITY_WPA;
+                return true;
+            } else { // assume "WPA2"
+                ap->security = NSAPI_SECURITY_WPA_WPA2;
+                return true;
+            }
+        }
+    } else { // ret == false
+        debug("\r\nSPWF> WARNING: might happen in case of RX buffer overflow! (%s, %d)\r\n", __func__, __LINE__);
+        empty_rx_buffer();
+    }
+
+    return ret;
+}
+
+nsapi_size_or_error_t SPWFSA04::scan(WiFiAccessPoint *res, unsigned limit)
+{
+    unsigned int cnt = 0, found;
+    nsapi_wifi_ap_t ap;
+
+    if (!_parser.send("AT+S.SCAN=s,")) {
+        return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    if(!(_parser.recv("AT-S.Parsing Networks:%u\n", &found) && _recv_delim_lf())) {
+        debug_if(_dbg_on, "SPWF> error start network scanning\r\n");
+        empty_rx_buffer();
+        return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    debug_if(_dbg_on, "AT^ AT-S.Parsing Networks:%u\r\n", found);
+
+    if(found > 0) {
+        while (_recv_ap(&ap)) {
+            if (cnt < limit) {
+                res[cnt] = WiFiAccessPoint(ap);
+            }
+
+            if (!((limit != 0) && ((cnt + 1) > limit))) {
+                cnt++;
+            }
+        }
+    } else {
+        if(!_recv_ok()) {
+            empty_rx_buffer();
+        }
+    }
+
+    return cnt;
+}
+
+#endif // MBED_CONF_IDW0XX1_EXPANSION_BOARD
diff -r 000000000000 -r 79ce2b184a43 SPWFSA04/SPWFSA04.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA04/SPWFSA04.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,66 @@
+/* SPWFSA04 Device
+ * 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.
+ */
+ 
+#ifndef SPWFSA04_H
+#define SPWFSA04_H
+
+#include "mbed.h"
+#include "ATCmdParser.h"
+#include "BlockExecuter.h"
+
+#include "./spwfsa04_at_strings.h"
+#include "../SPWFSAxx.h"
+
+class SpwfSAInterface;
+
+/** SPWFSA04 Interface class.
+    This is an interface to a SPWFSA04 module.
+ */
+class SPWFSA04 : public SPWFSAxx
+{
+public:
+    SPWFSA04(PinName tx, PinName rx,
+             PinName rts, PinName cts,
+             SpwfSAInterface &ifce, bool debug,
+             PinName wakeup, PinName reset);
+
+    /**
+     * Open a socketed connection
+     *
+     * @param type the type of socket to open "u" (UDP) or "t" (TCP)
+     * @param id id to get the new socket number, valid 0-7
+     * @param port port to open connection with
+     * @param addr the IP address of the destination
+     * @return true only if socket opened successfully
+     */
+    bool open(const char *type, int* id, const char* addr, int port);
+
+    /** Scan for available networks
+     *
+     * @param  ap    Pointer to allocated array to store discovered AP
+     * @param  limit Size of allocated @a res array, or 0 to only count available AP
+     * @return       Number of entries in @a res, or if @a count was 0 number of available networks, negative on error
+     *               see @a nsapi_error
+     */
+    nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned limit);
+
+private:
+    bool _recv_ap(nsapi_wifi_ap_t *ap);
+
+    virtual int _read_in(char*, int, uint32_t);
+};
+
+#endif // SPWFSA04_H
diff -r 000000000000 -r 79ce2b184a43 SPWFSA04/spwfsa04_at_strings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSA04/spwfsa04_at_strings.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,65 @@
+#ifndef SPWFSAXX_AT_STRINGS_H
+#define SPWFSAXX_AT_STRINGS_H
+
+/* Define beyond macro if your X-NUCLEO-IDW04A1 expansion board has NOT the `WIFI_RST` HW patch applied on it */
+// #define IDW04A1_WIFI_HW_BUG_WA // delegated to mbed config system
+
+#if defined(TARGET_FF_ARDUINO)
+
+#if !defined(SPWFSAXX_WAKEUP_PIN)
+#define SPWFSAXX_WAKEUP_PIN   A3
+#endif
+#if !defined(SPWFSAXX_RESET_PIN)
+#ifndef IDW04A1_WIFI_HW_BUG_WA
+#define SPWFSAXX_RESET_PIN    D7
+#else // IDW04A1_WIFI_HW_PATCH
+#define SPWFSAXX_RESET_PIN    NC
+#endif // !IDW04A1_WIFI_HW_PATCH
+#endif
+
+#else // !defined(TARGET_FF_ARDUINO)
+
+#if !defined(SPWFSAXX_WAKEUP_PIN)
+#define SPWFSAXX_WAKEUP_PIN   NC
+#endif
+#if !defined(SPWFSAXX_RESET_PIN)
+#define SPWFSAXX_RESET_PIN    NC
+#endif
+
+#endif // !defined(TARGET_FF_ARDUINO)
+
+#define SPWFXX_SEND_RECV_PKTSIZE    (730)
+
+#define SPWFXX_OOB_ERROR            "AT-S.ERROR:"                                           // "ERROR:"
+
+#define SPWFXX_RECV_OK              "AT-S.OK\n"                                             // "OK\n"
+#define SPWFXX_RECV_WIFI_UP         "+WIND:24:WiFi Up:%*u:%u.%u.%u.%u\n"                    // "+WIND:24:WiFi Up:%u.%u.%u.%u\n"
+#define SPWFXX_RECV_IP_ADDR         "AT-S.Var:ip_ipaddr=%u.%u.%u.%u\n"                      // "#  ip_ipaddr = %u.%u.%u.%u\n"
+#define SPWFXX_RECV_GATEWAY         "AT-S.Var:ip_gw=%u.%u.%u.%u\n"                          // "#  ip_gw = %u.%u.%u.%u\n"
+#define SPWFXX_RECV_NETMASK         "AT-S.Var:ip_netmask=%u.%u.%u.%u\n"                     // "#  ip_netmask = %u.%u.%u.%u\n"
+#define SPWFXX_RECV_RX_RSSI         "AT-S.Var:0.rx_rssi=%d\n"                               // "#  0.rx_rssi = %d\n"
+#define SPWFXX_RECV_MAC_ADDR        "AT-S.Var:nv_wifi_macaddr=%x:%x:%x:%x:%x:%x\n"          // "#  nv_wifi_macaddr = %x:%x:%x:%x:%x:%x\n"
+#define SPWFXX_RECV_DATALEN         "AT-S.Query:%u\n"                                       // " DATALEN: %u\n"
+#define SPWFXX_RECV_PENDING_DATA    "::%u:%*u:%u\n"                                         // ":%d:%d\n"
+#define SPWFXX_RECV_SOCKET_CLOSED   ":%u:%*u\n"                                             // ":%d\n"
+
+#define SPWFXX_SEND_FWCFG           "AT+S.FCFG"                                             // "AT&F"
+#define SPWFXX_SEND_DISABLE_LE      "AT+S.SCFG=console_echo,0"                              // "AT+S.SCFG=localecho1,0"
+#define SPWFXX_SEND_DSPLY_CFGV      "AT+S.GCFG"                                             // "AT&V"
+#define SPWFXX_SEND_GET_CONS_STATE  "AT+S.GCFG=console_enabled"                             // "AT+S.GCFG=console1_enabled"
+#define SPWFXX_SEND_GET_CONS_SPEED  "AT+S.GCFG=console_speed"                               // "AT+S.GCFG=console1_speed"
+#define SPWFXX_SEND_GET_HWFC_STATE  "AT+S.GCFG=console_hwfc"                                // "AT+S.GCFG=console1_hwfc"
+#define SPWFXX_SEND_GET_CONS_DELIM  "AT+S.GCFG=console_delimiter"                           // "AT+S.GCFG=console1_delimiter"
+#define SPWFXX_SEND_GET_CONS_ERRS   "AT+S.GCFG=console_errs"                                // "AT+S.GCFG=console1_errs"
+#define SPWFXX_SEND_DISABLE_FC      "AT+S.SCFG=console_hwfc,0"                              // "AT+S.SCFG=console1_hwfc,0"
+#define SPWFXX_SEND_ENABLE_FC       "AT+S.SCFG=console_hwfc,1"                              // "AT+S.SCFG=console1_hwfc,1"
+#define SPWFXX_SEND_SW_RESET        "AT+S.RESET"                                            // "AT+CFUN=1"
+#define SPWFXX_SEND_SAVE_SETTINGS   "AT+S.WCFG"                                             // "AT&W"
+#define SPWFXX_SEND_WIND_OFF_HIGH   "AT+S.SCFG=console_wind_off_high,"                      // "AT+S.SCFG=wind_off_high,"
+#define SPWFXX_SEND_WIND_OFF_MEDIUM "AT+S.SCFG=console_wind_off_medium,"                    // "AT+S.SCFG=wind_off_medium,"
+#define SPWFXX_SEND_WIND_OFF_LOW    "AT+S.SCFG=console_wind_off_low,"                       // "AT+S.SCFG=wind_off_low,"
+
+#define SPWFXX_WINDS_HIGH_ON        "0x00100000"                                            // "0x00000000"
+#define SPWFXX_WINDS_MEDIUM_ON      "0x80000000"                                            // "0x00000000"
+
+#endif // SPWFSAXX_AT_STRINGS_H
diff -r 000000000000 -r 79ce2b184a43 SPWFSAxx.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSAxx.cpp	Wed May 23 14:37:10 2018 +0000
@@ -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;
+}
diff -r 000000000000 -r 79ce2b184a43 SPWFSAxx.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SPWFSAxx.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,446 @@
+/* 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.
+ */
+ 
+#ifndef SPWFSAXX_H
+#define SPWFSAXX_H
+
+#include "mbed.h"
+#include "ATCmdParser.h"
+#include "BlockExecuter.h"
+
+/* Common SPWFSAxx macros */
+#define SPWFXX_WINDS_LOW_ON         "0x00000000"
+#define SPWFXX_DEFAULT_BAUD_RATE    115200
+#define SPWFXX_MAX_TRIALS           3
+
+#if !defined(SPWFSAXX_RTS_PIN)
+#define SPWFSAXX_RTS_PIN    NC
+#endif // !defined(SPWFSAXX_RTS_PIN)
+#if !defined(SPWFSAXX_CTS_PIN)
+#define SPWFSAXX_CTS_PIN    NC
+#endif // !defined(SPWFSAXX_CTS_PIN)
+
+#define SPWFXX_ERR_OK               (+1)
+#define SPWFXX_ERR_OOM              (-1)
+#define SPWFXX_ERR_READ             (-2)
+#define SPWFXX_ERR_LEN              (-3)
+
+
+/* Max number of sockets & packets */
+#define SPWFSA_SOCKET_COUNT         (8)
+#define SPWFSA_MAX_PACKETS          (4)
+
+#define PENDING_DATA_SLOTS          (13)
+
+/* Pending data packets size buffer */
+class SpwfRealPendingPackets {
+public:
+    SpwfRealPendingPackets() {
+        reset();
+    }
+
+    void add(uint32_t new_cum_size) {
+        MBED_ASSERT(new_cum_size >= cumulative_size);
+
+        if(new_cum_size == cumulative_size) {
+            /* nothing to do */
+            return;
+        }
+
+        /* => `new_cum_size > cumulative_size` */
+        real_pkt_sizes[last_pkt_ptr] = (uint16_t)(new_cum_size - cumulative_size);
+        cumulative_size = new_cum_size;
+
+        last_pkt_ptr = (last_pkt_ptr + 1) % PENDING_DATA_SLOTS;
+
+        MBED_ASSERT(first_pkt_ptr != last_pkt_ptr);
+    }
+
+    uint32_t get(void) {
+        if(empty()) return 0;
+
+        return real_pkt_sizes[first_pkt_ptr];
+    }
+
+    uint32_t cumulative(void) {
+        return cumulative_size;
+    }
+
+    uint32_t remove(uint32_t size) {
+        MBED_ASSERT(!empty());
+
+        uint32_t ret = real_pkt_sizes[first_pkt_ptr];
+        first_pkt_ptr = (first_pkt_ptr + 1) % PENDING_DATA_SLOTS;
+
+        MBED_ASSERT(ret == size);
+        MBED_ASSERT(ret <= cumulative_size);
+        cumulative_size -= ret;
+
+        return ret;
+    }
+
+    void reset(void) {
+        memset(this, 0, sizeof(*this));
+    }
+
+private:
+    bool empty(void) {
+        if(first_pkt_ptr == last_pkt_ptr) {
+            MBED_ASSERT(cumulative_size == 0);
+            return true;
+        }
+        return false;
+    }
+
+    uint16_t real_pkt_sizes[PENDING_DATA_SLOTS];
+    uint8_t  first_pkt_ptr;
+    uint8_t  last_pkt_ptr;
+    uint32_t cumulative_size;
+};
+
+class SpwfSAInterface;
+
+/** SPWFSAxx Interface class.
+    This is an interface to a SPWFSAxx module.
+ */
+class SPWFSAxx
+{
+private:
+    /* abstract class*/
+    SPWFSAxx(PinName tx, PinName rx, PinName rts, PinName cts,
+             SpwfSAInterface &ifce, bool debug,
+             PinName wakeup, PinName reset);
+
+public:
+    /**
+     * Init the SPWFSAxx
+     *
+     * @param mode mode in which to startup
+     * @return true only if SPWFSAxx has started up correctly
+     */
+    bool startup(int mode);
+
+    /**
+     * Connect SPWFSAxx to AP
+     *
+     * @param ap the name of the AP
+     * @param passPhrase the password of AP
+     * @param securityMode the security mode of AP (WPA/WPA2, WEP, Open)
+     * @return true only if SPWFSAxx is connected successfully
+     */
+    bool connect(const char *ap, const char *passPhrase, int securityMode);
+
+    /**
+     * Disconnect SPWFSAxx from AP
+     *
+     * @return true only if SPWFSAxx is disconnected successfully
+     */
+    bool disconnect(void);
+
+    /**
+     * Get the IP address of SPWFSAxx
+     *
+     * @return null-terminated IP address or null if no IP address is assigned
+     */
+    const char *getIPAddress(void);
+
+    /**
+     * Get the MAC address of SPWFSAxx
+     *
+     * @return null-terminated MAC address or null if no MAC address is assigned
+     */
+    const char *getMACAddress(void);
+
+    /** Get the local gateway
+     *
+     *  @return         Null-terminated representation of the local gateway
+     *                  or null if no network mask has been received
+     */
+    const char *getGateway(void);
+
+    /** Get the local network mask
+     *
+     *  @return         Null-terminated representation of the local network mask
+     *                  or null if no network mask has been received
+     */
+    const char *getNetmask(void);
+
+    /** Gets the current radio signal strength for active connection
+     *
+     * @return          Connection strength in dBm (negative value)
+     */
+    int8_t getRssi();
+
+    /**
+     * Sends data to an open socket
+     *
+     * @param spwf_id module id of socket to send to
+     * @param data data to be sent
+     * @param amount amount of data to be sent - max 1024
+     * @param internal_id driver id of socket to send to
+     * @return number of written bytes on success, negative on failure
+     */
+    nsapi_size_or_error_t send(int spwf_id, const void *data, uint32_t amount, int internal_id);
+
+    /**
+     * Receives data from an open socket
+     *
+     * @param id id to receive from
+     * @param data placeholder for returned information
+     * @param amount number of bytes to be received
+     * @param datagram receive a datagram packet
+     * @return the number of bytes received
+     */
+    int32_t recv(int id, void *data, uint32_t amount, bool datagram);
+
+    /**
+     * Closes a socket
+     *
+     * @param id id of socket to close, valid only 0-4
+     * @return true only if socket is closed successfully
+     */
+    bool close(int id);
+
+    /**
+     * Allows timeout to be changed between commands
+     *
+     * @param timeout_ms timeout of the connection
+     */
+    void setTimeout(uint32_t timeout_ms);
+
+    /**
+     * Attach a function to call whenever network state has changed
+     *
+     * @param func A pointer to a void function, or 0 to set as none
+     */
+    void attach(Callback<void()> func);
+
+    /**
+     * Attach a function to call whenever network state has changed
+     *
+     * @param obj pointer to the object to call the member function on
+     * @param method pointer to the member function to call
+     */
+    template <typename T, typename M>
+    void attach(T *obj, M method) {
+        attach(Callback<void()>(obj, method));
+    }
+
+    static const char _cr_ = '\x0d'; // '\r' carriage return
+    static const char _lf_ = '\x0a'; // '\n' line feed
+
+private:
+    UARTSerial _serial;
+    ATCmdParser _parser;
+
+    DigitalOut _wakeup;
+    DigitalOut _reset;
+    PinName _rts;
+    PinName _cts;
+
+    int _timeout;
+    bool _dbg_on;
+
+    int _pending_sockets_bitmap;
+    SpwfRealPendingPackets _pending_pkt_sizes[SPWFSA_SOCKET_COUNT];
+
+    bool _network_lost_flag;
+    SpwfSAInterface &_associated_interface;
+
+    /**
+     * Reset SPWFSAxx
+     *
+     * @return true only if SPWFSAxx resets successfully
+     */
+    bool hw_reset(void);
+    bool reset(void);
+
+    /**
+     * Check if SPWFSAxx is connected
+     *
+     * @return true only if the chip has an IP address
+     */
+    bool isConnected(void);
+
+    /**
+     * Checks if data is available
+     */
+    bool readable(void) {
+        return _serial.FileHandle::readable();
+    }
+
+    /**
+     * Checks if data can be written
+     */
+    bool writeable(void) {
+        return _serial.FileHandle::writable();
+    }
+
+    /**
+     * Try to empty RX buffer
+     * Can be used when commands fail receiving expected response to try to recover situation
+     * @note Gives no guarantee that situation improves
+     */
+    void empty_rx_buffer(void) {
+        while(readable()) _parser.getc();
+    }
+
+    /* block calling (external) callback */
+    volatile unsigned int _call_event_callback_blocked;
+    Callback<void()> _callback_func;
+
+    struct packet {
+        struct packet *next;
+        int id;
+        uint32_t len;
+        // data follows
+    } *_packets, **_packets_end;
+
+    void _packet_handler_th(void);
+    void _execute_bottom_halves(void);
+    void _network_lost_handler_th(void);
+    void _network_lost_handler_bh(void);
+    void _hard_fault_handler(void);
+    void _wifi_hwfault_handler(void);
+    void _server_gone_handler(void);
+#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1
+    void _skip_oob(void);
+#endif
+    bool _wait_wifi_hw_started(void);
+    bool _wait_console_active(void);
+    int _read_len(int);
+    int _flush_in(char*, int);
+    bool _winds_off(void);
+    void _winds_on(void);
+    void _read_in_pending(void);
+    int _read_in_pkt(int spwf_id, bool close);
+    int _read_in_packet(int spwf_id, uint32_t amount);
+    void _recover_from_hard_faults(void);
+    void _free_packets(int spwf_id);
+    void _free_all_packets(void);
+    void _process_winds();
+
+    virtual int _read_in(char*, int, uint32_t) = 0;
+
+    bool _recv_delim_lf(void) {
+        return (_parser.getc() == _lf_);
+    }
+
+    bool _recv_delim_cr(void) {
+        return (_parser.getc() == _cr_);
+    }
+
+    bool _recv_delim_cr_lf(void) {
+        return _recv_delim_cr() && _recv_delim_lf();
+    }
+
+    bool _recv_ok(void) {
+        return _parser.recv(SPWFXX_RECV_OK) && _recv_delim_lf();
+    }
+
+    void _add_pending_packet_sz(int spwf_id, uint32_t size);
+    void _add_pending_pkt_size(int spwf_id, uint32_t size) {
+        _pending_pkt_sizes[spwf_id].add(size);
+    }
+
+    uint32_t _get_cumulative_size(int spwf_id) {
+        return _pending_pkt_sizes[spwf_id].cumulative();
+    }
+
+    uint32_t _remove_pending_pkt_size(int spwf_id, uint32_t size) {
+        return _pending_pkt_sizes[spwf_id].remove(size);
+    }
+
+    uint32_t _get_pending_pkt_size(int spwf_id) {
+        return _pending_pkt_sizes[spwf_id].get();
+    }
+
+   void _reset_pending_pkt_sizes(int spwf_id) {
+        _pending_pkt_sizes[spwf_id].reset();
+    }
+
+   void _set_pending_data(int spwf_id) {
+       _pending_sockets_bitmap |= (1 << spwf_id);
+   }
+
+   void _clear_pending_data(int spwf_id) {
+        _pending_sockets_bitmap &= ~(1 << spwf_id);
+    }
+
+    bool _is_data_pending(int spwf_id) {
+        return (_pending_sockets_bitmap & (1 << spwf_id)) ? true : false;
+    }
+
+    bool _is_data_pending(void) {
+        if(_pending_sockets_bitmap != 0) return true;
+        else return false;
+    }
+
+    void _packet_handler_bh(void) {
+        /* read in other eventually pending packages */
+        _read_in_pending();
+    }
+
+    /* Do not call the (external) callback in IRQ context while performing critical module operations */
+    void _event_handler(void);
+
+    void _error_handler(void);
+
+    void _call_callback(void) {
+        if((bool)_callback_func) {
+            _callback_func();
+        }
+    }
+
+    bool _is_event_callback_blocked(void) {
+        return (_call_event_callback_blocked != 0);
+    }
+
+    void _block_event_callback(void) {
+        _call_event_callback_blocked++;
+    }
+
+    void _unblock_event_callback(void) {
+        MBED_ASSERT(_call_event_callback_blocked > 0);
+        _call_event_callback_blocked--;
+        if(_call_event_callback_blocked == 0) {
+            _trigger_event_callback();
+        }
+    }
+
+    /* trigger call of (external) callback in case there is still data */
+    void _trigger_event_callback(void) {
+        MBED_ASSERT(_call_event_callback_blocked == 0);
+        /* if still data available */
+        if(readable()) {
+            _call_callback();
+        }
+    }
+
+    char _ip_buffer[16];
+    char _gateway_buffer[16];
+    char _netmask_buffer[16];
+    char _mac_buffer[18];
+
+    char _msg_buffer[256];
+
+private:
+    friend class SPWFSA01;
+    friend class SPWFSA04;
+    friend class SpwfSAInterface;
+};
+
+#endif // SPWFSAXX_H
diff -r 000000000000 -r 79ce2b184a43 SpwfSAInterface.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SpwfSAInterface.cpp	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,512 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 20015 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.
+ */
+
+/**
+  ******************************************************************************
+  * @file    SpwfSAInterface.cpp
+  * @author  STMicroelectronics
+  * @brief   Implementation of the NetworkStack for the SPWF Device
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+#include "SpwfSAInterface.h"
+#include "mbed_debug.h"
+#include "BlockExecuter.h"
+
+#if MBED_CONF_RTOS_PRESENT
+static Mutex _spwf_mutex; // assuming a recursive mutex
+static void _spwf_lock() {
+    (void)(_spwf_mutex.lock());
+}
+static Callback<void()> _callback_spwf_lock(&_spwf_lock);
+
+static void _spwf_unlock() {
+    (void)(_spwf_mutex.unlock());
+}
+static Callback<void()> _callback_spwf_unlock(&_spwf_unlock);
+
+#define SYNC_HANDLER \
+        BlockExecuter sync_handler(_callback_spwf_unlock, _callback_spwf_lock)
+#else
+#define SYNC_HANDLER
+#endif
+
+
+SpwfSAInterface::SpwfSAInterface(PinName tx, PinName rx,
+                                 PinName rts, PinName cts, bool debug,
+                                 PinName wakeup, PinName reset)
+: _spwf(tx, rx, rts, cts, *this, debug, wakeup, reset),
+  _dbg_on(debug)
+{
+    inner_constructor();
+    reset_credentials();
+}
+
+nsapi_error_t SpwfSAInterface::init(void)
+{
+    _spwf.setTimeout(SPWF_INIT_TIMEOUT);
+
+    if(_spwf.startup(0)) {
+        return NSAPI_ERROR_OK;
+    }
+    else return NSAPI_ERROR_DEVICE_ERROR;
+}
+
+nsapi_error_t SpwfSAInterface::connect(void)
+{
+    int mode;
+    char *pass_phrase = ap_pass;
+    SYNC_HANDLER;
+
+    // check for valid SSID
+    if(ap_ssid[0] == '\0') {
+        return NSAPI_ERROR_PARAMETER;
+    }
+
+    switch(ap_sec)
+    {
+        case NSAPI_SECURITY_NONE:
+            mode = 0;
+            pass_phrase = NULL;
+            break;
+        case NSAPI_SECURITY_WEP:
+            mode = 1;
+            break;
+        case NSAPI_SECURITY_WPA:
+        case NSAPI_SECURITY_WPA2:
+            mode = 2;
+            break;
+        default:
+            mode = 2;
+            break;
+    }
+
+    // First: disconnect
+    if(_connected_to_network) {
+        if(!disconnect()) {
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+    }
+
+    //initialize the device before connecting
+    if(!_isInitialized)
+    {
+        if(init() != NSAPI_ERROR_OK) return NSAPI_ERROR_DEVICE_ERROR;
+        _isInitialized=true;
+    }
+
+    // Then: (re-)connect
+    _spwf.setTimeout(SPWF_CONNECT_TIMEOUT);
+
+    if (!_spwf.connect(ap_ssid, pass_phrase, mode)) {
+        return NSAPI_ERROR_AUTH_FAILURE;
+    }
+
+    if (!_spwf.getIPAddress()) {
+        return NSAPI_ERROR_DHCP_FAILURE;
+    }
+
+    _connected_to_network = true;
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_error_t SpwfSAInterface::connect(const char *ssid, const char *pass, nsapi_security_t security,
+                                       uint8_t channel)
+{
+    nsapi_error_t ret;
+    SYNC_HANDLER;
+
+    if (channel != 0) {
+        return NSAPI_ERROR_UNSUPPORTED;
+    }
+
+    ret = set_credentials(ssid, pass, security);
+    if(ret != NSAPI_ERROR_OK) return ret;
+
+    return connect();
+}
+
+nsapi_error_t SpwfSAInterface::disconnect(void)
+{
+    SYNC_HANDLER;
+
+    _spwf.setTimeout(SPWF_DISCONNECT_TIMEOUT);
+
+    if (!_spwf.disconnect()) {
+        return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    return NSAPI_ERROR_OK;
+}
+
+const char *SpwfSAInterface::get_ip_address(void)
+{
+    SYNC_HANDLER;
+
+    _spwf.setTimeout(SPWF_MISC_TIMEOUT);
+    return _spwf.getIPAddress();
+}
+
+const char *SpwfSAInterface::get_mac_address(void)
+{
+    SYNC_HANDLER;
+
+    _spwf.setTimeout(SPWF_MISC_TIMEOUT);
+    return _spwf.getMACAddress();
+}
+
+const char *SpwfSAInterface::get_gateway(void)
+{
+    SYNC_HANDLER;
+
+    if(!_connected_to_network) return NULL;
+
+    _spwf.setTimeout(SPWF_MISC_TIMEOUT);
+    return _spwf.getGateway();
+}
+
+const char *SpwfSAInterface::get_netmask(void)
+{
+    SYNC_HANDLER;
+
+    if(!_connected_to_network) return NULL;
+
+    _spwf.setTimeout(SPWF_MISC_TIMEOUT);
+    return _spwf.getNetmask();
+}
+
+nsapi_error_t SpwfSAInterface::socket_open(void **handle, nsapi_protocol_t proto)
+{
+    int internal_id;
+    SYNC_HANDLER;
+
+    for (internal_id = 0; internal_id < SPWFSA_SOCKET_COUNT; internal_id++) {
+        if(_ids[internal_id].internal_id == SPWFSA_SOCKET_COUNT) break;
+    }
+
+    if(internal_id == SPWFSA_SOCKET_COUNT) {
+        debug_if(_dbg_on, "NO Socket ID Error\r\n");
+        return NSAPI_ERROR_NO_SOCKET;
+    }
+
+    spwf_socket_t *socket = &_ids[internal_id];
+    socket->internal_id = internal_id;
+    socket->spwf_id = SPWFSA_SOCKET_COUNT;
+    socket->server_gone = false;
+    socket->no_more_data = false;
+    socket->proto = proto;
+    socket->addr = SocketAddress();
+
+    *handle = socket;
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_error_t SpwfSAInterface::socket_connect(void *handle, const SocketAddress &addr)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    SYNC_HANDLER;
+
+    MBED_ASSERT(((unsigned int)socket->internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT));
+
+    if(_socket_has_connected(socket->internal_id)) {
+        return NSAPI_ERROR_IS_CONNECTED;
+    }
+
+    _spwf.setTimeout(SPWF_OPEN_TIMEOUT);
+
+    const char *proto = (socket->proto == NSAPI_UDP) ? "u" : "t"; //"s" for secure socket?
+
+    if(addr.get_ip_version() != NSAPI_IPv4) { // IPv6 not supported (yet)
+        return NSAPI_ERROR_UNSUPPORTED;
+    }
+
+    {
+        BlockExecuter netsock_wa_obj(Callback<void()>(&_spwf, &SPWFSAxx::_unblock_event_callback),
+                                     Callback<void()>(&_spwf, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */
+
+        /* block asynchronous indications */
+        if(!_spwf._winds_off()) {
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+
+        {
+            BlockExecuter bh_handler(Callback<void()>(&_spwf, &SPWFSAxx::_execute_bottom_halves));
+            {
+                BlockExecuter winds_enabler(Callback<void()>(&_spwf, &SPWFSAxx::_winds_on));
+
+                if(!_spwf.open(proto, &socket->spwf_id, addr.get_ip_address(), addr.get_port())) {
+                    MBED_ASSERT(_spwf._call_event_callback_blocked == 1);
+
+                    return NSAPI_ERROR_DEVICE_ERROR;
+                }
+
+                /* check for the module to report a valid id */
+                MBED_ASSERT(((unsigned int)socket->spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT));
+
+                _internal_ids[socket->spwf_id] = socket->internal_id;
+                socket->addr = addr;
+
+                MBED_ASSERT(_spwf._call_event_callback_blocked == 1);
+
+                return NSAPI_ERROR_OK;
+            }
+        }
+    }
+}
+
+nsapi_error_t SpwfSAInterface::socket_bind(void *handle, const SocketAddress &address)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+nsapi_error_t SpwfSAInterface::socket_listen(void *handle, int backlog)
+{      
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+nsapi_error_t SpwfSAInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address)
+{    
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+nsapi_error_t SpwfSAInterface::socket_close(void *handle)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    int internal_id = socket->internal_id;
+    SYNC_HANDLER;
+
+    if(!_socket_is_open(internal_id)) return NSAPI_ERROR_NO_SOCKET;
+
+    if(_socket_has_connected(socket)) {
+        _spwf.setTimeout(SPWF_CLOSE_TIMEOUT);
+        if (!_spwf.close(socket->spwf_id)) {
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+        _internal_ids[socket->spwf_id] = SPWFSA_SOCKET_COUNT;
+    }
+
+    _ids[internal_id].internal_id = SPWFSA_SOCKET_COUNT;
+    _ids[internal_id].spwf_id = SPWFSA_SOCKET_COUNT;
+
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_size_or_error_t SpwfSAInterface::socket_send(void *handle, const void *data, unsigned size)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    SYNC_HANDLER;
+
+    CHECK_NOT_CONNECTED_ERR();
+
+    _spwf.setTimeout(SPWF_SEND_TIMEOUT);
+    return _spwf.send(socket->spwf_id, data, size, socket->internal_id);
+}
+
+nsapi_size_or_error_t SpwfSAInterface::socket_recv(void *handle, void *data, unsigned size)
+{
+    SYNC_HANDLER;
+
+    return _socket_recv(handle, data, size, false);
+}
+
+nsapi_size_or_error_t SpwfSAInterface::_socket_recv(void *handle, void *data, unsigned size, bool datagram)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+
+    CHECK_NOT_CONNECTED_ERR();
+
+    if(!_socket_has_connected(socket)) {
+        return NSAPI_ERROR_WOULD_BLOCK;
+    } else if(socket->no_more_data) {
+        return 0;
+    }
+
+    _spwf.setTimeout(SPWF_RECV_TIMEOUT);
+
+    int32_t recv = _spwf.recv(socket->spwf_id, (char*)data, (uint32_t)size, datagram);
+
+    MBED_ASSERT(!_spwf._is_event_callback_blocked());
+    MBED_ASSERT((recv != 0) || (size == 0));
+
+    if (recv < 0) {
+        if(!_socket_is_still_connected(socket)) {
+            socket->no_more_data = true;
+            return 0;
+        }
+
+        return NSAPI_ERROR_WOULD_BLOCK;
+    }
+
+    return recv;
+}
+
+nsapi_size_or_error_t SpwfSAInterface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    SYNC_HANDLER;
+
+    CHECK_NOT_CONNECTED_ERR();
+
+    if ((_socket_has_connected(socket)) && (socket->addr != addr)) {
+        _spwf.setTimeout(SPWF_CLOSE_TIMEOUT);
+        if (!_spwf.close(socket->spwf_id)) {
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+        _internal_ids[socket->spwf_id] = SPWFSA_SOCKET_COUNT;
+        socket->spwf_id = SPWFSA_SOCKET_COUNT;
+    }
+
+    _spwf.setTimeout(SPWF_CONN_SND_TIMEOUT);
+    if (!_socket_has_connected(socket)) {
+        nsapi_error_t err = socket_connect(socket, addr);
+        if (err < 0) {
+            return err;
+        }
+    }
+
+    return socket_send(socket, data, size);
+}
+
+nsapi_size_or_error_t SpwfSAInterface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    nsapi_error_t ret;
+    SYNC_HANDLER;
+
+    ret = _socket_recv(socket, data, size, true);
+    if (ret >= 0 && addr) {
+        *addr = socket->addr;
+    }
+
+    return ret;
+}
+
+void SpwfSAInterface::socket_attach(void *handle, void (*callback)(void *), void *data)
+{
+    spwf_socket_t *socket = (spwf_socket_t*)handle;
+    SYNC_HANDLER;
+
+    if(!_socket_is_open(socket)) return; // might happen e.g. after module hard fault or voluntary disconnection
+
+    _cbs[socket->internal_id].callback = callback;
+    _cbs[socket->internal_id].data = data;
+}
+
+void SpwfSAInterface::event(void) {
+    for (int internal_id = 0; internal_id < SPWFSA_SOCKET_COUNT; internal_id++) {
+        if (_cbs[internal_id].callback && (_ids[internal_id].internal_id != SPWFSA_SOCKET_COUNT)) {
+            _cbs[internal_id].callback(_cbs[internal_id].data);
+        }
+    }
+}
+
+nsapi_error_t SpwfSAInterface::set_credentials(const char *ssid, const char *pass, nsapi_security_t security)
+{
+    SYNC_HANDLER;
+
+    if((ssid == NULL) || (strlen(ssid) == 0)) {
+        return NSAPI_ERROR_PARAMETER;
+    }
+
+    if((pass != NULL) && (strlen(pass) > 0)) {
+        if(strlen(pass) < sizeof(ap_pass)) {
+            if(security == NSAPI_SECURITY_NONE) {
+                return NSAPI_ERROR_PARAMETER;
+            }
+        } else {
+            return NSAPI_ERROR_PARAMETER;
+        }
+    } else if(security != NSAPI_SECURITY_NONE) {
+        return NSAPI_ERROR_PARAMETER;
+    }
+
+    reset_credentials();
+
+    ap_sec = security;
+    strncpy(ap_ssid, ssid, sizeof(ap_ssid) - 1);
+    strncpy(ap_pass, pass, sizeof(ap_pass) - 1);
+
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_error_t SpwfSAInterface::set_channel(uint8_t channel)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+int8_t SpwfSAInterface::get_rssi(void)
+{
+    SYNC_HANDLER;
+
+    if(!_connected_to_network) return 0;
+
+    _spwf.setTimeout(SPWF_MISC_TIMEOUT);
+    return _spwf.getRssi();
+}
+
+nsapi_size_or_error_t SpwfSAInterface::scan(WiFiAccessPoint *res, unsigned count)
+{
+    SYNC_HANDLER;
+
+    nsapi_size_or_error_t ret;
+
+    //initialize the device before scanning
+    if(!_isInitialized)
+    {
+        if(init() != NSAPI_ERROR_OK) return NSAPI_ERROR_DEVICE_ERROR;
+    }
+
+    _spwf.setTimeout(SPWF_SCAN_TIMEOUT);
+
+    {
+        BlockExecuter netsock_wa_obj(Callback<void()>(&_spwf, &SPWFSAxx::_unblock_event_callback),
+                                     Callback<void()>(&_spwf, &SPWFSAxx::_block_event_callback)); /* disable calling (external) callback in IRQ context */
+
+        /* block asynchronous indications */
+        if(!_spwf._winds_off()) {
+            MBED_ASSERT(_spwf._call_event_callback_blocked == 1);
+
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+
+        ret = _spwf.scan(res, count);
+
+        /* unblock asynchronous indications */
+        _spwf._winds_on();
+    }
+
+    MBED_ASSERT(!_spwf._is_event_callback_blocked());
+
+    //de-initialize the device after scanning
+    if(!_isInitialized)
+    {
+        nsapi_error_t err = disconnect();
+        if(err != NSAPI_ERROR_OK) return err;
+    }
+
+    return ret;
+}
diff -r 000000000000 -r 79ce2b184a43 SpwfSAInterface.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SpwfSAInterface.h	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,439 @@
+/* mbed Microcontroller Library
+ * 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.
+ */
+
+/**
+  ******************************************************************************
+  * @file    SpwfSAInterface.h
+  * @author  STMicroelectronics
+  * @brief   Header file of the NetworkStack for the SPWF Device
+  ******************************************************************************
+  * @copy
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+  
+#ifndef SPWFSA_INTERFACE_H
+#define SPWFSA_INTERFACE_H
+
+#include <limits.h>
+
+#include "mbed.h"
+
+#define IDW01M1 1
+#define IDW04A1 2
+
+#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1
+#include "SPWFSA01/SPWFSA01.h"
+#elif MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1
+#include "SPWFSA04/SPWFSA04.h"
+#else
+#error No (valid) Wi-Fi exapnsion board defined (MBED_CONF_IDW0XX1_EXPANSION_BOARD: options are IDW01M1 and IDW04A1)
+#endif
+
+// Various timeouts for different SPWF operations
+#define SPWF_CONNECT_TIMEOUT    60000
+#define SPWF_DISCONNECT_TIMEOUT 30002
+#define SPWF_HF_TIMEOUT         30001
+#define SPWF_NETLOST_TIMEOUT    30000
+#define SPWF_READ_BIN_TIMEOUT   13000
+#define SPWF_CLOSE_TIMEOUT      10001
+#define SPWF_SEND_TIMEOUT       10000
+#define SPWF_INIT_TIMEOUT       8000
+#define SPWF_OPEN_TIMEOUT       5002
+#define SPWF_CONN_SND_TIMEOUT   5001
+#define SPWF_SCAN_TIMEOUT       5000
+#define SPWF_MISC_TIMEOUT       301
+#define SPWF_RECV_TIMEOUT       300
+
+/** SpwfSAInterface class
+ *  Implementation of the NetworkStack for the SPWF Device
+ */
+//  NOTE - betzw - TODO: MUST become singleton!
+class SpwfSAInterface : public NetworkStack, public WiFiInterface
+{
+public:
+    /** SpwfSAInterface constructor
+     * @param tx        TX pin
+     * @param rx        RX pin
+     * @param rts       RTS pin
+     * @param cts       CTS pin
+     * @param debug     Enable debugging
+     * @param wakeup    Wakeup pin
+     * @param reset     Reset pin
+     */
+    SpwfSAInterface(PinName tx, PinName rx,
+                    PinName rts = SPWFSAXX_RTS_PIN, PinName cts = SPWFSAXX_CTS_PIN, bool debug = false,
+                    PinName wakeup = SPWFSAXX_WAKEUP_PIN, PinName reset = SPWFSAXX_RESET_PIN);
+
+    /** Start the interface
+     *
+     *  Attempts to connect to a WiFi network. Requires ssid and passphrase to be set.
+     *  If passphrase is invalid, NSAPI_ERROR_AUTH_ERROR is returned.
+     *
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual nsapi_error_t connect();
+
+    /** Start the interface
+     *
+     *  Attempts to connect to a WiFi network.
+     *
+     *  @param ssid      Name of the network to connect to
+     *  @param pass      Security passphrase to connect to the network
+     *  @param security  Type of encryption for connection (Default: NSAPI_SECURITY_NONE)
+     *  @param channel   This parameter is not supported, setting it to anything else than 0 will result in NSAPI_ERROR_UNSUPPORTED
+     *  @return          0 on success, or error code on failure
+     */
+    virtual nsapi_error_t connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE,
+                                  uint8_t channel = 0);
+
+    /** Set the WiFi network credentials
+     *
+     *  @param ssid      Name of the network to connect to
+     *  @param pass      Security passphrase to connect to the network
+     *  @param security  Type of encryption for connection
+     *                   (defaults to NSAPI_SECURITY_NONE)
+     *  @return          0 on success, or error code on failure
+     */
+    virtual nsapi_error_t set_credentials(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE);
+
+    /** Set the WiFi network channel - NOT SUPPORTED
+     *
+     *  This function is not supported and will return NSAPI_ERROR_UNSUPPORTED
+     *
+     *  @param channel   Channel on which the connection is to be made, or 0 for any (Default: 0)
+     *  @return          Not supported, returns NSAPI_ERROR_UNSUPPORTED
+     */
+    virtual nsapi_error_t set_channel(uint8_t channel);
+
+    /** Stop the interface
+     *  @return             `NSAPI_ERROR_OK` on success, negative on failure
+     */
+    virtual nsapi_error_t disconnect();
+
+    /** Get the internally stored IP address
+     *  @return             IP address of the interface or null if not yet connected
+     */
+    virtual const char *get_ip_address();
+
+    /** Get the internally stored MAC address
+     *  @return             MAC address of the interface
+     */
+    virtual const char *get_mac_address();
+
+    /** Get the local gateway
+     *
+     *  @return         Null-terminated representation of the local gateway
+     *                  or null if no network mask has been received
+     */
+    virtual const char *get_gateway();
+
+    /** Get the local network mask
+     *
+     *  @return         Null-terminated representation of the local network mask
+     *                  or null if no network mask has been received
+     */
+    virtual const char *get_netmask();
+
+    /** Gets the current radio signal strength for active connection
+     *
+     * @return          Connection strength in dBm (negative value)
+     */
+    virtual int8_t get_rssi();
+
+    /** Scan for available networks
+     *
+     *  This function will block. If the @a count is 0, function will only return count of available networks, so that
+     *  user can allocated necessary memory. If the @count is grater than 0 and the @a ap is not NULL it'll be populated
+     *  with discovered networks up to value of @a count.
+     *
+     *  @param  res      Pointer to allocated array to store discovered AP
+     *  @param  count    Size of allocated @a res array, or 0 to only count available AP
+     *  @return          Number of entries in @a, or if @a count was 0 number of available networks,
+     *                   negative on error see @a nsapi_error
+     */
+    virtual nsapi_size_or_error_t scan(WiFiAccessPoint *res, unsigned count);
+
+    /** Translates a hostname to an IP address with specific version
+     *
+     *  The hostname may be either a domain name or an IP address. If the
+     *  hostname is an IP address, no network transactions will be performed.
+     *
+     *  If no stack-specific DNS resolution is provided, the hostname
+     *  will be resolve using a UDP socket on the stack.
+     *
+     *  @param address  Destination for the host SocketAddress
+     *  @param host     Hostname to resolve
+     *  @param version  IP version of address to resolve, NSAPI_UNSPEC indicates
+     *                  version is chosen by the stack (defaults to NSAPI_UNSPEC)
+     *  @return         0 on success, negative error code on failure
+     */
+    using NetworkInterface::gethostbyname;
+
+    /** Add a domain name server to list of servers to query
+     *
+     *  @param addr     Destination for the host address
+     *  @return         0 on success, negative error code on failure
+     */
+    using NetworkInterface::add_dns_server;
+
+private:
+    /** 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             `NSAPI_ERROR_OK` on success, negative on failure
+     */
+    virtual nsapi_error_t socket_open(void **handle, nsapi_protocol_t proto);
+
+    /** Close the socket
+     *  @param handle       Socket handle
+     *  @return             `NSAPI_ERROR_OK` on success, negative on failure
+     *  @note On failure, any memory associated with the socket must still
+     *        be cleaned up
+     */
+    virtual nsapi_error_t socket_close(void *handle);
+
+    /** Bind a server socket to a specific port - NOT SUPPORTED
+     *
+     *  This function is not supported and will return NSAPI_ERROR_UNSUPPORTED
+     *
+     *  @param handle       Socket handle
+     *  @param address      Local address to listen for incoming connections on
+     *  @return             Not supported, returns NSAPI_ERROR_UNSUPPORTED
+     */
+    virtual nsapi_error_t socket_bind(void *handle, const SocketAddress &address);
+
+    /** Start listening for incoming connections - NOT SUPPORTED
+     *
+     *  This function is not supported and will return NSAPI_ERROR_UNSUPPORTED
+     *
+     *  @param handle       Socket handle
+     *  @param backlog      Number of pending connections that can be queued up at any
+     *                      one time [Default: 1]
+     *  @return             Not supported, returns NSAPI_ERROR_UNSUPPORTED
+     */
+    virtual nsapi_error_t socket_listen(void *handle, int backlog);
+
+    /** Connects this TCP socket to the server
+     *  @param handle       Socket handle
+     *  @param address      SocketAddress to connect to
+     *  @return             `NSAPI_ERROR_OK` on success, negative on failure
+     */
+    virtual nsapi_error_t socket_connect(void *handle, const SocketAddress &address);
+
+    /** Accept a new connection - NOT SUPPORTED
+     *
+     *  This function is not supported and will return NSAPI_ERROR_UNSUPPORTED
+     *
+     *  @param handle       Handle in which to store new socket
+     *  @param server       Socket handle to server to accept from
+     *  @return             Not supported, returns NSAPI_ERROR_UNSUPPORTED
+     */
+    virtual nsapi_error_t socket_accept(void *handle, void **socket, SocketAddress *address);
+
+    /** Send data to the remote host
+     *  @param handle       Socket handle
+     *  @param data         The buffer to send to the host
+     *  @param size         The length of the buffer to send
+     *  @return             Number of written bytes on success, negative on failure
+     *  @note This call is not-blocking, if this call would block, must
+     *        immediately return NSAPI_ERROR_WOULD_BLOCK
+     */
+    virtual nsapi_size_or_error_t socket_send(void *handle, const void *data, unsigned size);
+
+    /** Receive data from the remote host
+     *  @param handle       Socket handle
+     *  @param data         The buffer in which to store the data received from the host
+     *  @param size         The maximum length of the buffer
+     *  @return             Number of received bytes on success, negative on failure
+     *  @note This call is not-blocking, if this call would block, must
+     *        immediately return NSAPI_ERROR_WOULD_BLOCK
+     */
+    virtual nsapi_size_or_error_t socket_recv(void *handle, void *data, unsigned size);
+
+    /** Send a packet to a remote endpoint
+     *  @param handle       Socket handle
+     *  @param address      The remote SocketAddress
+     *  @param data         The packet to be sent
+     *  @param size         The length of the packet to be sent
+     *  @return             The number of written bytes on success, negative on failure
+     *  @note This call is not-blocking, if this call would block, must
+     *        immediately return NSAPI_ERROR_WOULD_BLOCK
+     */
+    virtual nsapi_size_or_error_t socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size);
+
+    /** Receive a packet from a remote endpoint
+     *  @param handle       Socket handle
+     *  @param address      Destination for the remote SocketAddress or null
+     *  @param buffer       The buffer for storing the incoming packet data
+     *                      If a packet is too long to fit in the supplied buffer,
+     *                      excess bytes are discarded
+     *  @param size         The length of the buffer
+     *  @return             The number of received bytes on success, negative on failure
+     *  @note This call is not-blocking, if this call would block, must
+     *        immediately return NSAPI_ERROR_WOULD_BLOCK
+     */
+    virtual nsapi_size_or_error_t 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);
+
+    /** Provide access to the NetworkStack object
+     *
+     *  @return The underlying NetworkStack object
+     */
+    virtual NetworkStack *get_stack()
+    {
+        return this;
+    }
+
+private:
+    /** spwf_socket class
+     *  Implementation of SPWF socket structure
+     */
+    typedef struct spwf_socket {
+        int8_t internal_id;
+        int spwf_id;
+        bool server_gone;
+        bool no_more_data;
+        nsapi_protocol_t proto;
+        SocketAddress addr;
+    } spwf_socket_t;
+
+    bool _socket_is_open(spwf_socket_t *sock) {
+        if(((unsigned int)sock->internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) {
+            return (_ids[sock->internal_id].internal_id == sock->internal_id);
+        }
+        return false;
+    }
+
+    bool _socket_has_connected(spwf_socket_t *sock) {
+        return (_socket_is_open(sock) && (((unsigned int)sock->spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)));
+    }
+
+    bool _socket_is_still_connected(spwf_socket_t *sock) {
+        return (_socket_has_connected(sock) && !sock->server_gone);
+    }
+
+    bool _socket_is_open(int internal_id) {
+        if(((unsigned int)internal_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) {
+            return (_ids[internal_id].internal_id == internal_id);
+        }
+        return false;
+    }
+
+    bool _socket_has_connected(int internal_id) {
+        if(!_socket_is_open(internal_id)) return false;
+
+        spwf_socket_t &sock = _ids[internal_id];
+        return (sock.spwf_id != SPWFSA_SOCKET_COUNT);
+    }
+
+    bool _socket_is_still_connected(int internal_id) {
+        if(!_socket_has_connected(internal_id)) return false;
+
+        spwf_socket_t &sock = _ids[internal_id];
+        return (!sock.server_gone);
+    }
+
+    void reset_credentials() {
+        memset(ap_ssid, 0, sizeof(ap_ssid));
+        memset(ap_pass, 0, sizeof(ap_pass));
+        ap_sec = NSAPI_SECURITY_NONE;
+    }
+
+#if MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW01M1
+    SPWFSA01 _spwf;
+#elif MBED_CONF_IDW0XX1_EXPANSION_BOARD == IDW04A1
+    SPWFSA04 _spwf;
+#endif
+
+    bool _isInitialized;
+    bool _dbg_on;
+    bool _connected_to_network;
+
+    spwf_socket_t _ids[SPWFSA_SOCKET_COUNT];
+    struct {
+        void (*callback)(void *);
+        void *data;
+    } _cbs[SPWFSA_SOCKET_COUNT];
+    int _internal_ids[SPWFSA_SOCKET_COUNT];
+
+    char ap_ssid[33]; /* 32 is what 802.11 defines as longest possible name; +1 for the \0 */
+    nsapi_security_t ap_sec;
+    char ap_pass[64]; /* The longest allowed passphrase */
+
+private:
+    void event(void);
+    nsapi_error_t init(void);
+    nsapi_size_or_error_t _socket_recv(void *handle, void *data, unsigned size, bool datagram);
+
+
+    int get_internal_id(int spwf_id) { // checks also if `spwf_id` is (still) "valid"
+        if(((unsigned int)spwf_id) < ((unsigned int)SPWFSA_SOCKET_COUNT)) { // valid `spwf_id`
+            int internal_id = _internal_ids[spwf_id];
+            if((_socket_is_open(internal_id)) && (_ids[internal_id].spwf_id == spwf_id)) {
+                return internal_id;
+            } else {
+                return SPWFSA_SOCKET_COUNT;
+            }
+        } else { // invalid `spwf_id`
+            return SPWFSA_SOCKET_COUNT;
+        }
+    }
+
+    /* Called at initialization or after module hard fault */
+    void inner_constructor() {
+        memset(_ids, 0, sizeof(_ids));
+        memset(_cbs, 0, sizeof(_cbs));
+
+        for (int sock_cnt = 0; sock_cnt < SPWFSA_SOCKET_COUNT; sock_cnt++) {
+            _ids[sock_cnt].internal_id = SPWFSA_SOCKET_COUNT;
+            _ids[sock_cnt].spwf_id = SPWFSA_SOCKET_COUNT;
+            _internal_ids[sock_cnt] = SPWFSA_SOCKET_COUNT;
+        }
+
+        _spwf.attach(this, &SpwfSAInterface::event);
+
+        _connected_to_network = false;
+        _isInitialized = false;
+    }
+
+private:
+    friend class SPWFSAxx;
+    friend class SPWFSA01;
+    friend class SPWFSA04;
+};
+
+#define CHECK_NOT_CONNECTED_ERR() { \
+        if(!_connected_to_network) return NSAPI_ERROR_NO_CONNECTION; \
+} \
+
+
+#endif
diff -r 000000000000 -r 79ce2b184a43 mbed_app_idw01m1.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app_idw01m1.json	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,9 @@
+{
+    "target_overrides": {
+        "*": {
+            "idw0xx1.expansion-board": "IDW01M1",
+	    "drivers.uart-serial-txbuf-size": 512,
+	    "drivers.uart-serial-rxbuf-size": 512
+        }
+    }
+}
diff -r 000000000000 -r 79ce2b184a43 mbed_app_idw04a1.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app_idw04a1.json	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,10 @@
+{
+    "target_overrides": {
+        "*": {
+            "idw0xx1.expansion-board": "IDW04A1",
+	    "drivers.uart-serial-txbuf-size": 512,
+	    "drivers.uart-serial-rxbuf-size": 512
+        }
+    },
+    "macros": ["IDW04A1_WIFI_HW_BUG_WA"]
+}
diff -r 000000000000 -r 79ce2b184a43 mbed_lib.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_lib.json	Wed May 23 14:37:10 2018 +0000
@@ -0,0 +1,9 @@
+{
+    "name": "idw0xx1",
+    "config": {
+        "expansion-board":{
+	    "help": "Options are IDW01M1 and IDW04A1",
+	    "value": "IDW01M1"
+        }
+    }
+}