diff --git a/esp8266/Makefile b/esp8266/Makefile
index 1909e0d76e95f31895540b616224d396e49c01ba..389e86edf9d8d75251d2b4e6e5188d34c37918e4 100644
--- a/esp8266/Makefile
+++ b/esp8266/Makefile
@@ -14,6 +14,7 @@ INC =  -I.
 INC += -I..
 INC += -I../stmhal
 INC += -I../lib/mp-readline
+INC += -I../lib/netutils
 INC += -I$(BUILD)
 INC += -I$(ESP_SDK)/include
 
@@ -49,6 +50,7 @@ SRC_C = \
 	modpyb.c \
 	modpybpin.c \
 	modesp.c \
+	utils.c \
 
 STM_SRC_C = $(addprefix stmhal/,\
 	printf.c \
@@ -59,6 +61,7 @@ STM_SRC_C = $(addprefix stmhal/,\
 LIB_SRC_C = $(addprefix lib/,\
 	libc/string0.c \
 	mp-readline/readline.c \
+	netutils/netutils.c \
 	)
 
 SRC_S = \
diff --git a/esp8266/modesp.c b/esp8266/modesp.c
index bc1e6f9c75ae1d91dabf5995cbafc6fb1b363812..f5baadcbf73afbf37653e8fa1a91ee15958b1e90 100644
--- a/esp8266/modesp.c
+++ b/esp8266/modesp.c
@@ -33,28 +33,442 @@
 #include "py/gc.h"
 #include "py/runtime.h"
 #include MICROPY_HAL_H
+#include "netutils.h"
 #include "queue.h"
 #include "user_interface.h"
+#include "espconn.h"
+#include "utils.h"
 
-#define MODESP_INCLUDE_CONSTANTS (1)
+STATIC const mp_obj_type_t esp_socket_type;
 
-// Singleton instance of scan callback, meaning that there can be only
-// one concurrent AP scan.
-STATIC mp_obj_t scan_cb_obj;
+typedef struct _esp_socket_obj_t {
+    mp_obj_base_t base;
+    struct espconn *espconn;
 
-static void error_check(bool status, const char *msg) {
-    if (!status) {
-        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg));
+    mp_obj_t cb_connect;
+    mp_obj_t cb_recv;
+    mp_obj_t cb_disconnect;
+
+    uint8_t *recvbuf;
+    mp_uint_t recvbuf_len;
+
+    bool fromserver;
+
+    mp_obj_list_t *connlist;
+} esp_socket_obj_t;
+
+// Due to the onconnect callback not being able to recognize the parent esp_socket,
+// we can have only one esp_socket listening at a time
+// This should be solvable by some PIC hacking
+STATIC esp_socket_obj_t *esp_socket_listening;
+
+STATIC mp_obj_t esp_socket_make_new_base() {
+    esp_socket_obj_t *s = m_new_obj_with_finaliser(esp_socket_obj_t);
+    s->recvbuf = NULL;
+    s->base.type = (mp_obj_t)&esp_socket_type;
+    s->cb_connect = mp_const_none;
+    s->cb_recv = mp_const_none;
+    s->cb_disconnect = mp_const_none;
+    s->fromserver = false;
+    return s;
+}
+
+// constructor esp_socket(family=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP, fileno=None)
+// Arguments ignored as we do not support UDP (yet)
+STATIC mp_obj_t esp_socket_make_new(mp_obj_t type_in, mp_uint_t n_args,
+    mp_uint_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 4, false);
+
+    esp_socket_obj_t *s = esp_socket_make_new_base();
+    s->espconn = m_new_obj(struct espconn);
+    espconn_create(s->espconn);
+
+    s->espconn->reverse = s;
+    // TODO: UDP Support
+    s->espconn->type = ESPCONN_TCP;
+    s->espconn->state = ESPCONN_NONE;
+
+    s->espconn->proto.tcp = m_new_obj(esp_tcp);
+
+    return s;
+}
+
+// method socket.close()
+STATIC mp_obj_t esp_socket_close(mp_obj_t self_in) {
+    esp_socket_obj_t *s = self_in;
+
+    if (esp_socket_listening == s) {
+        esp_socket_listening = NULL;
+    }
+
+    if (s->espconn->state != ESPCONN_NONE && s->espconn->state != ESPCONN_CLOSE) {
+        espconn_disconnect(s->espconn);
+    }
+
+    if (s->connlist != NULL) {
+        mp_obj_list_set_len(s->connlist, 0);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_socket_close_obj, esp_socket_close);
+
+// method socket.__del__()
+STATIC mp_obj_t esp_socket___del__(mp_obj_t self_in) {
+    esp_socket_obj_t *s = self_in;
+
+    esp_socket_close(self_in);
+
+    if (s->fromserver) {
+        espconn_delete(s->espconn);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_socket___del___obj, esp_socket___del__);
+
+// method socket.bind(address)
+STATIC mp_obj_t esp_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
+    esp_socket_obj_t *s = self_in;
+
+    mp_uint_t port = netutils_parse_inet_addr(addr_in,
+        s->espconn->proto.tcp->remote_ip, NETUTILS_BIG);
+    s->espconn->proto.tcp->local_port = port;
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_bind_obj, esp_socket_bind);
+
+STATIC void esp_socket_recv_callback(void *arg, char *pdata, unsigned short len) {
+    struct espconn *conn = arg;
+    esp_socket_obj_t *s = conn->reverse;
+
+    if (s->cb_recv != mp_const_none) {
+        call_function_2_protected(s->cb_recv, s, mp_obj_new_bytes((byte *)pdata, len));
+    } else {
+        if (s->recvbuf == NULL) {
+            s->recvbuf = gc_alloc(len, false);
+            s->recvbuf_len = len;
+            if (s->recvbuf != NULL) {
+                memcpy(s->recvbuf, pdata, len);
+            }
+        } else {
+            s->recvbuf = gc_realloc(s->recvbuf, s->recvbuf_len + len);
+            if (s->recvbuf != NULL) {
+                memcpy(&s->recvbuf[s->recvbuf_len], pdata, len);
+                s->recvbuf_len += len;
+            }
+        }
+        if (s->recvbuf == NULL) {
+            esp_socket_close(s);
+            return;
+        }
+    }
+}
+
+STATIC void esp_socket_sent_callback(void *arg) {
+}
+
+STATIC void esp_socket_disconnect_callback(void *arg) {
+    struct espconn *conn = arg;
+    esp_socket_obj_t *s = conn->reverse;
+
+    if (s->cb_disconnect != mp_const_none) {
+        call_function_1_protected(s->cb_disconnect, s);
+    }
+
+    esp_socket_close(s);
+}
+
+STATIC void esp_socket_connect_callback_server(void *arg) {
+    struct espconn *conn = arg;
+
+    esp_socket_obj_t *s = esp_socket_make_new_base();
+    s->espconn = conn;
+    s->fromserver = true;
+    conn->reverse = s;
+
+    espconn_regist_recvcb(conn, esp_socket_recv_callback);
+    espconn_regist_sentcb(conn, esp_socket_sent_callback);
+    espconn_regist_disconcb(conn, esp_socket_disconnect_callback);
+    espconn_regist_time(conn, 15, 0);
+
+    if (esp_socket_listening->cb_connect != mp_const_none) {
+        call_function_1_protected(esp_socket_listening->cb_connect, s);
+    } else {
+        mp_obj_list_append(esp_socket_listening->connlist, s);
+    }
+}
+
+STATIC void esp_socket_connect_callback_client(void *arg) {
+    struct espconn *conn = arg;
+    esp_socket_obj_t *s = conn->reverse;
+
+    if (s->cb_connect != mp_const_none) {
+        call_function_1_protected(s->cb_connect, s);
+    }
+}
+
+// method socket.listen(backlog)
+STATIC mp_obj_t esp_socket_listen(mp_obj_t self_in, mp_obj_t backlog) {
+    esp_socket_obj_t *s = self_in;
+
+    if (esp_socket_listening != NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+            "only one espconn can listen at a time"));
+    }
+
+    esp_socket_listening = s;
+
+    s->connlist = mp_obj_new_list(0, NULL);
+
+    espconn_regist_connectcb(s->espconn, esp_socket_connect_callback_server);
+    espconn_accept(s->espconn);
+    espconn_regist_time(s->espconn, 1500, 0);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_listen_obj, esp_socket_listen);
+
+// method socket.accept()
+STATIC mp_obj_t esp_socket_accept(mp_obj_t self_in) {
+    esp_socket_obj_t *s = self_in;
+
+    if (s->connlist == NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+            "not listening"));
+    }
+
+    do {
+        mp_uint_t len;
+        mp_obj_t *items;
+
+        mp_obj_list_get(s->connlist, &len, &items);
+        if (len == 0) {
+            break;
+        }
+
+        esp_socket_obj_t *rs = items[0];
+        mp_obj_list_remove(s->connlist, rs);
+        if (rs->espconn->state != ESPCONN_CLOSE) {
+            return rs;
+        }
+    } while (true);
+
+    nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+        "no connection in queue"));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_socket_accept_obj, esp_socket_accept);
+
+// method socket.connect(address)
+STATIC mp_obj_t esp_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
+    esp_socket_obj_t *s = self_in;
+
+    if (s->espconn == NULL || s->espconn->state != ESPCONN_NONE) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+            "transport endpoint is already connected or closed"));
     }
+
+    espconn_regist_connectcb(s->espconn, esp_socket_connect_callback_client);
+    espconn_regist_recvcb(s->espconn, esp_socket_recv_callback);
+    espconn_regist_sentcb(s->espconn, esp_socket_sent_callback);
+    espconn_regist_disconcb(s->espconn, esp_socket_disconnect_callback);
+
+    s->espconn->proto.tcp->remote_port =
+        netutils_parse_inet_addr(addr_in, s->espconn->proto.tcp->remote_ip,
+            NETUTILS_BIG);
+    espconn_connect(s->espconn);
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_connect_obj, esp_socket_connect);
+
+// method socket.send(bytes)
+STATIC mp_obj_t esp_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
+    esp_socket_obj_t *s = self_in;
+
+    if (s->espconn->state == ESPCONN_NONE || s->espconn->state == ESPCONN_CLOSE) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+            "not connected"));
+    }
+
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+
+    espconn_sent(s->espconn, bufinfo.buf, bufinfo.len);
+
+    return mp_obj_new_int(bufinfo.len);
 }
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_send_obj, esp_socket_send);
+
+// method socket.recv(bufsize)
+STATIC mp_obj_t esp_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
+    esp_socket_obj_t *s = self_in;
 
-mp_obj_t call_function_1_protected(mp_obj_t fun, mp_obj_t arg) {
-    nlr_buf_t nlr;
-    if (nlr_push(&nlr) == 0) {
-        return mp_call_function_1(fun, arg);
+    if (s->recvbuf == NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
+            "no data available"));
+    }
+
+    mp_uint_t mxl = mp_obj_get_int(len_in);
+    if (mxl >= s->recvbuf_len) {
+        mp_obj_t trt = mp_obj_new_bytes(s->recvbuf, s->recvbuf_len);
+        gc_free(s->recvbuf);
+        s->recvbuf = NULL;
+        return trt;
     } else {
-        mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
-        return (mp_obj_t)nlr.ret_val;
+        mp_obj_t trt = mp_obj_new_bytes(s->recvbuf, mxl);
+        memmove(s->recvbuf, &s->recvbuf[mxl], s->recvbuf_len - mxl);
+        s->recvbuf_len -= mxl;
+        s->recvbuf = gc_realloc(s->recvbuf, s->recvbuf_len);
+        return trt;
+    }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_recv_obj, esp_socket_recv);
+
+// method socket.sendto(bytes, address)
+STATIC mp_obj_t esp_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) {
+    nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "UDP not supported"));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_socket_sendto_obj, esp_socket_sendto);
+
+// method socket.recvfrom(bufsize)
+STATIC mp_obj_t esp_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
+    nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "UDP not supported"));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_recvfrom_obj, esp_socket_recvfrom);
+
+STATIC mp_obj_t esp_socket_onconnect(mp_obj_t self_in, mp_obj_t lambda_in) {
+    esp_socket_obj_t *s = self_in;
+    s->cb_connect = lambda_in;
+
+    if (s->connlist != NULL) {
+        do {
+            mp_uint_t len;
+            mp_obj_t *items;
+
+            mp_obj_list_get(s->connlist, &len, &items);
+            if (len == 0) {
+                break;
+            }
+
+            esp_socket_obj_t *rs = items[0];
+            mp_obj_list_remove(s->connlist, rs);
+            if (s->espconn->state != ESPCONN_CLOSE) {
+                call_function_1_protected(s->cb_connect, rs);
+            }
+        } while (true);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_onconnect_obj, esp_socket_onconnect);
+
+STATIC mp_obj_t esp_socket_onrecv(mp_obj_t self_in, mp_obj_t lambda_in) {
+    esp_socket_obj_t *s = self_in;
+    s->cb_recv = lambda_in;
+    if (s->recvbuf != NULL) {
+        call_function_2_protected(s->cb_recv, s,
+            mp_obj_new_bytes((byte *)s->recvbuf, s->recvbuf_len));
+        s->recvbuf = NULL;
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_onrecv_obj, esp_socket_onrecv);
+
+STATIC mp_obj_t esp_socket_ondisconnect(mp_obj_t self_in, mp_obj_t lambda_in) {
+    esp_socket_obj_t *s = self_in;
+    s->cb_disconnect = lambda_in;
+
+    if (s->espconn->state == ESPCONN_CLOSE) {
+        call_function_1_protected(s->cb_disconnect, s);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_ondisconnect_obj, esp_socket_ondisconnect);
+
+typedef struct _esp_getaddrinfo_cb_struct_t {
+    mp_obj_t lambda;
+    mp_uint_t port;
+} esp_getaddrinfo_cb_struct_t;
+
+STATIC esp_getaddrinfo_cb_struct_t esp_getaddrinfo_cb_struct;
+
+STATIC void esp_getaddrinfo_cb(const char *name, ip_addr_t *ipaddr, void *arg) {
+    mp_obj_t namestr = mp_obj_new_str(name, strlen(name), true);
+    if (ipaddr != NULL) {
+        uint8_t ip[4];
+        ip[0] = (ipaddr->addr >> 24) & 0xff;
+        ip[1] = (ipaddr->addr >> 16) & 0xff;
+        ip[2] = (ipaddr->addr >>  8) & 0xff;
+        ip[3] = (ipaddr->addr >>  0) & 0xff;
+
+        mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL);
+
+        tuple->items[0] = MP_OBJ_NEW_SMALL_INT(0);
+        tuple->items[1] = MP_OBJ_NEW_SMALL_INT(0);
+        tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
+        tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
+        tuple->items[4] = netutils_format_inet_addr(ip,
+            esp_getaddrinfo_cb_struct.port, NETUTILS_BIG);
+        call_function_2_protected(esp_getaddrinfo_cb_struct.lambda, namestr, tuple);
+    } else {
+        call_function_2_protected(esp_getaddrinfo_cb_struct.lambda, namestr, mp_const_none);
+    }
+}
+
+STATIC mp_obj_t esp_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in,
+    mp_obj_t lambda_in) {
+    mp_uint_t hlen;
+    const char *host = mp_obj_str_get_data(host_in, &hlen);
+    ip_addr_t ipaddr;
+
+    esp_getaddrinfo_cb_struct.lambda = lambda_in;
+    esp_getaddrinfo_cb_struct.port = mp_obj_get_int(port_in);
+
+    err_t ret = espconn_gethostbyname(NULL, host, &ipaddr,
+        esp_getaddrinfo_cb);
+
+    if (ret == ESPCONN_OK) {
+        esp_getaddrinfo_cb(host, &ipaddr, NULL);
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_getaddrinfo_obj, esp_getaddrinfo);
+
+STATIC const mp_map_elem_t esp_socket_locals_dict_table[] = {
+    { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&esp_socket___del___obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&esp_socket_close_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&esp_socket_bind_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&esp_socket_listen_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&esp_socket_accept_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&esp_socket_connect_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&esp_socket_send_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&esp_socket_recv_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&esp_socket_sendto_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&esp_socket_recvfrom_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_onconnect), (mp_obj_t)&esp_socket_onconnect_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_onrecv), (mp_obj_t)&esp_socket_onrecv_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_ondisconnect), (mp_obj_t)&esp_socket_ondisconnect_obj },
+};
+STATIC MP_DEFINE_CONST_DICT(esp_socket_locals_dict, esp_socket_locals_dict_table);
+
+STATIC const mp_obj_type_t esp_socket_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_socket,
+    .make_new = esp_socket_make_new,
+    .locals_dict = (mp_obj_t)&esp_socket_locals_dict,
+};
+
+#define MODESP_INCLUDE_CONSTANTS (1)
+
+static void error_check(bool status, const char *msg) {
+    if (!status) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg));
     }
 }
 
@@ -70,13 +484,13 @@ STATIC void esp_scan_cb(scaninfo *si, STATUS status) {
             t->items[3] = MP_OBJ_NEW_SMALL_INT(bs->rssi);
             t->items[4] = MP_OBJ_NEW_SMALL_INT(bs->authmode);
             t->items[5] = MP_OBJ_NEW_SMALL_INT(bs->is_hidden);
-            call_function_1_protected(scan_cb_obj, t);
+            call_function_1_protected(MP_STATE_PORT(scan_cb_obj), t);
         }
     }
 }
 
 STATIC mp_obj_t esp_scan(mp_obj_t cb_in) {
-    scan_cb_obj = cb_in;
+    MP_STATE_PORT(scan_cb_obj) = cb_in;
     wifi_set_opmode(STATION_MODE);
     wifi_station_scan(NULL, (scan_done_cb_t)esp_scan_cb);
     return mp_const_none;
@@ -118,6 +532,8 @@ STATIC const mp_map_elem_t esp_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&esp_disconnect_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&esp_scan_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_status), (mp_obj_t)&esp_status_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&esp_getaddrinfo_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&esp_socket_type },
 
 #if MODESP_INCLUDE_CONSTANTS
     { MP_OBJ_NEW_QSTR(MP_QSTR_STAT_IDLE),
diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h
index dcd9407f8293c1c24a2f2ef44fdf2336afe8547e..cae5398f971aec4dcf6dfe68ea1654254f6eea64 100644
--- a/esp8266/mpconfigport.h
+++ b/esp8266/mpconfigport.h
@@ -71,7 +71,11 @@ extern const struct _mp_obj_module_t esp_module;
 #define MP_STATE_PORT MP_STATE_VM
 
 #define MICROPY_PORT_ROOT_POINTERS \
-    const char *readline_hist[8];
+    const char *readline_hist[8]; \
+    \
+    /* Singleton instance of scan callback, meaning that there can
+       be only one concurrent AP scan. */ \
+    mp_obj_t scan_cb_obj; \
 
 // We need to provide a declaration/definition of alloca()
 #include <alloca.h>
diff --git a/esp8266/qstrdefsport.h b/esp8266/qstrdefsport.h
index 26fce54de7cbac7bc074d6bd652b4a7565ea62bf..fb064095e416e524b2d5f0d0eadb420f3a7c022c 100644
--- a/esp8266/qstrdefsport.h
+++ b/esp8266/qstrdefsport.h
@@ -42,10 +42,27 @@ Q(sync)
 Q(hard_reset)
 
 Q(esp)
+Q(socket)
 Q(connect)
 Q(disconnect)
 Q(scan)
 Q(status)
+Q(getaddrinfo)
+Q(send)
+Q(sendto)
+Q(recv)
+Q(recvfrom)
+Q(listen)
+Q(accept)
+Q(bind)
+Q(settimeout)
+Q(setblocking)
+Q(setsockopt)
+Q(close)
+Q(protocol)
+Q(onconnect)
+Q(onrecv)
+Q(ondisconnect)
 Q(STAT_IDLE)
 Q(STAT_CONNECTING)
 Q(STAT_WRONG_PASSWORD)
diff --git a/esp8266/utils.c b/esp8266/utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..e91ebe318deef13b78275559a607ec2b2ffb5311
--- /dev/null
+++ b/esp8266/utils.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Josef Gajdusek
+ * Copyright (c) 2015 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/runtime.h"
+#include "py/obj.h"
+#include "py/nlr.h"
+
+mp_obj_t call_function_1_protected(mp_obj_t fun, mp_obj_t arg) {
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        return mp_call_function_1(fun, arg);
+    } else {
+        mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
+        return (mp_obj_t)nlr.ret_val;
+    }
+}
+
+mp_obj_t call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        return mp_call_function_2(fun, arg1, arg2);
+    } else {
+        mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
+        return (mp_obj_t)nlr.ret_val;
+    }
+}
diff --git a/esp8266/utils.h b/esp8266/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..ceef9720e884b6539faef5f9a1d768dc68a50f50
--- /dev/null
+++ b/esp8266/utils.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Josef Gajdusek
+ * Copyright (c) 2015 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+mp_obj_t call_function_1_protected(mp_obj_t fun, mp_obj_t arg);
+mp_obj_t call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);