diff --git a/.travis.yml b/.travis.yml
index b22d9bb..dfb0fda 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,12 +2,13 @@ language: c
 sudo: false
 env:
   # Target commit for https://github.com/pfalcon/esp-open-sdk/
-  OPENSDK_COMMIT=cd1d336
+  OPENSDK_COMMIT=9bd3aba
   CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
   CROSS_BINDIR="${CROSS_ROOT}/bin"
   ESPTOOL2_COMMIT=ec0e2c7
   ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
   PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
+  MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples CROSS=\"ccache xtensa-lx106-elf-\""
 cache:
   directories:
     - ${CROSS_ROOT}
@@ -57,5 +58,7 @@ before_install:
 script:
   - cd ${TRAVIS_BUILD_DIR}
   # Remove ssid_config requirement for examples
-  - sed -i "s%#warning%//#warning%" include/ssid_config.h
-  - make -C examples/ build-examples CROSS="ccache xtensa-lx106-elf-" V=1
+  - sed -i "s%#error%//#error%" include/ssid_config.h
+  # Try building everything w/ non-verbose output (to avoid 4MB log limit), but if it fails then
+  # rerun with V=1 so the error part is output verbosely
+  - ${MAKE_CMD} || ( ${MAKE_CMD} V=1 )
diff --git a/FreeRTOS/Source/portable/esp8266/port.c b/FreeRTOS/Source/portable/esp8266/port.c
index fc452f0..21a0d55 100644
--- a/FreeRTOS/Source/portable/esp8266/port.c
+++ b/FreeRTOS/Source/portable/esp8266/port.c
@@ -178,9 +178,6 @@ void xPortSysTickHandle (void)
 	//OpenNMI();
 }
 
-static bool sdk_compat_initialised;
-void sdk_compat_initialise(void);
-
 /*
  * See header file for description.
  */
@@ -189,14 +186,6 @@ portBASE_TYPE xPortStartScheduler( void )
     _xt_isr_attach(INUM_SOFT, SV_ISR);
     _xt_isr_unmask(BIT(INUM_SOFT));
 
-    /* ENORMOUS HACK: Call the sdk_compat_initialise() function.
-       This can be removed happily once we have open source startup code.
-    */
-    if(!sdk_compat_initialised) {
-        sdk_compat_initialised = true;
-        sdk_compat_initialise();
-    }
-
     /* Initialize system tick timer interrupt and schedule the first tick. */
     _xt_isr_attach(INUM_TICK, sdk__xt_timer_int);
     _xt_isr_unmask(BIT(INUM_TICK));
diff --git a/README.md b/README.md
index f709216..50edf61 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Please note that this project is released with a [Contributor Code of Conduct](h
 
 ## Quick Start
 
-* Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/), build it with `make STANDALONE=n`, then edit your PATH and add the generated toolchain `bin` directory. The path will be something like `/path/to/esp-open-sdk/xtensa-lx106-elf/bin`. (Despite the similar name esp-open-sdk has different maintainers - but we think it's fantastic!)
+* Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/), build it with `make toolchain esptool libhal STANDALONE=n`, then edit your PATH and add the generated toolchain `bin` directory. The path will be something like `/path/to/esp-open-sdk/xtensa-lx106-elf/bin`. (Despite the similar name esp-open-sdk has different maintainers - but we think it's fantastic!)
 
     (Other toolchains may also work, as long as a gcc cross-compiler is available on the PATH and libhal (and libhal headers) are compiled and available to gcc. The proprietary Tensilica "xcc" compiler will probably not work.)
 
diff --git a/common.mk b/common.mk
index 7706a15..a9ecd69 100644
--- a/common.mk
+++ b/common.mk
@@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
 # Compiler names, etc. assume gdb
 CROSS ?= xtensa-lx106-elf-
 
+# Path to the filteroutput.py tool
+FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
+
 AR = $(CROSS)ar
 CC = $(CROSS)gcc
 CPP = $(CROSS)cpp
@@ -106,8 +109,14 @@ ENTRY_SYMBOL ?= call_user_start
 # but compiled code size will come down a small amount.)
 SPLIT_SECTIONS ?= 1
 
+# Set this to 1 to have all compiler warnings treated as errors (and stop the
+# build).  This is recommended whenever you are working on code which will be
+# submitted back to the main project, as all submitted code will be expected to
+# compile without warnings to be accepted.
+WARNINGS_AS_ERRORS ?= 0
+
 # Common flags for both C & C++_
-C_CXX_FLAGS     ?= -Wall -Werror -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
+C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
 # Flags for C only
 CFLAGS		?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
 # Flags for C++ only
@@ -118,6 +127,10 @@ CPPFLAGS	+= -mlongcalls -mtext-section-literals
 
 LDFLAGS		= -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
 
+ifeq ($(WARNINGS_AS_ERRORS),1)
+    C_CXX_FLAGS += -Werror
+endif
+
 ifeq ($(SPLIT_SECTIONS),1)
   C_CXX_FLAGS += -ffunction-sections -fdata-sections
   LDFLAGS += -Wl,-gc-sections
@@ -375,7 +388,7 @@ size: $(PROGRAM_OUT)
 	$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
 
 test: flash
-	screen $(ESPPORT) 115200
+	$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
 
 # the rebuild target is written like this so it can be run in a parallel build
 # environment without causing weird side effects
diff --git a/core/app_main.c b/core/app_main.c
index 631b2bb..5e02667 100644
--- a/core/app_main.c
+++ b/core/app_main.c
@@ -41,8 +41,6 @@ void user_init(void);
 #define RTCMEM_BACKUP_PHY_VER  31
 #define RTCMEM_SYSTEM_PP_VER   62
 
-#define halt()  while (1) {}
-
 extern uint32_t _bss_start;
 extern uint32_t _bss_end;
 
@@ -85,7 +83,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor);
 static void zero_bss(void);
 static void init_networking(uint8_t *phy_info, uint8_t *mac_addr);
 static void init_g_ic(void);
-static void dump_excinfo(void);
 static void user_start_phase2(void);
 static void dump_flash_sector(uint32_t start_sector, uint32_t length);
 static void dump_flash_config_sectors(uint32_t start_sector);
@@ -102,11 +99,11 @@ static void IRAM get_otp_mac_address(uint8_t *buf) {
     if (!(otp_flags & 0x8000)) {
         //FIXME: do we really need this check?
         printf("Firmware ONLY supports ESP8266!!!\n");
-        halt();
+        abort();
     }
     if (otp_id0 == 0 && otp_id1 == 0) {
         printf("empty otp\n");
-        halt();
+        abort();
     }
     if (otp_flags & 0x1000) {
         // If bit 12 is set, it indicates that the vendor portion of the MAC
@@ -147,23 +144,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor) {
     SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
 }
 
-// .text+0x148
-void IRAM sdk_user_fatal_exception_handler(void) {
-    if (!sdk_NMIIrqIsOn) {
-        vPortEnterCritical();
-        do {
-            DPORT.DPORT0 &= 0xffffffe0;
-        } while (DPORT.DPORT0 & 0x00000001);
-    }
-    Cache_Read_Disable();
-    Cache_Read_Enable(0, 0, 1);
-    dump_excinfo();
-    uart_flush_txfifo(0);
-    uart_flush_txfifo(1);
-    sdk_system_restart_in_nmi();
-    halt();
-}
-
 
 static void IRAM default_putc(char c) {
     uart_putc(0, c);
@@ -277,7 +257,7 @@ static void zero_bss(void) {
 static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) {
     if (sdk_register_chipv6_phy(phy_info)) {
         printf("FATAL: sdk_register_chipv6_phy failed");
-        halt();
+        abort();
     }
     uart_set_baud(0, 74906);
     uart_set_baud(1, 74906);
@@ -333,37 +313,6 @@ static void init_g_ic(void) {
     }
 }
 
-// .Lfunc008 -- .irom0.text+0x2a0
-static void dump_excinfo(void) {
-    uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
-    uint32_t excinfo[8];
-
-    RSR(exccause, exccause);
-    printf("Fatal exception (%d): \n", (int)exccause);
-    RSR(epc1, epc1);
-    RSR(epc2, epc2);
-    RSR(epc3, epc3);
-    RSR(excvaddr, excvaddr);
-    RSR(depc, depc);
-    RSR(excsave1, excsave1);
-    printf("%s=0x%08x\n", "epc1", epc1);
-    printf("%s=0x%08x\n", "epc2", epc2);
-    printf("%s=0x%08x\n", "epc3", epc3);
-    printf("%s=0x%08x\n", "excvaddr", excvaddr);
-    printf("%s=0x%08x\n", "depc", depc);
-    printf("%s=0x%08x\n", "excsave1", excsave1);
-    sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
-    excinfo[0] = 2;
-    excinfo[1] = exccause;
-    excinfo[2] = epc1;
-    excinfo[3] = epc2;
-    excinfo[4] = epc3;
-    excinfo[5] = excvaddr;
-    excinfo[6] = depc;
-    excinfo[7] = excsave1;
-    sdk_system_rtc_mem_write(0, excinfo, 32);
-}
-
 // .irom0.text+0x398
 void sdk_wdt_init(void) {
     WDT.CTRL &= ~WDT_CTRL_ENABLE;
@@ -408,6 +357,9 @@ void sdk_user_init_task(void *params) {
     vTaskDelete(NULL);
 }
 
+extern void (*__init_array_start)(void);
+extern void (*__init_array_end)(void);
+
 // .Lfunc009 -- .irom0.text+0x5b4
 static void user_start_phase2(void) {
     uint8_t *buf;
@@ -445,6 +397,13 @@ static void user_start_phase2(void) {
     }
     init_networking(phy_info, sdk_info.sta_mac_addr);
     free(phy_info);
+
+    // Call gcc constructor functions
+    void (**ctor)(void);
+    for ( ctor = &__init_array_start; ctor != &__init_array_end; ++ctor) {
+        (*ctor)();
+    }
+
     tcpip_init(NULL, NULL);
     sdk_wdt_init();
     xTaskCreate(sdk_user_init_task, (signed char *)"uiT", 1024, 0, 14, &sdk_xUserTaskHandle);
diff --git a/core/debug_dumps.c b/core/debug_dumps.c
new file mode 100644
index 0000000..15396ac
--- /dev/null
+++ b/core/debug_dumps.c
@@ -0,0 +1,175 @@
+/* Code for dumping status/debug output/etc, including fatal
+ * exception handling & abort implementation.
+ *
+ * Part of esp-open-rtos
+ *
+ * Partially reverse engineered from MIT licensed Espressif RTOS SDK Copyright (C) Espressif Systems.
+ * Additions Copyright (C) 2015 Superhouse Automation Pty Ltd
+ * BSD Licensed as described in the file LICENSE
+ */
+#include <string.h>
+#include <stdio.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+#include "debug_dumps.h"
+#include "common_macros.h"
+#include "xtensa_ops.h"
+#include "esp/rom.h"
+#include "esp/uart.h"
+#include "espressif/esp_common.h"
+#include "sdk_internal.h"
+
+/* Forward declarations */
+static void IRAM fatal_handler_prelude(void);
+/* Inner parts of crash handlers marked noinline to ensure they don't inline into IRAM. */
+static void __attribute__((noinline)) __attribute__((noreturn)) fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
+static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
+
+/* fatal_exception_handler called from any unhandled user exception
+ *
+ * (similar to a hard fault on other processor architectures)
+ *
+ * This function is run from IRAM, but the majority of the handler
+ * runs from flash after fatal_handler_prelude ensures it is mapped
+ * safely.
+ */
+void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) {
+    fatal_handler_prelude();
+    fatal_exception_handler_inner(sp, registers_saved_on_stack);
+}
+
+/* Abort implementation
+ *
+ * Replaces the weak-linked abort implementation provided by newlib libc.
+ *
+ * Disable interrupts, enable flash mapping, dump stack & caller
+ * address, restart.
+ *
+ * This function is run from IRAM, but the majority of the abort
+ * handler runs from flash after fatal_handler_prelude ensures it is
+ * mapped safely.
+ *
+ */
+void IRAM abort(void) {
+    uint32_t *sp, *caller;
+    RETADDR(caller);
+    /* abort() caller is one instruction before our return address */
+    caller = (uint32_t *)((intptr_t)caller - 3);
+    SP(sp);
+    fatal_handler_prelude();
+    abort_handler_inner(caller, sp);
+}
+
+/* Dump exception information from special function registers */
+static void dump_excinfo(void) {
+    uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
+    uint32_t excinfo[8];
+
+    RSR(exccause, exccause);
+    printf("Fatal exception (%d): \n", (int)exccause);
+    RSR(epc1, epc1);
+    RSR(epc2, epc2);
+    RSR(epc3, epc3);
+    RSR(excvaddr, excvaddr);
+    RSR(depc, depc);
+    RSR(excsave1, excsave1);
+    printf("%s=0x%08x\n", "epc1", epc1);
+    printf("%s=0x%08x\n", "epc2", epc2);
+    printf("%s=0x%08x\n", "epc3", epc3);
+    printf("%s=0x%08x\n", "excvaddr", excvaddr);
+    printf("%s=0x%08x\n", "depc", depc);
+    printf("%s=0x%08x\n", "excsave1", excsave1);
+    sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
+    excinfo[0] = 2;
+    excinfo[1] = exccause;
+    excinfo[2] = epc1;
+    excinfo[3] = epc2;
+    excinfo[4] = epc3;
+    excinfo[5] = excvaddr;
+    excinfo[6] = depc;
+    excinfo[7] = excsave1;
+    sdk_system_rtc_mem_write(0, excinfo, 32);
+}
+
+/* dump stack memory (frames above sp) to stdout
+
+   There's a lot of smart stuff we could do while dumping stack
+   but for now we just dump what looks like our stack region.
+
+   Probably dumps more memory than it needs to, the first instance of
+   0xa5a5a5a5 probably constitutes the end of our stack.
+*/
+void dump_stack(uint32_t *sp) {
+    printf("\nStack: SP=%p\n", sp);
+    for(uint32_t *p = sp; p < sp + 32; p += 4) {
+        if((intptr_t)p >= 0x3fffc000) {
+            break; /* approximate end of RAM */
+        }
+        printf("%p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
+        if(p[0] == 0xa5a5a5a5 && p[1] == 0xa5a5a5a5
+           && p[2] == 0xa5a5a5a5 && p[3] == 0xa5a5a5a5) {
+            break; /* FreeRTOS uses this pattern to mark untouched stack space */
+        }
+    }
+}
+
+/* Dump normal registers that were stored above 'sp'
+   by the exception handler preamble
+*/
+void dump_registers_in_exception_handler(uint32_t *sp) {
+    uint32_t excsave1;
+    uint32_t *saved = sp - (0x50 / sizeof(uint32_t));
+    printf("Registers:\n");
+    RSR(excsave1, excsave1);
+    printf("a0 %08x ", excsave1);
+    printf("a1 %08x ", (intptr_t)sp);
+    for(int a = 2; a < 14; a++) {
+        printf("a%-2d %08x%c", a, saved[a+3], a == 3 || a == 7 || a == 11 ? '\n':' ');
+    }
+    printf("SAR %08x\n", saved[0x13]);
+}
+
+
+/* Prelude ensures exceptions/NMI off and flash is mapped, allowing
+   calls to non-IRAM functions.
+*/
+static void IRAM fatal_handler_prelude(void) {
+    if (!sdk_NMIIrqIsOn) {
+        vPortEnterCritical();
+        do {
+            DPORT.DPORT0 &= 0xffffffe0;
+        } while (DPORT.DPORT0 & 0x00000001);
+    }
+    Cache_Read_Disable();
+    Cache_Read_Enable(0, 0, 1);
+}
+
+/* Main part of fatal exception handler, is run from flash to save
+   some IRAM.
+*/
+static void fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
+    dump_excinfo();
+    if (sp) {
+        if (registers_saved_on_stack) {
+            dump_registers_in_exception_handler(sp);
+        }
+        dump_stack(sp);
+    }
+    uart_flush_txfifo(0);
+    uart_flush_txfifo(1);
+    sdk_system_restart_in_nmi();
+    while(1) {}
+}
+
+/* Main part of abort handler, can be run from flash to save some
+   IRAM.
+*/
+static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
+    printf("abort() invoked at %p.\n", caller);
+    dump_stack(sp);
+    uart_flush_txfifo(0);
+    uart_flush_txfifo(1);
+    sdk_system_restart_in_nmi();
+    while(1) {}
+}
diff --git a/core/esp_interrupts.c b/core/esp_interrupts.c
index 4fd68b7..b3aefff 100644
--- a/core/esp_interrupts.c
+++ b/core/esp_interrupts.c
@@ -9,6 +9,8 @@
 
 _xt_isr isr[16];
 
+bool esp_in_isr;
+
 void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
 {
     isr[i] = func;
@@ -20,6 +22,8 @@ void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
 */
 uint16_t IRAM _xt_isr_handler(uint16_t intset)
 {
+    esp_in_isr = true;
+
     /* WDT has highest priority (occasional WDT resets otherwise) */
     if(intset & BIT(INUM_WDT)) {
         _xt_clear_ints(BIT(INUM_WDT));
@@ -35,5 +39,7 @@ uint16_t IRAM _xt_isr_handler(uint16_t intset)
         intset -= mask;
     }
 
+    esp_in_isr = false;
+
     return 0;
 }
diff --git a/core/exception_vectors.S b/core/exception_vectors.S
index c1f1761..000e742 100644
--- a/core/exception_vectors.S
+++ b/core/exception_vectors.S
@@ -68,7 +68,9 @@ DebugExceptionVector:
         .type   DebugExceptionVector, @function
 
         wsr     a0, excsave2
-        call0   sdk_user_fatal_exception_handler
+        mov     a2, a1
+        movi    a3, 0
+        call0   fatal_exception_handler
         rfi     2
 
         .org    VecBase + 0x20
@@ -81,7 +83,9 @@ KernelExceptionVector:
         .type   KernelExceptionVector, @function
 
         break   1, 0
-        call0   sdk_user_fatal_exception_handler
+        mov     a2, a1
+        movi    a3, 0
+        call0   fatal_exception_handler
         rfe
 
         .org    VecBase + 0x50
@@ -98,7 +102,9 @@ DoubleExceptionVector:
         .type   DoubleExceptionVector, @function
 
         break   1, 4
-        call0   sdk_user_fatal_exception_handler
+        mov     a2, a1
+        movi    a3, 0
+        call0   fatal_exception_handler
 
 /* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
  * vectors on chip reset. */
@@ -251,11 +257,13 @@ LoadStoreErrorHandler:
          * will have correct values */
         wsr     a0, sar
         l32i    a0, sp, 0
-        l32i    a2, sp, 0x08
+        /*l32i    a2, sp, 0x08*/
         l32i    a3, sp, 0x0c
         l32i    a4, sp, 0x10
         rsr     a1, excsave1
-        call0   sdk_user_fatal_exception_handler
+        mov     a2, a1
+        movi    a3, 0
+        call0   fatal_exception_handler
 
         .balign 4
 .LSE_assign_a1:
@@ -515,7 +523,9 @@ UserExceptionHandler:
         .literal_position
 .LUserFailOtherExceptionCause:
         break   1, 1
-        call0   sdk_user_fatal_exception_handler
+        addi    a2, a1, 0x50 /* UserExceptionHandler pushes stack down 0x50 */
+        movi    a3, 1
+        call0   fatal_exception_handler
 
 /* _xt_user_exit is pushed onto the stack as part of the user exception handler,
    restores same set registers which were saved there and returns from exception */
diff --git a/core/include/debug_dumps.h b/core/include/debug_dumps.h
new file mode 100644
index 0000000..809b87a
--- /dev/null
+++ b/core/include/debug_dumps.h
@@ -0,0 +1,22 @@
+/* Functions for dumping status/debug output/etc, including fatal
+ * exception handling.
+ *
+ * Part of esp-open-rtos
+ *
+ * Copyright (C) 2015-2016 Superhouse Automation Pty Ltd
+ * BSD Licensed as described in the file LICENSE
+ */
+#ifndef _DEBUG_DUMPS_H
+#define _DEBUG_DUMPS_H
+#include <stdint.h>
+
+/* Dump stack memory starting from stack pointer address sp. */
+void dump_stack(uint32_t *sp);
+
+/* Called from exception_vectors.S when a fatal exception occurs.
+
+   Probably not useful to be called in other contexts.
+*/
+void __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack);
+
+#endif
diff --git a/core/include/xtensa_ops.h b/core/include/xtensa_ops.h
index 2fdc0f8..25252f6 100644
--- a/core/include/xtensa_ops.h
+++ b/core/include/xtensa_ops.h
@@ -25,4 +25,19 @@
 #define ESYNC() asm volatile ( "esync" )
 #define DSYNC() asm volatile ( "dsync" )
 
+
+/* Read stack pointer to variable.
+ *
+ * Note that the compiler will push a stack frame (minimum 16 bytes)
+ * in the prelude of a C function that calls any other functions.
+ */
+#define SP(var) asm volatile ("mov %0, a1" : "=r" (var));
+
+/* Read the function return address to a variable.
+ *
+ * Depends on the containing function being simple enough that a0 is
+ * being used as a working register.
+ */
+#define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var))
+
 #endif /* _XTENSA_OPS_H */
diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c
index b414481..0d4b95e 100644
--- a/core/newlib_syscalls.c
+++ b/core/newlib_syscalls.c
@@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
        if (heap_end + incr > stack_ptr)
        {
        _write (1, "_sbrk: Heap collided with stack\n", 32);
-       while(1) {}
+       abort();
        }
     */
     heap_end += incr;
diff --git a/core/sdk_compat.c b/core/sdk_compat.c
index b0d6789..1e60b85 100644
--- a/core/sdk_compat.c
+++ b/core/sdk_compat.c
@@ -17,20 +17,6 @@ void IRAM *zalloc(size_t nbytes)
     return calloc(1, nbytes);
 }
 
-extern void (*__init_array_start)(void);
-extern void (*__init_array_end)(void);
-
-/* Do things which should be done as part of the SDK startup code, but aren't.
-   TODO: Move into app_main.c
-*/
-void sdk_compat_initialise()
-{
-    /* Call C++ constructors or C functions marked with __attribute__((constructor)) */
-    void (**p)(void);
-    for ( p = &__init_array_start; p != &__init_array_end; ++p)
-        (*p)();
-}
-
 /* UART RX function from Espressif SDK internals.
  *
  * Not part of published API.
diff --git a/examples/cpp_01_tasks/Makefile b/examples/cpp_01_tasks/Makefile
index f9254cf..0953d08 100644
--- a/examples/cpp_01_tasks/Makefile
+++ b/examples/cpp_01_tasks/Makefile
@@ -1,5 +1,4 @@
 # Simple makefile for simple example
 PROGRAM=cpp_01_tasks
-OTA=0
 EXTRA_COMPONENTS=extras/cpp_support
 include ../../common.mk
diff --git a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c
index fe12055..def94fd 100644
--- a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c
+++ b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c
@@ -19,15 +19,14 @@
 
 // DS18B20 driver
 #include "ds18b20/ds18b20.h"
-// Onewire init
-#include "onewire/onewire.h"
 
 void broadcast_temperature(void *pvParameters)
 {
 
     uint8_t amount = 0;
-    uint8_t sensors = 2;
-    ds_sensor_t t[sensors];
+    uint8_t sensors = 1;
+    ds18b20_addr_t addrs[sensors];
+    float results[sensors];
     
     // Use GPIO 13 as one wire pin. 
     uint8_t GPIO_FOR_ONE_WIRE = 13;
@@ -36,8 +35,6 @@ void broadcast_temperature(void *pvParameters)
 
     // Broadcaster part
     err_t err;
-    // Initialize one wire bus.
-    onewire_init(GPIO_FOR_ONE_WIRE);
 
     while(1) {
 
@@ -66,18 +63,17 @@ void broadcast_temperature(void *pvParameters)
 
         for(;;) {
             // Search all DS18B20, return its amount and feed 't' structure with result data.
-            amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t);
+            amount = ds18b20_scan_devices(GPIO_FOR_ONE_WIRE, addrs, sensors);
 
             if (amount < sensors){
                 printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
             }
 
-            for (int i = 0; i < amount; ++i)
+            ds18b20_measure_and_read_multi(GPIO_FOR_ONE_WIRE, addrs, sensors, results);
+            for (int i = 0; i < sensors; ++i)
             {
-                int intpart = (int)t[i].value;
-                int fraction = (int)((t[i].value - intpart) * 100);
-                // Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning.
-                sprintf(msg, "Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction);
+                // ("\xC2\xB0" is the degree character (U+00B0) in UTF-8)
+                sprintf(msg, "Sensor %08x%08x reports: %f \xC2\xB0""C\n", (uint32_t)(addrs[i] >> 32), (uint32_t)addrs[i], results[i]);
                 printf("%s", msg);
 
                 struct netbuf* buf = netbuf_new();
diff --git a/examples/ds18b20_onewire/ds18b20_onewire.c b/examples/ds18b20_onewire/ds18b20_onewire.c
index 78d13bf..b9b7655 100644
--- a/examples/ds18b20_onewire/ds18b20_onewire.c
+++ b/examples/ds18b20_onewire/ds18b20_onewire.c
@@ -1,59 +1,78 @@
-/* ds18b20 - Retrieves temperature from ds18b20 sensors and print it out.
+/* ds18b20_onewire.c - Retrieves readings from one or more DS18B20 temperature
+ * sensors, and prints the results to stdout.
  *
  * This sample code is in the public domain.,
  */
-#include "espressif/esp_common.h"
-#include "esp/uart.h"
 
 #include "FreeRTOS.h"
 #include "task.h"
-#include "timers.h"
-#include "queue.h"
+#include "esp/uart.h"
 
-// DS18B20 driver
 #include "ds18b20/ds18b20.h"
-// Onewire init
-#include "onewire/onewire.h"
  
-void print_temperature(void *pvParameters)
-{
-    int delay = 500;
-    uint8_t amount = 0;
-    // Declare amount of sensors
-    uint8_t sensors = 2;
-    ds_sensor_t t[sensors];
-    
-    // Use GPIO 13 as one wire pin. 
-    uint8_t GPIO_FOR_ONE_WIRE = 13;
-    
-    onewire_init(GPIO_FOR_ONE_WIRE);
+#define SENSOR_GPIO 13
+#define MAX_SENSORS 8
+#define RESCAN_INTERVAL 8
+#define LOOP_DELAY_MS 250
+
+void print_temperature(void *pvParameters) {
+    ds18b20_addr_t addrs[MAX_SENSORS];
+    float temps[MAX_SENSORS];
+    int sensor_count;
     
+    // There is no special initialization required before using the ds18b20
+    // routines.  However, we make sure that the internal pull-up resistor is
+    // enabled on the GPIO pin so that one can connect up a sensor without
+    // needing an external pull-up (Note: The internal (~47k) pull-ups of the
+    // ESP8266 do appear to work, at least for simple setups (one or two sensors
+    // connected with short leads), but do not technically meet the pull-up
+    // requirements from the DS18B20 datasheet and may not always be reliable.
+    // For a real application, a proper 4.7k external pull-up resistor is
+    // recommended instead!)
+
+    gpio_set_pullup(SENSOR_GPIO, true, true);
+
     while(1) {
-        // Search all DS18B20, return its amount and feed 't' structure with result data.
-        amount = ds18b20_read_all(GPIO_FOR_ONE_WIRE, t);
+        // Every RESCAN_INTERVAL samples, check to see if the sensors connected
+        // to our bus have changed.
+        sensor_count = ds18b20_scan_devices(SENSOR_GPIO, addrs, MAX_SENSORS);
 
-        if (amount < sensors){
-            printf("Something is wrong, I expect to see %d sensors \nbut just %d was detected!\n", sensors, amount);
-        }
+        if (sensor_count < 1) {
+            printf("\nNo sensors detected!\n");
+        } else {
+            printf("\n%d sensors detected:\n", sensor_count);
+            // If there were more sensors found than we have space to handle,
+            // just report the first MAX_SENSORS..
+            if (sensor_count > MAX_SENSORS) sensor_count = MAX_SENSORS;
 
-        for (int i = 0; i < amount; ++i)
-        {
-            int intpart = (int)t[i].value;
-            int fraction = (int)((t[i].value - intpart) * 100);
-            // Multiple "" here is just to satisfy compiler and don`t raise 'hex escape sequence out of range' warning.
-            printf("Sensor %d report: %d.%02d ""\xC2""\xB0""C\n",t[i].id, intpart, fraction);
+            // Do a number of temperature samples, and print the results.
+            for (int i = 0; i < RESCAN_INTERVAL; i++) {
+                ds18b20_measure_and_read_multi(SENSOR_GPIO, addrs, sensor_count, temps);
+                for (int j = 0; j < sensor_count; j++) {
+                    // The DS18B20 address is a 64-bit integer, but newlib-nano
+                    // printf does not support printing 64-bit values, so we
+                    // split it up into two 32-bit integers and print them
+                    // back-to-back to make it look like one big hex number.
+                    uint32_t addr0 = addrs[j] >> 32;
+                    uint32_t addr1 = addrs[j];
+                    float temp_c = temps[j];
+                    float temp_f = (temp_c * 1.8) + 32;
+                    printf("  Sensor %08x%08x reports %f deg C (%f deg F)\n", addr0, addr1, temp_c, temp_f);
+                }
+                printf("\n");
+
+                // Wait for a little bit between each sample (note that the
+                // ds18b20_measure_and_read_multi operation already takes at
+                // least 750ms to run, so this is on top of that delay).
+                vTaskDelay(LOOP_DELAY_MS / portTICK_RATE_MS);
+            }
         }
-        printf("\n");
-        vTaskDelay(delay / portTICK_RATE_MS);
     }
 }
 
-void user_init(void)
-{
+void user_init(void) {
     uart_set_baud(0, 115200);
 
-    printf("SDK version:%s\n", sdk_system_get_sdk_version());
-
     xTaskCreate(&print_temperature, (signed char *)"print_temperature", 256, NULL, 2, NULL);
 }
 
diff --git a/examples/experiments/unaligned_load/unaligned_load.c b/examples/experiments/unaligned_load/unaligned_load.c
index 258b4d0..4244804 100644
--- a/examples/experiments/unaligned_load/unaligned_load.c
+++ b/examples/experiments/unaligned_load/unaligned_load.c
@@ -305,7 +305,7 @@ static void test_system_interaction()
     }
     uint32_t ticks = xTaskGetTickCount() - start;
     printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
-    while(1) {}
+    abort();
 }
 
 /* The following "sanity tests" are designed to try to execute every code path
diff --git a/examples/http_get_mbedtls/cert.c b/examples/http_get_mbedtls/cert.c
index f8ab11f..7acc438 100644
--- a/examples/http_get_mbedtls/cert.c
+++ b/examples/http_get_mbedtls/cert.c
@@ -1,43 +1,44 @@
-/* This is the root certificate for the CA trust chain of
+/* This is the CA certificate for the CA trust chain of
    www.howsmyssl.com in PEM format, as dumped via:
 
    openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
 
-   The root cert is the last cert in the chain output by the server.
+   The CA cert is the last cert in the chain output by the server.
 */
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
 
+/*
+ 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
+   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
+ */
 const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n"
-"MIIFNzCCBB+gAwIBAgISAfl7TPw6Mf/F6VCBc5eaHA7kMA0GCSqGSIb3DQEBCwUA\r\n"
-"MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\r\n"
-"ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTAeFw0xNTEyMzAwNzQ0MDBaFw0x\r\n"
-"NjAzMjkwNzQ0MDBaMBwxGjAYBgNVBAMTEXd3dy5ob3dzbXlzc2wuY29tMIIBIjAN\r\n"
-"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKF7WzSrDPinQhd9mVfoW5u46/TC\r\n"
-"fbYKR3MEryetUSeQuwuXFj2xO6+a/JQ99UC3Eq+9s8uFdx1zFFS6qfW+HXBwGWVf\r\n"
-"ajKEyIcXUJZCRn7aWpTWZq6cuv4bZSv1QklGViQs8UCZifcN0A/mYrH7zHG2WDn2\r\n"
-"fxNgA+nJBqbZPr1gP9hqGFCnX+dPR5WxtC9+Dv9Sx+wiOWVz/obVTCygdqqcpa5I\r\n"
-"3/U9REVgO2VfT+xMty6NZTMCjTJ+GXZuB/BrMe9+ZmgWk0grJyqdrCxOCyK6B4g+\r\n"
-"Fvs8WFRNTHdQnP3/NT5hPtreZ3nuY2YY7RbGFwUaBcvJwbbIqalpiQ1X7QIDAQAB\r\n"
-"o4ICQzCCAj8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr\r\n"
-"BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQMAe087CSp3PjgqyIYyWTR\r\n"
-"a2aMnTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBwBggrBgEFBQcB\r\n"
-"AQRkMGIwLwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmludC14MS5sZXRzZW5jcnlw\r\n"
-"dC5vcmcvMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDEubGV0c2VuY3J5\r\n"
-"cHQub3JnLzBNBgNVHREERjBEgg1ob3dzbXlzc2wuY29tgg1ob3dzbXl0bHMuY29t\r\n"
-"ghF3d3cuaG93c215dGxzLmNvbYIRd3d3Lmhvd3NteXNzbC5jb20wgf4GA1UdIASB\r\n"
-"9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpo\r\n"
-"dHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlz\r\n"
-"IENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcg\r\n"
-"UGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmlj\r\n"
-"YXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBv\r\n"
-"c2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEALB2QrIrcxxFr81b6khy9LwVBpthL\r\n"
-"2LSs0xWhA09gxHmPnVrqim3Wa9HFYRApSqtTQWSx58TO+MERXQ7eDX50k53oem+Q\r\n"
-"gXn90HVDkR0jbV1CsAD5qL00kKOofAyGkUhFlO2hcRU0CIj7Z9HZB8xpYdZWLUSZ\r\n"
-"BpgiXdf/YYRIwgx29GRQjhfl/H30fHyawY5SvquJuvAeEr7lxqAmEEg3a7J6ibHL\r\n"
-"90zf5KMkXGyVsXqxLqrEXQKgTvpUMeP5iYAxE45R5GNmYS2jajyTi4XkWw4nEu4Q\r\n"
-"dMTLuwxGz87KOwSSFOLU903hO3IIPvFIIMY6aVK2kAgQPNIqk9nFurTF9A==\r\n"
+"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
+"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
+"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
+"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
+"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
+"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
+"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
+"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
+"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
+"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
+"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
+"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
+"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
+"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
+"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
+"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
+"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
+"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
+"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
+"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
+"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
+"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
+"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
+"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
+"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
 "-----END CERTIFICATE-----\r\n";
 
 
diff --git a/examples/http_get_mbedtls/http_get_mbedtls.c b/examples/http_get_mbedtls/http_get_mbedtls.c
index f45fd54..7aad0e3 100644
--- a/examples/http_get_mbedtls/http_get_mbedtls.c
+++ b/examples/http_get_mbedtls/http_get_mbedtls.c
@@ -41,11 +41,11 @@
 #include "mbedtls/error.h"
 #include "mbedtls/certs.h"
 
-#define WEB_SERVER "howsmyssl.com"
+#define WEB_SERVER "www.howsmyssl.com"
 #define WEB_PORT "443"
 #define WEB_URL "https://www.howsmyssl.com/a/check"
 
-#define GET_REQUEST "GET "WEB_URL" HTTP/1.1\n\n"
+#define GET_REQUEST "GET "WEB_URL" HTTP/1.1\nHost: "WEB_SERVER"\n\n"
 
 /* Root cert for howsmyssl.com, stored in cert.c */
 extern const char *server_root_cert;
@@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
                                     strlen(pers))) != 0)
     {
         printf(" failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret);
-        while(1) {} /* todo: replace with abort() */
+        abort();
     }
 
     printf(" ok\n");
@@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
     if(ret < 0)
     {
         printf(" failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
-        while(1) {} /* todo: replace with abort() */
+        abort();
     }
 
     printf(" ok (%d skipped)\n", ret);
@@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
     if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
     {
         printf(" failed\n  ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
-        while(1) {} /* todo: replace with abort() */
+        abort();
     }
 
     /*
diff --git a/examples/mqtt_client/Makefile b/examples/mqtt_client/Makefile
new file mode 100644
index 0000000..0eff202
--- /dev/null
+++ b/examples/mqtt_client/Makefile
@@ -0,0 +1,3 @@
+PROGRAM=mqtt_client
+EXTRA_COMPONENTS = extras/paho_mqtt_c
+include ../../common.mk
diff --git a/examples/mqtt_client/mqtt_client.c b/examples/mqtt_client/mqtt_client.c
new file mode 100644
index 0000000..fdf86c3
--- /dev/null
+++ b/examples/mqtt_client/mqtt_client.c
@@ -0,0 +1,221 @@
+#include "espressif/esp_common.h"
+#include "esp/uart.h"
+
+#include <string.h>
+
+#include <FreeRTOS.h>
+#include <task.h>
+#include <ssid_config.h>
+
+#include <espressif/esp_sta.h>
+#include <espressif/esp_wifi.h>
+
+#include <paho_mqtt_c/MQTTESP8266.h>
+#include <paho_mqtt_c/MQTTClient.h>
+
+#include <semphr.h>
+
+
+/* You can use http://test.mosquitto.org/ to test mqtt_client instead
+ * of setting up your own MQTT server */
+#define MQTT_HOST ("test.mosquitto.org")
+#define MQTT_PORT 1883
+
+#define MQTT_USER NULL
+#define MQTT_PASS NULL
+
+xSemaphoreHandle wifi_alive;
+xQueueHandle publish_queue;
+#define PUB_MSG_LEN 16
+
+static void  beat_task(void *pvParameters)
+{
+    portTickType xLastWakeTime = xTaskGetTickCount();
+    char msg[PUB_MSG_LEN];
+    int count = 0;
+
+    while (1) {
+        vTaskDelayUntil(&xLastWakeTime, 10000 / portTICK_RATE_MS);
+        printf("beat\r\n");
+        snprintf(msg, PUB_MSG_LEN, "Beat %d\r\n", count++);
+        if (xQueueSend(publish_queue, (void *)msg, 0) == pdFALSE) {
+            printf("Publish queue overflow.\r\n");
+        }
+    }
+}
+
+static void  topic_received(MessageData *md)
+{
+    int i;
+    MQTTMessage *message = md->message;
+    printf("Received: ");
+    for( i = 0; i < md->topic->lenstring.len; ++i)
+        printf("%c", md->topic->lenstring.data[ i ]);
+
+    printf(" = ");
+    for( i = 0; i < (int)message->payloadlen; ++i)
+        printf("%c", ((char *)(message->payload))[i]);
+
+    printf("\r\n");
+}
+
+static const char *  get_my_id(void)
+{
+    // Use MAC address for Station as unique ID
+    static char my_id[13];
+    static bool my_id_done = false;
+    int8_t i;
+    uint8_t x;
+    if (my_id_done)
+        return my_id;
+    if (!sdk_wifi_get_macaddr(STATION_IF, (uint8_t *)my_id))
+        return NULL;
+    for (i = 5; i >= 0; --i)
+    {
+        x = my_id[i] & 0x0F;
+        if (x > 9) x += 7;
+        my_id[i * 2 + 1] = x + '0';
+        x = my_id[i] >> 4;
+        if (x > 9) x += 7;
+        my_id[i * 2] = x + '0';
+    }
+    my_id[12] = '\0';
+    my_id_done = true;
+    return my_id;
+}
+
+static void  mqtt_task(void *pvParameters)
+{
+    int ret         = 0;
+    struct Network network;
+    MQTTClient client   = DefaultClient;
+    char mqtt_client_id[20];
+    uint8_t mqtt_buf[100];
+    uint8_t mqtt_readbuf[100];
+    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
+
+    NewNetwork( &network );
+    memset(mqtt_client_id, 0, sizeof(mqtt_client_id));
+    strcpy(mqtt_client_id, "ESP-");
+    strcat(mqtt_client_id, get_my_id());
+
+    while(1) {
+        xSemaphoreTake(wifi_alive, portMAX_DELAY);
+        printf("%s: started\n\r", __func__);
+        printf("%s: (Re)connecting to MQTT server %s ... ",__func__,
+               MQTT_HOST);
+        ret = ConnectNetwork(&network, MQTT_HOST, MQTT_PORT);
+        if( ret ){
+            printf("error: %d\n\r", ret);
+            taskYIELD();
+            continue;
+        }
+        printf("done\n\r");
+        NewMQTTClient(&client, &network, 5000, mqtt_buf, 100,
+                      mqtt_readbuf, 100);
+
+        data.willFlag       = 0;
+        data.MQTTVersion    = 3;
+        data.clientID.cstring   = mqtt_client_id;
+        data.username.cstring   = MQTT_USER;
+        data.password.cstring   = MQTT_PASS;
+        data.keepAliveInterval  = 10;
+        data.cleansession   = 0;
+        printf("Send MQTT connect ... ");
+        ret = MQTTConnect(&client, &data);
+        if(ret){
+            printf("error: %d\n\r", ret);
+            DisconnectNetwork(&network);
+            taskYIELD();
+            continue;
+        }
+        printf("done\r\n");
+        MQTTSubscribe(&client, "/esptopic", QOS1, topic_received);
+        xQueueReset(publish_queue);
+
+        while(1){
+
+            char msg[PUB_MSG_LEN - 1] = "\0";
+            while(xQueueReceive(publish_queue, (void *)msg, 0) ==
+                  pdTRUE){
+                printf("got message to publish\r\n");
+                MQTTMessage message;
+                message.payload = msg;
+                message.payloadlen = PUB_MSG_LEN;
+                message.dup = 0;
+                message.qos = QOS1;
+                message.retained = 0;
+                ret = MQTTPublish(&client, "/beat", &message);
+                if (ret != SUCCESS ){
+                    printf("error while publishing message: %d\n", ret );
+                    break;
+                }
+            }
+
+            ret = MQTTYield(&client, 1000);
+            if (ret == DISCONNECTED)
+                break;
+        }
+        printf("Connection dropped, request restart\n\r");
+        taskYIELD();
+    }
+}
+
+static void  wifi_task(void *pvParameters)
+{
+    uint8_t status  = 0;
+    uint8_t retries = 30;
+    struct sdk_station_config config = {
+        .ssid = WIFI_SSID,
+        .password = WIFI_PASS,
+    };
+
+    printf("WiFi: connecting to WiFi\n\r");
+    sdk_wifi_set_opmode(STATION_MODE);
+    sdk_wifi_station_set_config(&config);
+
+    while(1)
+    {
+        while ((status != STATION_GOT_IP) && (retries)){
+            status = sdk_wifi_station_get_connect_status();
+            printf("%s: status = %d\n\r", __func__, status );
+            if( status == STATION_WRONG_PASSWORD ){
+                printf("WiFi: wrong password\n\r");
+                break;
+            } else if( status == STATION_NO_AP_FOUND ) {
+                printf("WiFi: AP not found\n\r");
+                break;
+            } else if( status == STATION_CONNECT_FAIL ) {
+                printf("WiFi: connection failed\r\n");
+                break;
+            }
+            vTaskDelay( 1000 / portTICK_RATE_MS );
+            --retries;
+        }
+        if (status == STATION_GOT_IP) {
+            printf("WiFi: Connected\n\r");
+            xSemaphoreGive( wifi_alive );
+            taskYIELD();
+        }
+
+        while ((status = sdk_wifi_station_get_connect_status()) == STATION_GOT_IP) {
+            xSemaphoreGive( wifi_alive );
+            taskYIELD();
+        }
+        printf("WiFi: disconnected\n\r");
+        sdk_wifi_station_disconnect();
+        vTaskDelay( 1000 / portTICK_RATE_MS );
+    }
+}
+
+void user_init(void)
+{
+    uart_set_baud(0, 115200);
+    printf("SDK version:%s\n", sdk_system_get_sdk_version());
+
+    vSemaphoreCreateBinary(wifi_alive);
+    publish_queue = xQueueCreate(3, PUB_MSG_LEN);
+    xTaskCreate(&wifi_task, (int8_t *)"wifi_task",  256, NULL, 2, NULL);
+    xTaskCreate(&beat_task, (int8_t *)"beat_task", 256, NULL, 3, NULL);
+    xTaskCreate(&mqtt_task, (int8_t *)"mqtt_task", 1024, NULL, 4, NULL);
+}
diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index 9189f02..d55c03a 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -14,13 +14,15 @@
 #include "ssid_config.h"
 
 #include "ota-tftp.h"
-#include "rboot-ota.h"
+#include "rboot-integration.h"
+#include "rboot.h"
+#include "rboot-api.h"
 
 void user_init(void)
 {
     uart_set_baud(0, 115200);
 
-    rboot_config_t conf = rboot_get_config();
+    rboot_config conf = rboot_get_config();
     printf("\r\n\r\nOTA Basic demo.\r\nCurrently running on flash slot %d / %d.\r\n\r\n",
            conf.current_rom, conf.count);
 
diff --git a/examples/tls_server/tls_server.c b/examples/tls_server/tls_server.c
index d253bc9..9030dc0 100644
--- a/examples/tls_server/tls_server.c
+++ b/examples/tls_server/tls_server.c
@@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
                                     strlen(pers))) != 0)
     {
         printf(" failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret);
-        while(1) {} /* todo: replace with abort() */
+        abort();
     }
 
     printf(" ok\n");
@@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
     if(ret < 0)
     {
         printf(" failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
-        while(1) {} /* todo: replace with abort() */
+        abort();
     }
 
     printf(" ok (%d skipped)\n", ret);
@@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
     if(ret != 0)
     {
         printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
-        while(1) { } /*todo: replace with abort() */
+        abort();
     }
 
     printf(" ok\n");
@@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
     if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
     {
         printf( " failed\n  ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
-        while(1) { }
+        abort();
     }
 
     mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c
index dc028fc..c965b04 100644
--- a/extras/ds18b20/ds18b20.c
+++ b/extras/ds18b20/ds18b20.c
@@ -1,43 +1,47 @@
 #include "FreeRTOS.h"
 #include "task.h"
+#include "math.h"
 
-#include "onewire/onewire.h"
 #include "ds18b20.h"
 
-#define DS1820_WRITE_SCRATCHPAD 0x4E
-#define DS1820_READ_SCRATCHPAD  0xBE
-#define DS1820_COPY_SCRATCHPAD  0x48
-#define DS1820_READ_EEPROM      0xB8
-#define DS1820_READ_PWRSUPPLY   0xB4
-#define DS1820_SEARCHROM        0xF0
-#define DS1820_SKIP_ROM         0xCC
-#define DS1820_READROM          0x33
-#define DS1820_MATCHROM         0x55
-#define DS1820_ALARMSEARCH      0xEC
-#define DS1820_CONVERT_T        0x44
+#define DS18B20_WRITE_SCRATCHPAD 0x4E
+#define DS18B20_READ_SCRATCHPAD  0xBE
+#define DS18B20_COPY_SCRATCHPAD  0x48
+#define DS18B20_READ_EEPROM      0xB8
+#define DS18B20_READ_PWRSUPPLY   0xB4
+#define DS18B20_SEARCHROM        0xF0
+#define DS18B20_SKIP_ROM         0xCC
+#define DS18B20_READROM          0x33
+#define DS18B20_MATCHROM         0x55
+#define DS18B20_ALARMSEARCH      0xEC
+#define DS18B20_CONVERT_T        0x44
+
+#define os_sleep_ms(x) vTaskDelay(((x) + portTICK_RATE_MS - 1) / portTICK_RATE_MS)
 
 uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) {
-    
-    uint8_t addr[8];
+    onewire_addr_t addr;
+    onewire_search_t search;
     uint8_t sensor_id = 0;
-    onewire_reset_search(pin);
+
+    onewire_search_start(&search);
     
-    while(onewire_search(pin, addr)){
-        uint8_t crc = onewire_crc8(addr, 7);
-        if (crc != addr[7]){
-            printf("CRC check failed: %02X %02X\n", addr[7], crc);
+    while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) {
+        uint8_t crc = onewire_crc8((uint8_t *)&addr, 7);
+        if (crc != (addr >> 56)){
+            printf("CRC check failed: %02X %02X\n", (unsigned)(addr >> 56), crc);
             return 0;
         }
 
         onewire_reset(pin);
         onewire_select(pin, addr);
-        onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER);
+        onewire_write(pin, DS18B20_CONVERT_T);
         
+        onewire_power(pin);
         vTaskDelay(750 / portTICK_RATE_MS);
         
         onewire_reset(pin);
         onewire_select(pin, addr);
-        onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
+        onewire_write(pin, DS18B20_READ_SCRATCHPAD);
 
         uint8_t get[10];
 
@@ -71,15 +75,15 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) {
 float ds18b20_read_single(uint8_t pin) {
   
     onewire_reset(pin);
+    onewire_skip_rom(pin);
+    onewire_write(pin, DS18B20_CONVERT_T);
 
-    onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
-    onewire_write(pin, DS1820_CONVERT_T, ONEWIRE_DEFAULT_POWER);
-
+    onewire_power(pin);
     vTaskDelay(750 / portTICK_RATE_MS);
 
     onewire_reset(pin);
-    onewire_write(pin, DS1820_SKIP_ROM, ONEWIRE_DEFAULT_POWER);
-    onewire_write(pin, DS1820_READ_SCRATCHPAD, ONEWIRE_DEFAULT_POWER);
+    onewire_skip_rom(pin);
+    onewire_write(pin, DS18B20_READ_SCRATCHPAD);
     
     uint8_t get[10];
 
@@ -106,3 +110,114 @@ float ds18b20_read_single(uint8_t pin) {
     return temperature;
     //printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100);
 }
+
+bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait) {
+    if (!onewire_reset(pin)) {
+        return false;
+    }
+    if (addr == DS18B20_ANY) {
+        onewire_skip_rom(pin);
+    } else {
+        onewire_select(pin, addr);
+    }
+    taskENTER_CRITICAL();
+    onewire_write(pin, DS18B20_CONVERT_T);
+    // For parasitic devices, power must be applied within 10us after issuing
+    // the convert command.
+    onewire_power(pin);
+    taskEXIT_CRITICAL();
+
+    if (wait) {
+        os_sleep_ms(750);
+        onewire_depower(pin);
+    }
+
+    return true;
+}
+
+bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer) {
+    uint8_t crc;
+    uint8_t expected_crc;
+
+    if (!onewire_reset(pin)) {
+        return false;
+    }
+    if (addr == DS18B20_ANY) {
+        onewire_skip_rom(pin);
+    } else {
+        onewire_select(pin, addr);
+    }
+    onewire_write(pin, DS18B20_READ_SCRATCHPAD);
+    
+    for (int i = 0; i < 8; i++) {
+        buffer[i] = onewire_read(pin);
+    }
+    crc = onewire_read(pin);
+
+    expected_crc = onewire_crc8(buffer, 8);
+    if (crc != expected_crc) {
+        printf("CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc);
+        return false;
+    }
+
+    return true;
+}
+
+float ds18b20_read_temperature(int pin, ds18b20_addr_t addr) {
+    uint8_t scratchpad[8];
+    int temp;
+
+    if (!ds18b20_read_scratchpad(pin, addr, scratchpad)) {
+        return NAN;
+    }
+
+    temp = scratchpad[1] << 8 | scratchpad[0];
+    
+    return ((float)temp * 625.0)/10000;
+}
+
+float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr) {
+    if (!ds18b20_measure(pin, addr, true)) {
+        return NAN;
+    }
+    return ds18b20_read_temperature(pin, addr);
+}
+
+bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) {
+    if (!ds18b20_measure(pin, DS18B20_ANY, true)) {
+        for (int i=0; i < addr_count; i++) {
+            result_list[i] = NAN;
+        }
+        return false;
+    }
+    return ds18b20_read_temp_multi(pin, addr_list, addr_count, result_list);
+}
+
+int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count) {
+    onewire_search_t search;
+    onewire_addr_t addr;
+    int found = 0;
+
+    onewire_search_start(&search);
+    while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) {
+        if (found < addr_count) {
+            addr_list[found] = addr;
+        }
+        found++;
+    }
+    return found;
+}
+
+bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list) {
+    bool result = true;
+
+    for (int i = 0; i < addr_count; i++) {
+        result_list[i] = ds18b20_read_temperature(pin, addr_list[i]);
+        if (isnan(result_list[i])) {
+            result = false;
+        }
+    }
+    return result;
+}
+
+
diff --git a/extras/ds18b20/ds18b20.h b/extras/ds18b20/ds18b20.h
index 594227b..04b4a53 100644
--- a/extras/ds18b20/ds18b20.h
+++ b/extras/ds18b20/ds18b20.h
@@ -1,6 +1,139 @@
 #ifndef DRIVER_DS18B20_H_
 #define DRIVER_DS18B20_H_
 
+#include "onewire/onewire.h"
+
+/** @file ds18b20.h
+ *
+ *  Communicate with the DS18B20 family of one-wire temperature sensor ICs.
+ *
+ */
+
+typedef onewire_addr_t ds18b20_addr_t;
+
+/** An address value which can be used to indicate "any device on the bus" */
+#define DS18B20_ANY ONEWIRE_NONE
+
+/** Find the addresses of all DS18B20 devices on the bus.
+ *
+ *  Scans the bus for all devices and places their addresses in the supplied
+ *  array.  If there are more than `addr_count` devices on the bus, only the
+ *  first `addr_count` are recorded.
+ *
+ *  @param pin         The GPIO pin connected to the DS18B20 bus
+ *  @param addr_list   A pointer to an array of ds18b20_addr_t values.  This
+ *                     will be populated with the addresses of the found
+ *                     devices.
+ *  @param addr_count  Number of slots in the `addr_list` array.  At most this
+ *                     many addresses will be returned.
+ *
+ *  @returns The number of devices found.  Note that this may be less than,
+ *  equal to, or more than `addr_count`, depending on how many DS18B20 devices
+ *  are attached to the bus.
+ */
+int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count);
+
+/** Tell one or more sensors to perform a temperature measurement and
+ *  conversion (CONVERT_T) operation.  This operation can take up to 750ms to
+ *  complete.
+ *
+ *  If `wait=true`, this routine will automatically drive the pin high for the
+ *  necessary 750ms after issuing the command to ensure parasitically-powered
+ *  devices have enough power to perform the conversion operation (for
+ *  non-parasitically-powered devices, this is not necessary but does not
+ *  hurt).  If `wait=false`, this routine will drive the pin high, but will
+ *  then return immediately.  It is up to the caller to wait the requisite time
+ *  and then depower the bus using onewire_depower() or by issuing another
+ *  command once conversion is done.
+ *
+ *  @param pin   The GPIO pin connected to the DS18B20 device
+ *  @param addr  The 64-bit address of the device on the bus.  This can be set
+ *               to ::DS18B20_ANY to send the command to all devices on the bus
+ *               at the same time.
+ *  @param wait  Whether to wait for the necessary 750ms for the DS18B20 to
+ *               finish performing the conversion before returning to the
+ *               caller (You will normally want to do this).
+ *
+ *  @returns `true` if the command was successfully issued, or `false` on error.
+ */
+bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait);
+
+/** Read the value from the last CONVERT_T operation.
+ *
+ *  This should be called after ds18b20_measure() to fetch the result of the
+ *  temperature measurement.
+ *
+ *  @param pin     The GPIO pin connected to the DS18B20 device
+ *  @param addr    The 64-bit address of the device to read.  This can be set
+ *                 to ::DS18B20_ANY to read any device on the bus (but note
+ *                 that this will only work if there is exactly one device
+ *                 connected, or they will corrupt each others' transmissions)
+ *
+ *  @returns The temperature in degrees Celsius, or NaN if there was an error.
+ */
+float ds18b20_read_temperature(int pin, ds18b20_addr_t addr);
+
+/** Read the value from the last CONVERT_T operation for multiple devices.
+ *
+ *  This should be called after ds18b20_measure() to fetch the result of the
+ *  temperature measurement.
+ *
+ *  @param pin         The GPIO pin connected to the DS18B20 bus
+ *  @param addr_list   A list of addresses for devices to read.
+ *  @param addr_count  The number of entries in `addr_list`.
+ *  @param result_list An array of floats to hold the returned temperature
+ *                     values.  It should have at least `addr_count` entries.
+ *
+ *  @returns `true` if all temperatures were fetched successfully, or `false`
+ *  if one or more had errors (the temperature for erroring devices will be
+ *  returned as NaN).
+ */
+bool ds18b20_read_temp_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list);
+
+/** Perform a ds18b20_measure() followed by ds18b20_read_temperature()
+ *
+ *  @param pin     The GPIO pin connected to the DS18B20 device
+ *  @param addr    The 64-bit address of the device to read.  This can be set
+ *                 to ::DS18B20_ANY to read any device on the bus (but note
+ *                 that this will only work if there is exactly one device
+ *                 connected, or they will corrupt each others' transmissions)
+ *
+ *  @returns The temperature in degrees Celsius, or NaN if there was an error.
+ */
+float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr);
+
+/** Perform a ds18b20_measure() followed by ds18b20_read_temp_multi()
+ *
+ *  @param pin         The GPIO pin connected to the DS18B20 bus
+ *  @param addr_list   A list of addresses for devices to read.
+ *  @param addr_count  The number of entries in `addr_list`.
+ *  @param result_list An array of floats to hold the returned temperature
+ *                     values.  It should have at least `addr_count` entries.
+ *
+ *  @returns `true` if all temperatures were fetched successfully, or `false`
+ *  if one or more had errors (the temperature for erroring devices will be
+ *  returned as NaN).
+ */
+bool ds18b20_measure_and_read_multi(int pin, ds18b20_addr_t *addr_list, int addr_count, float *result_list);
+
+/** Read the scratchpad data for a particular DS18B20 device.
+ *
+ *  This is not generally necessary to do directly.  It is done automatically
+ *  as part of ds18b20_read_temperature().
+ *
+ *  @param pin     The GPIO pin connected to the DS18B20 device
+ *  @param addr    The 64-bit address of the device to read.  This can be set
+ *                 to ::DS18B20_ANY to read any device on the bus (but note
+ *                 that this will only work if there is exactly one device
+ *                 connected, or they will corrupt each others' transmissions)
+ *  @param buffer  An 8-byte buffer to hold the read data.
+ *
+ *  @returns `true` if the data was read successfully, or `false` on error.
+ */
+bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer);
+
+// The following are obsolete/deprecated APIs
+
 typedef struct {
     uint8_t id;
     float value;
diff --git a/extras/mbedtls/mbedtls b/extras/mbedtls/mbedtls
index 0a0c22e..a7ffc8f 160000
--- a/extras/mbedtls/mbedtls
+++ b/extras/mbedtls/mbedtls
@@ -1 +1 @@
-Subproject commit 0a0c22e0efcf2f8f71d7e16712f80b8f77326f72
+Subproject commit a7ffc8f7396573bec401e0afcc073137522d5305
diff --git a/extras/onewire/onewire.c b/extras/onewire/onewire.c
index 3a946ba..159360b 100644
--- a/extras/onewire/onewire.c
+++ b/extras/onewire/onewire.c
@@ -1,206 +1,207 @@
 #include "onewire.h"
+#include "string.h"
+#include "task.h"
+#include "esp/gpio.h"
 
-// global search state
-static unsigned char ROM_NO[ONEWIRE_NUM][8];
-static uint8_t LastDiscrepancy[ONEWIRE_NUM];
-static uint8_t LastFamilyDiscrepancy[ONEWIRE_NUM];
-static uint8_t LastDeviceFlag[ONEWIRE_NUM];
+#define ONEWIRE_SELECT_ROM 0x55
+#define ONEWIRE_SKIP_ROM   0xcc
+#define ONEWIRE_SEARCH     0xf0
 
-void onewire_init(uint8_t pin)
-{
-  gpio_enable(pin, GPIO_INPUT);  
-  onewire_reset_search(pin);
+// Waits up to `max_wait` microseconds for the specified pin to go high.
+// Returns true if successful, false if the bus never comes high (likely
+// shorted).
+static inline bool _onewire_wait_for_bus(int pin, int max_wait) {
+    bool state;
+    for (int i = 0; i < ((max_wait + 4) / 5); i++) {
+        if (gpio_read(pin)) break;
+        sdk_os_delay_us(5);
+    }
+    state = gpio_read(pin);
+    // Wait an extra 1us to make sure the devices have an adequate recovery
+    // time before we drive things low again.
+    sdk_os_delay_us(1);
+    return state;
 }
 
 // Perform the onewire reset function.  We will wait up to 250uS for
 // the bus to come high, if it doesn't then it is broken or shorted
-// and we return a 0;
+// and we return false;
 //
-// Returns 1 if a device asserted a presence pulse, 0 otherwise.
+// Returns true if a device asserted a presence pulse, false otherwise.
 //
-uint8_t onewire_reset(uint8_t pin)
-{
-	uint8_t r;
-	uint8_t retries = 125;
+bool onewire_reset(int pin) {
+    bool r;
 
-	noInterrupts();
-	DIRECT_MODE_INPUT(pin);
-	interrupts();
-	// wait until the wire is high... just in case
-	do {
-		if (--retries == 0) return 0;
-		delayMicroseconds(2);
-	} while ( !DIRECT_READ(pin));
+    gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
+    gpio_write(pin, 1);
+    // wait until the wire is high... just in case
+    if (!_onewire_wait_for_bus(pin, 250)) return false;
 
-	noInterrupts();
-	DIRECT_WRITE_LOW(pin);
-	DIRECT_MODE_OUTPUT(pin);	// drive output low
-	interrupts();
-	delayMicroseconds(480);
-	noInterrupts();
-	DIRECT_MODE_INPUT(pin);	// allow it to float
-	delayMicroseconds(70);
-	r = !DIRECT_READ(pin);
-	interrupts();
-	delayMicroseconds(410);
-	return r;
+    gpio_write(pin, 0);
+    sdk_os_delay_us(480);
+
+    taskENTER_CRITICAL();
+    gpio_write(pin, 1); // allow it to float
+    sdk_os_delay_us(70);
+    r = !gpio_read(pin);
+    taskEXIT_CRITICAL();
+
+    // Wait for all devices to finish pulling the bus low before returning
+    if (!_onewire_wait_for_bus(pin, 410)) return false;
+
+    return r;
 }
 
-// Write a bit. Port and bit is used to cut lookup time and provide
-// more certain timing.
-//
-static void onewire_write_bit(uint8_t pin, uint8_t v)
-{
-	if (v & 1) {
-		noInterrupts();
-		DIRECT_WRITE_LOW(pin);
-		DIRECT_MODE_OUTPUT(pin);	// drive output low
-		delayMicroseconds(10);
-		DIRECT_WRITE_HIGH(pin);	// drive output high
-		interrupts();
-		delayMicroseconds(55);
-	} else {
-		noInterrupts();
-		DIRECT_WRITE_LOW(pin);
-		DIRECT_MODE_OUTPUT(pin);	// drive output low
-		delayMicroseconds(65);
-		DIRECT_WRITE_HIGH(pin);	// drive output high
-		interrupts();
-		delayMicroseconds(5);
-	}
+static bool _onewire_write_bit(int pin, bool v) {
+    if (!_onewire_wait_for_bus(pin, 10)) return false;
+    if (v) {
+        taskENTER_CRITICAL();
+        gpio_write(pin, 0);  // drive output low
+        sdk_os_delay_us(10);
+        gpio_write(pin, 1);  // allow output high
+        taskEXIT_CRITICAL();
+        sdk_os_delay_us(55);
+    } else {
+        taskENTER_CRITICAL();
+        gpio_write(pin, 0);  // drive output low
+        sdk_os_delay_us(65);
+        gpio_write(pin, 1); // allow output high
+        taskEXIT_CRITICAL();
+    }
+    sdk_os_delay_us(1);
+
+    return true;
 }
 
-// Read a bit. Port and bit is used to cut lookup time and provide
-// more certain timing.
-//
-static uint8_t onewire_read_bit(uint8_t pin)
-{
-	uint8_t r;
+static int _onewire_read_bit(int pin) {
+    int r;
 
-	noInterrupts();
-	DIRECT_MODE_OUTPUT(pin);
-	DIRECT_WRITE_LOW(pin);
-	delayMicroseconds(3);
-	DIRECT_MODE_INPUT(pin);	// let pin float, pull up will raise
-	delayMicroseconds(10);
-	r = DIRECT_READ(pin);
-	interrupts();
-	delayMicroseconds(53);
-	return r;
+    if (!_onewire_wait_for_bus(pin, 10)) return -1;
+    taskENTER_CRITICAL();
+    gpio_write(pin, 0);
+    sdk_os_delay_us(2);
+    gpio_write(pin, 1);  // let pin float, pull up will raise
+    sdk_os_delay_us(11);
+    r = gpio_read(pin);  // Must sample within 15us of start
+    taskEXIT_CRITICAL();
+    sdk_os_delay_us(48);
+
+    return r;
 }
 
-// Write a byte. The writing code uses the active drivers to raise the
-// pin high, if you need power after the write (e.g. DS18S20 in
-// parasite power mode) then set 'power' to 1, otherwise the pin will
-// go tri-state at the end of the write to avoid heating in a short or
-// other mishap.
+// Write a byte. The writing code uses open-drain mode and expects the pullup
+// resistor to pull the line high when not driven low.  If you need strong
+// power after the write (e.g. DS18B20 in parasite power mode) then call
+// onewire_power() after this is complete to actively drive the line high.
 //
-void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) {
-  uint8_t bitMask;
+bool onewire_write(int pin, uint8_t v) {
+    uint8_t bitMask;
 
-  for (bitMask = 0x01; bitMask; bitMask <<= 1) {
-	  onewire_write_bit(pin, (bitMask & v)?1:0);
-  }
-  if ( !power) {
-  	noInterrupts();
-  	DIRECT_MODE_INPUT(pin);
-  	DIRECT_WRITE_LOW(pin);
-  	interrupts();
-  }
+    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
+        if (!_onewire_write_bit(pin, (bitMask & v))) {
+            return false;
+        }
+    }
+    return true;
 }
 
-void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
-  uint16_t i;
-  for (i = 0 ; i < count ; i++)
-    onewire_write(pin, buf[i], ONEWIRE_DEFAULT_POWER);
-  if (!power) {
-    noInterrupts();
-    DIRECT_MODE_INPUT(pin);
-    DIRECT_WRITE_LOW(pin);
-    interrupts();
-  }
+bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count) {
+    size_t i;
+
+    for (i = 0 ; i < count ; i++) {
+        if (!onewire_write(pin, buf[i])) {
+            return false;
+        }
+    }
+    return true;
 }
 
 // Read a byte
 //
-uint8_t onewire_read(uint8_t pin) {
-  uint8_t bitMask;
-  uint8_t r = 0;
+int onewire_read(int pin) {
+    uint8_t bitMask;
+    int r = 0;
+    int bit;
 
-  for (bitMask = 0x01; bitMask; bitMask <<= 1) {
-  	if (onewire_read_bit(pin)) r |= bitMask;
-  }
-  return r;
+    for (bitMask = 0x01; bitMask; bitMask <<= 1) {
+        bit = _onewire_read_bit(pin);
+        if (bit < 0) {
+            return -1;
+        } else if (bit) {
+            r |= bitMask;
+        }
+    }
+    return r;
 }
 
-void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count) {
-  uint16_t i;
-  for (i = 0 ; i < count ; i++)
-    buf[i] = onewire_read(pin);
+bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) {
+    size_t i;
+    int b;
+
+    for (i = 0 ; i < count ; i++) {
+        b = onewire_read(pin);
+        if (b < 0) return false;
+        buf[i] = b;
+    }
+    return true;
 }
 
-// Do a ROM select
-//
-void onewire_select(uint8_t pin, const uint8_t rom[8])
-{
+bool onewire_select(int pin, onewire_addr_t addr) {
     uint8_t i;
 
-    onewire_write(pin, 0x55, ONEWIRE_DEFAULT_POWER);           // Choose ROM
+    if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) {
+        return false;
+    }
 
-    for (i = 0; i < 8; i++) onewire_write(pin, rom[i], ONEWIRE_DEFAULT_POWER);
+    for (i = 0; i < 8; i++) {
+        if (!onewire_write(pin, addr & 0xff)) {
+            return false;
+        }
+        addr >>= 8;
+    }
+
+    return true;
 }
 
-// Do a ROM skip
-//
-void onewire_skip(uint8_t pin)
-{
-    onewire_write(pin, 0xCC, ONEWIRE_DEFAULT_POWER);           // Skip ROM
+bool onewire_skip_rom(int pin) {
+    return onewire_write(pin, ONEWIRE_SKIP_ROM);
 }
 
-void onewire_depower(uint8_t pin)
-{
-	noInterrupts();
-	DIRECT_MODE_INPUT(pin);
-	interrupts();
+bool onewire_power(int pin) {
+    // Make sure the bus is not being held low before driving it high, or we
+    // may end up shorting ourselves out.
+    if (!_onewire_wait_for_bus(pin, 10)) return false;
+
+    gpio_enable(pin, GPIO_OUTPUT);
+    gpio_write(pin, 1);
+
+    return true;
 }
 
-// You need to use this function to start a search again from the beginning.
-// You do not need to do it for the first search, though you could.
-//
-void onewire_reset_search(uint8_t pin)
-{
-  // reset the search state
-  LastDiscrepancy[pin] = 0;
-  LastDeviceFlag[pin] = 0;
-  LastFamilyDiscrepancy[pin] = 0;
-  int i;
-  for(i = 7; ; i--) {
-    ROM_NO[pin][i] = 0;
-    if ( i == 0) break;
-  }
+void onewire_depower(int pin) {
+    gpio_enable(pin, GPIO_OUT_OPEN_DRAIN);
 }
 
-// Setup the search to find the device type 'family_code' on the next call
-// to search(*newAddr) if it is present.
-//
-void onewire_target_search(uint8_t pin, uint8_t family_code)
-{
-   // set the search state to find SearchFamily type devices
-   ROM_NO[pin][0] = family_code;
-   uint8_t i;
-   for (i = 1; i < 8; i++)
-      ROM_NO[pin][i] = 0;
-   LastDiscrepancy[pin] = 64;
-   LastFamilyDiscrepancy[pin] = 0;
-   LastDeviceFlag[pin] = 0;
+void onewire_search_start(onewire_search_t *search) {
+    // reset the search state
+    memset(search, 0, sizeof(*search));
 }
 
-// Perform a search. If this function returns a '1' then it has
-// enumerated the next device and you may retrieve the ROM from the
-// OneWire::address variable. If there are no devices, no further
+void onewire_search_prefix(onewire_search_t *search, uint8_t family_code) {
+    uint8_t i;
+
+    search->rom_no[0] = family_code;
+    for (i = 1; i < 8; i++) {
+        search->rom_no[i] = 0;
+    }
+    search->last_discrepancy = 64;
+    search->last_device_found = false;
+}
+
+// Perform a search. If the next device has been successfully enumerated, its
+// ROM address will be returned.  If there are no devices, no further
 // devices, or something horrible happens in the middle of the
-// enumeration then a 0 is returned.  If a new device is found then
-// its address is copied to newAddr.  Use OneWire::reset_search() to
+// enumeration then ONEWIRE_NONE is returned.  Use OneWire::reset_search() to
 // start over.
 //
 // --- Replaced by the one from the Dallas Semiconductor web site ---
@@ -210,129 +211,119 @@ void onewire_target_search(uint8_t pin, uint8_t family_code)
 // Return 1 : device found, ROM number in ROM_NO buffer
 //        0 : device not found, end of search
 //
-uint8_t onewire_search(uint8_t pin, uint8_t *newAddr)
-{
-   uint8_t id_bit_number;
-   uint8_t last_zero, rom_byte_number, search_result;
-   uint8_t id_bit, cmp_id_bit;
+onewire_addr_t onewire_search_next(onewire_search_t *search, int pin) {
+    //TODO: add more checking for read/write errors
+    uint8_t id_bit_number;
+    uint8_t last_zero, search_result;
+    int rom_byte_number;
+    uint8_t id_bit, cmp_id_bit;
+    onewire_addr_t addr;
+    unsigned char rom_byte_mask;
+    bool search_direction;
 
-   unsigned char rom_byte_mask, search_direction;
-
-   // initialize for search
-   id_bit_number = 1;
-   last_zero = 0;
-   rom_byte_number = 0;
-   rom_byte_mask = 1;
-   search_result = 0;
+    // initialize for search
+    id_bit_number = 1;
+    last_zero = 0;
+    rom_byte_number = 0;
+    rom_byte_mask = 1;
+    search_result = 0;
    
-   // if the last call was not the last one
-   if (!LastDeviceFlag[pin])
-   {
-      // 1-Wire reset
-      if (!onewire_reset(pin))
-      {
-         // reset the search
-         LastDiscrepancy[pin] = 0;
-         LastDeviceFlag[pin] = 0;
-         LastFamilyDiscrepancy[pin] = 0;
-         return 0;
-      }
+    // if the last call was not the last one
+    if (!search->last_device_found) {
+        // 1-Wire reset
+        if (!onewire_reset(pin)) {
+            // reset the search
+            search->last_discrepancy = 0;
+            search->last_device_found = false;
+            return ONEWIRE_NONE;
+        }
 
-      // issue the search command
-      onewire_write(pin, 0xF0, ONEWIRE_DEFAULT_POWER);
+        // issue the search command
+        onewire_write(pin, ONEWIRE_SEARCH);
 
-      // loop to do the search
-      do
-      {
-         // read a bit and its complement
-         id_bit = onewire_read_bit(pin);
-         cmp_id_bit = onewire_read_bit(pin);
+        // loop to do the search
+        do {
+            // read a bit and its complement
+            id_bit = _onewire_read_bit(pin);
+            cmp_id_bit = _onewire_read_bit(pin);
 
-         // check for no devices on 1-wire
-         if ((id_bit == 1) && (cmp_id_bit == 1))
-            break;
-         else
-         {
-            // all devices coupled have 0 or 1
-            if (id_bit != cmp_id_bit)
-               search_direction = id_bit;  // bit write value for search
-            else
-            {
-               // if this discrepancy if before the Last Discrepancy
-               // on a previous next then pick the same as last time
-               if (id_bit_number < LastDiscrepancy[pin])
-                  search_direction = ((ROM_NO[pin][rom_byte_number] & rom_byte_mask) > 0);
-               else
-                  // if equal to last pick 1, if not then pick 0
-                  search_direction = (id_bit_number == LastDiscrepancy[pin]);
+            // check for no devices on 1-wire
+            if ((id_bit < 0) || (cmp_id_bit < 0)) {
+                // Read error
+                break;
+            } else if ((id_bit == 1) && (cmp_id_bit == 1)) {
+                break;
+            } else {
+                // all devices coupled have 0 or 1
+                if (id_bit != cmp_id_bit) {
+                    search_direction = id_bit;  // bit write value for search
+                } else {
+                    // if this discrepancy if before the Last Discrepancy
+                    // on a previous next then pick the same as last time
+                    if (id_bit_number < search->last_discrepancy) {
+                        search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0);
+                    } else {
+                        // if equal to last pick 1, if not then pick 0
+                        search_direction = (id_bit_number == search->last_discrepancy);
+                    }
 
-               // if 0 was picked then record its position in LastZero
-               if (search_direction == 0)
-               {
-                  last_zero = id_bit_number;
+                    // if 0 was picked then record its position in LastZero
+                    if (!search_direction) {
+                        last_zero = id_bit_number;
+                    }
+                }
 
-                  // check for Last discrepancy in family
-                  if (last_zero < 9)
-                     LastFamilyDiscrepancy[pin] = last_zero;
-               }
+                // set or clear the bit in the ROM byte rom_byte_number
+                // with mask rom_byte_mask
+                if (search_direction) {
+                    search->rom_no[rom_byte_number] |= rom_byte_mask;
+                } else {
+                    search->rom_no[rom_byte_number] &= ~rom_byte_mask;
+                }
+
+                // serial number search direction write bit
+                _onewire_write_bit(pin, search_direction);
+
+                // increment the byte counter id_bit_number
+                // and shift the mask rom_byte_mask
+                id_bit_number++;
+                rom_byte_mask <<= 1;
+
+                // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
+                if (rom_byte_mask == 0) {
+                    rom_byte_number++;
+                    rom_byte_mask = 1;
+                }
+            }
+        } while (rom_byte_number < 8);  // loop until through all ROM bytes 0-7
+
+        // if the search was successful then
+        if (!(id_bit_number < 65)) {
+            // search successful so set last_discrepancy,last_device_found,search_result
+            search->last_discrepancy = last_zero;
+
+            // check for last device
+            if (search->last_discrepancy == 0) {
+                search->last_device_found = true;
             }
 
-            // set or clear the bit in the ROM byte rom_byte_number
-            // with mask rom_byte_mask
-            if (search_direction == 1)
-              ROM_NO[pin][rom_byte_number] |= rom_byte_mask;
-            else
-              ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask;
+            search_result = 1;
+        }
+    }
 
-            // serial number search direction write bit
-            onewire_write_bit(pin, search_direction);
-
-            // increment the byte counter id_bit_number
-            // and shift the mask rom_byte_mask
-            id_bit_number++;
-            rom_byte_mask <<= 1;
-
-            // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
-            if (rom_byte_mask == 0)
-            {
-                rom_byte_number++;
-                rom_byte_mask = 1;
-            }
-         }
-      }
-      while(rom_byte_number < 8);  // loop until through all ROM bytes 0-7
-
-      // if the search was successful then
-      if (!(id_bit_number < 65))
-      {
-         // search successful so set LastDiscrepancy,LastDeviceFlag,search_result
-         LastDiscrepancy[pin] = last_zero;
-
-         // check for last device
-         if (LastDiscrepancy[pin] == 0)
-            LastDeviceFlag[pin] = 1;
-
-         search_result = 1;
-      }
-   }
-
-   // if no device found then reset counters so next 'search' will be like a first
-   if (!search_result || !ROM_NO[pin][0])
-   {
-      LastDiscrepancy[pin] = 0;
-      LastDeviceFlag[pin] = 0;
-      LastFamilyDiscrepancy[pin] = 0;
-      search_result = 0;
-   }
-   else
-   {
-      for (rom_byte_number = 0; rom_byte_number < 8; rom_byte_number++)
-      {
-         newAddr[rom_byte_number] = ROM_NO[pin][rom_byte_number];
-         //printf("Ok I found something at %d - %x...\n",rom_byte_number, newAddr[rom_byte_number]);
-      }
-   }
-   return search_result;
+    // if no device found then reset counters so next 'search' will be like a first
+    if (!search_result || !search->rom_no[0]) {
+        search->last_discrepancy = 0;
+        search->last_device_found = false;
+        return ONEWIRE_NONE;
+    } else {
+        addr = 0;
+        for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) {
+            addr = (addr << 8) | search->rom_no[rom_byte_number];
+        }
+        //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr);
+    }
+    return addr;
 }
 
 // The 1-Wire CRC scheme is described in Maxim Application Note 27:
@@ -371,41 +362,38 @@ static const uint8_t dscrc_table[] = {
 // compared to all those delayMicrosecond() calls.  But I got
 // confused, so I use this table from the examples.)
 //
-uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
-{
-	uint8_t crc = 0;
+uint8_t onewire_crc8(const uint8_t *data, uint8_t len) {
+    uint8_t crc = 0;
 
-	while (len--) {
-		crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
-	}
-	return crc;
+    while (len--) {
+        crc = pgm_read_byte(dscrc_table + (crc ^ *data++));
+    }
+    return crc;
 }
 #else
 //
 // Compute a Dallas Semiconductor 8 bit CRC directly.
 // this is much slower, but much smaller, than the lookup table.
 //
-uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
-{
-	uint8_t crc = 0;
-	
-	while (len--) {
-		uint8_t inbyte = *addr++;
-    uint8_t i;
-		for (i = 8; i; i--) {
-			uint8_t mix = (crc ^ inbyte) & 0x01;
-			crc >>= 1;
-			if (mix) crc ^= 0x8C;
-			inbyte >>= 1;
-		}
-	}
-	return crc;
+uint8_t onewire_crc8(const uint8_t *data, uint8_t len) {
+    uint8_t crc = 0;
+    
+    while (len--) {
+        uint8_t inbyte = *data++;
+        for (int i = 8; i; i--) {
+            uint8_t mix = (crc ^ inbyte) & 0x01;
+            crc >>= 1;
+            if (mix) crc ^= 0x8C;
+            inbyte >>= 1;
+        }
+    }
+    return crc;
 }
 #endif
 
 // Compute the 1-Wire CRC16 and compare it against the received CRC.
 // Example usage (reading a DS2408):
-    //    // Put everything in a buffer so we can compute the CRC easily.
+//    // Put everything in a buffer so we can compute the CRC easily.
 //    uint8_t buf[13];
 //    buf[0] = 0xF0;    // Read PIO Registers
 //    buf[1] = 0x88;    // LSB address
@@ -423,9 +411,8 @@ uint8_t onewire_crc8(const uint8_t *addr, uint8_t len)
 //                       *not* at a 16-bit integer.
 // @param crc - The crc starting value (optional)
 // @return 1, iff the CRC matches.
-bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
-{
-    crc = ~onewire_crc16(input, len, crc);
+bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) {
+    uint16_t crc = ~onewire_crc16(input, len, crc_iv);
     return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
 }
 
@@ -441,8 +428,8 @@ bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inve
 // @param len - How many bytes to use.
 // @param crc - The crc starting value (optional)
 // @return The CRC16, as defined by Dallas Semiconductor.
-uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc)
-{
+uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) {
+    uint16_t crc = crc_iv;
     static const uint8_t oddparity[16] =
         { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
 
@@ -463,4 +450,4 @@ uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc)
       crc ^= cdata;
     }
     return crc;
-}
\ No newline at end of file
+}
diff --git a/extras/onewire/onewire.h b/extras/onewire/onewire.h
index 1a0b15d..89823d7 100644
--- a/extras/onewire/onewire.h
+++ b/extras/onewire/onewire.h
@@ -4,135 +4,232 @@
 #include <espressif/esp_misc.h> // sdk_os_delay_us
 #include "FreeRTOS.h"
 
-// 1 for keeping the parasitic power on H
-#define ONEWIRE_DEFAULT_POWER 1
+/** @file onewire.h
+ *
+ *  Routines to access devices using the Dallas Semiconductor 1-Wire(tm)
+ *  protocol.
+ */
 
-// Maximum number of devices.
-#define ONEWIRE_NUM 20
-
-// You can exclude certain features from OneWire.  In theory, this
-// might save some space.  In practice, the compiler automatically
-// removes unused code (technically, the linker, using -fdata-sections
-// and -ffunction-sections when compiling, and Wl,--gc-sections
-// when linking), so most of these will not result in any code size
-// reduction.  Well, unless you try to use the missing features
-// and redesign your program to not need them!  ONEWIRE_CRC8_TABLE
-// is the exception, because it selects a fast but large algorithm
-// or a small but slow algorithm.
-
-// Select the table-lookup method of computing the 8-bit CRC
-// by setting this to 1.  The lookup table enlarges code size by
-// about 250 bytes.  It does NOT consume RAM (but did in very
-// old versions of OneWire).  If you disable this, a slower
-// but very compact algorithm is used.
+/** Select the table-lookup method of computing the 8-bit CRC
+ *  by setting this to 1 during compilation.  The lookup table enlarges code
+ *  size by about 250 bytes.  By default, a slower but very compact algorithm
+ *  is used.
+ */
 #ifndef ONEWIRE_CRC8_TABLE
 #define ONEWIRE_CRC8_TABLE 0
 #endif
 
-// Platform specific I/O definitions
-#define noInterrupts portDISABLE_INTERRUPTS
-#define interrupts portENABLE_INTERRUPTS
-#define delayMicroseconds sdk_os_delay_us
+/** Type used to hold all 1-Wire device ROM addresses (64-bit) */
+typedef uint64_t onewire_addr_t;
 
-#define DIRECT_READ(pin)         gpio_read(pin)
-#define DIRECT_MODE_INPUT(pin)   gpio_enable(pin, GPIO_INPUT)
-#define DIRECT_MODE_OUTPUT(pin)  gpio_enable(pin, GPIO_OUTPUT)
-#define DIRECT_WRITE_LOW(pin)    gpio_write(pin, 0)
-#define DIRECT_WRITE_HIGH(pin)   gpio_write(pin, 1)
+/** Structure to contain the current state for onewire_search_next(), etc */
+typedef struct {
+    uint8_t rom_no[8];
+    uint8_t last_discrepancy;
+    bool last_device_found;
+} onewire_search_t;
 
-void onewire_init(uint8_t pin);
+/** ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device
+ *  (CRC mismatch), and so can be useful as an indicator for "no-such-device",
+ *   etc.
+ */
+#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL))
 
-// Perform a 1-Wire reset cycle. Returns 1 if a device responds
-// with a presence pulse.  Returns 0 if there is no device or the
-// bus is shorted or otherwise held low for more than 250uS
-uint8_t onewire_reset(uint8_t pin);
+/** Perform a 1-Wire reset cycle.
+ *
+ *  @param pin  The GPIO pin connected to the 1-Wire bus.
+ *
+ *  @returns `true` if at least one device responds with a presence pulse,
+ *           `false` if no devices were detected (or the bus is shorted, etc)
+ */
+bool onewire_reset(int pin);
 
-// Issue a 1-Wire rom select command, you do the reset first.
-void onewire_select(uint8_t pin, const uint8_t rom[8]);
+/** Issue a 1-Wire rom select command to select a particular device.
+ *
+ *  It is necessary to call onewire_reset() before calling this function.
+ *
+ *  @param pin   The GPIO pin connected to the 1-Wire bus.
+ *  @param addr  The ROM address of the device to select
+ *
+ *  @returns `true` if the "ROM select" command could be succesfully issued,
+ *           `false` if there was an error.
+ */
+bool onewire_select(int pin, const onewire_addr_t addr);
 
-// Issue a 1-Wire rom skip command, to address all on bus.
-void onewire_skip(uint8_t pin);
+/** Issue a 1-Wire "skip ROM" command to select *all* devices on the bus.
+ *
+ *  It is necessary to call onewire_reset() before calling this function.
+ *
+ *  @param pin   The GPIO pin connected to the 1-Wire bus.
+ *
+ *  @returns `true` if the "skip ROM" command could be succesfully issued,
+ *           `false` if there was an error.
+ */
+bool onewire_skip_rom(int pin);
 
-// Write a byte. If 'power' is one then the wire is held high at
-// the end for parasitically powered devices. You are responsible
-// for eventually depowering it by calling depower() or doing
-// another read or write.
-void onewire_write(uint8_t pin, uint8_t v, uint8_t power);
+/** Write a byte on the onewire bus.
+ *
+ *  The writing code uses open-drain mode and expects the pullup resistor to
+ *  pull the line high when not driven low.  If you need strong power after the
+ *  write (e.g. DS18B20 in parasite power mode) then call onewire_power() after
+ *  this is complete to actively drive the line high.
+ *
+ *  @param pin   The GPIO pin connected to the 1-Wire bus.
+ *  @param v     The byte value to write
+ *
+ *  @returns `true` if successful, `false` on error.
+ */
+bool onewire_write(int pin, uint8_t v);
 
-void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power);
+/** Write multiple bytes on the 1-Wire bus.
+ *
+ *  See onewire_write() for more info.
+ *
+ *  @param pin    The GPIO pin connected to the 1-Wire bus.
+ *  @param buf    A pointer to the buffer of bytes to be written
+ *  @param count  Number of bytes to write
+ *
+ *  @returns `true` if all bytes written successfully, `false` on error.
+ */
+bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count);
 
-// Read a byte.
-uint8_t onewire_read(uint8_t pin);
+/** Read a byte from a 1-Wire device.
+ *
+ *  @param pin    The GPIO pin connected to the 1-Wire bus.
+ *
+ *  @returns the read byte on success, negative value on error.
+ */
+int onewire_read(int pin);
 
-void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count);
+/** Read multiple bytes from a 1-Wire device.
+ *
+ *  @param pin    The GPIO pin connected to the 1-Wire bus.
+ *  @param buf    A pointer to the buffer to contain the read bytes
+ *  @param count  Number of bytes to read
+ *
+ *  @returns `true` on success, `false` on error.
+ */
+bool onewire_read_bytes(int pin, uint8_t *buf, size_t count);
 
-// Write a bit. The bus is always left powered at the end, see
-// note in write() about that.
-// void onewire_write_bit(uint8_t pin, uint8_t v);
+/** Actively drive the bus high to provide extra power for certain operations
+ *  of parasitically-powered devices.
+ *
+ *  For parasitically-powered devices which need more power than can be
+ *  provided via the normal pull-up resistor, it may be necessary for some
+ *  operations to drive the bus actively high.  This function can be used to
+ *  perform that operation.
+ *
+ *  The bus can be depowered once it is no longer needed by calling
+ *  onewire_depower(), or it will be depowered automatically the next time
+ *  onewire_reset() is called to start another command.
+ *
+ *  Note: Make sure the device(s) you are powering will not pull more current
+ *  than the ESP8266 is able to supply via its GPIO pins (this is especially
+ *  important when multiple devices are on the same bus and they are all
+ *  performing a power-intensive operation at the same time (i.e. multiple
+ *  DS18B20 sensors, which have all been given a "convert T" operation by using
+ *  onewire_skip_rom())).
+ *
+ *  Note: This routine will check to make sure that the bus is already high
+ *  before driving it, to make sure it doesn't attempt to drive it high while
+ *  something else is pulling it low (which could cause a reset or damage the
+ *  ESP8266).
+ *
+ *  @param pin    The GPIO pin connected to the 1-Wire bus.
+ *
+ *  @returns `true` on success, `false` on error.
+ */
+bool onewire_power(int pin);
 
-// Read a bit.
-// uint8_t onewire_read_bit(uint8_t pin);
+/** Stop forcing power onto the bus.
+ *
+ *  You only need to do this if you previously called onewire_power() to drive
+ *  the bus high and now want to allow it to float instead.  Note that
+ *  onewire_reset() will also automatically depower the bus first, so you do
+ *  not need to call this first if you just want to start a new operation.
+ *
+ *  @param pin    The GPIO pin connected to the 1-Wire bus.
+ */
+void onewire_depower(int pin);
 
-// Stop forcing power onto the bus. You only need to do this if
-// you used the 'power' flag to write() or used a write_bit() call
-// and aren't about to do another read or write. You would rather
-// not leave this powered if you don't have to, just in case
-// someone shorts your bus.
-void onewire_depower(uint8_t pin);
+/** Clear the search state so that it will start from the beginning on the next
+ *  call to onewire_search_next().
+ *
+ *  @param search  The onewire_search_t structure to reset.
+ */
+void onewire_search_start(onewire_search_t *search);
 
-// Clear the search state so that if will start from the beginning again.
-void onewire_reset_search(uint8_t pin);
+/** Setup the search to search for devices with the specified "family code".
+ *
+ *  @param search       The onewire_search_t structure to update.
+ *  @param family_code  The "family code" to search for.
+ */
+void onewire_search_prefix(onewire_search_t *search, uint8_t family_code);
 
-// Setup the search to find the device type 'family_code' on the next call
-// to search(*newAddr) if it is present.
-void onewire_target_search(uint8_t pin, uint8_t family_code);
+/** Search for the next device on the bus.
+ *
+ *  The order of returned device addresses is deterministic. You will always
+ *  get the same devices in the same order.
+ *
+ *  @returns the address of the next device on the bus, or ::ONEWIRE_NONE if
+ *  there is no next address.  ::ONEWIRE_NONE might also mean that the bus is
+ *  shorted, there are no devices, or you have already retrieved all of them.
+ *
+ *  It might be a good idea to check the CRC to make sure you didn't get
+ *  garbage.
+ */
+onewire_addr_t onewire_search_next(onewire_search_t *search, int pin);
 
-// Look for the next device. Returns 1 if a new address has been
-// returned. A zero might mean that the bus is shorted, there are
-// no devices, or you have already retrieved all of them.  It
-// might be a good idea to check the CRC to make sure you didn't
-// get garbage.  The order is deterministic. You will always get
-// the same devices in the same order.
-uint8_t onewire_search(uint8_t pin, uint8_t *newAddr);
+/** Compute a Dallas Semiconductor 8 bit CRC.
+ *
+ *  These are used in the ROM address and scratchpad registers to verify the
+ *  transmitted data is correct.
+ */
+uint8_t onewire_crc8(const uint8_t *data, uint8_t len);
 
-// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
-// ROM and scratchpad registers.
-uint8_t onewire_crc8(const uint8_t *addr, uint8_t len);
+/** Compute the 1-Wire CRC16 and compare it against the received CRC.
+ *
+ *  Example usage (reading a DS2408):
+ *  @code
+ *      // Put everything in a buffer so we can compute the CRC easily.
+ *      uint8_t buf[13];
+ *      buf[0] = 0xF0;    // Read PIO Registers
+ *      buf[1] = 0x88;    // LSB address
+ *      buf[2] = 0x00;    // MSB address
+ *      onewire_write_bytes(pin, buf, 3);    // Write 3 cmd bytes
+ *      onewire_read_bytes(pin, buf+3, 10);  // Read 6 data bytes, 2 0xFF, 2 CRC16
+ *      if (!onewire_check_crc16(buf, 11, &buf[11])) {
+ *          // TODO: Handle error.
+ *      }     
+ *  @endcode
+ *           
+ *  @param input         Array of bytes to checksum.
+ *  @param len           Number of bytes in `input`
+ *  @param inverted_crc  The two CRC16 bytes in the received data.
+ *                       This should just point into the received data,
+ *                       *not* at a 16-bit integer.
+ *  @param crc_iv        The crc starting value (optional)
+ *
+ *  @returns `true` if the CRC matches, `false` otherwise.
+ */
+bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv);
 
-// Compute the 1-Wire CRC16 and compare it against the received CRC.
-// Example usage (reading a DS2408):
-//    // Put everything in a buffer so we can compute the CRC easily.
-//    uint8_t buf[13];
-//    buf[0] = 0xF0;    // Read PIO Registers
-//    buf[1] = 0x88;    // LSB address
-//    buf[2] = 0x00;    // MSB address
-//    WriteBytes(net, buf, 3);    // Write 3 cmd bytes
-//    ReadBytes(net, buf+3, 10);  // Read 6 data bytes, 2 0xFF, 2 CRC16
-//    if (!CheckCRC16(buf, 11, &buf[11])) {
-//        // Handle error.
-//    }     
-//          
-// @param input - Array of bytes to checksum.
-// @param len - How many bytes to use.
-// @param inverted_crc - The two CRC16 bytes in the received data.
-//                       This should just point into the received data,
-//                       *not* at a 16-bit integer.
-// @param crc - The crc starting value (optional)
-// @return True, iff the CRC matches.
-bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc);
-
-// Compute a Dallas Semiconductor 16 bit CRC.  This is required to check
-// the integrity of data received from many 1-Wire devices.  Note that the
-// CRC computed here is *not* what you'll get from the 1-Wire network,
-// for two reasons:
-//   1) The CRC is transmitted bitwise inverted.
-//   2) Depending on the endian-ness of your processor, the binary
-//      representation of the two-byte return value may have a different
-//      byte order than the two bytes you get from 1-Wire.
-// @param input - Array of bytes to checksum.
-// @param len - How many bytes to use.
-// @param crc - The crc starting value (optional)
-// @return The CRC16, as defined by Dallas Semiconductor.
-uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc);
+/** Compute a Dallas Semiconductor 16 bit CRC.
+ *
+ *  This is required to check the integrity of data received from many 1-Wire
+ *  devices.  Note that the CRC computed here is *not* what you'll get from the
+ *  1-Wire network, for two reasons:
+ *    1. The CRC is transmitted bitwise inverted.
+ *    2. Depending on the endian-ness of your processor, the binary
+ *       representation of the two-byte return value may have a different
+ *       byte order than the two bytes you get from 1-Wire.
+ *
+ *  @param input   Array of bytes to checksum.
+ *  @param len     How many bytes are in `input`.
+ *  @param crc_iv  The crc starting value (optional)
+ *
+ *  @returns the CRC16, as defined by Dallas Semiconductor.
+ */
+uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv);
 
 #endif
diff --git a/extras/paho_mqtt_c/LICENSE.txt b/extras/paho_mqtt_c/LICENSE.txt
new file mode 100644
index 0000000..a6d74fb
--- /dev/null
+++ b/extras/paho_mqtt_c/LICENSE.txt
@@ -0,0 +1,32 @@
+Software License Agreement (BSD License)
+
+Copyright (c) 2015, Baoshi Zhu (www.ba0sh1.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this 
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+* Neither the name of Baoshi Zhu nor the names of its contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission from me.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Part of this project is based on Eclipse Paho which is copyrighted by IBM corp.
+Refer to the licensing information at the begining of corresponding source files.
diff --git a/extras/paho_mqtt_c/MQTTClient.c b/extras/paho_mqtt_c/MQTTClient.c
new file mode 100644
index 0000000..b964250
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTClient.c
@@ -0,0 +1,551 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include <lwip/arch.h>
+#include "MQTTClient.h"
+
+void  NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessgage) {
+    md->topic = aTopicName;
+    md->message = aMessgage;
+}
+
+
+int  getNextPacketId(MQTTClient *c) {
+    return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1;
+}
+
+
+int  sendPacket(MQTTClient* c, int length, Timer* timer)
+{
+    int rc = FAILURE,
+        sent = 0;
+
+    while (sent < length && !expired(timer))
+    {
+        rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, left_ms(timer));
+        if (rc < 0)  // there was an error writing the data
+            break;
+        sent += rc;
+    }
+    if (sent == length)
+    {
+        countdown(&(c->ping_timer), c->keepAliveInterval); // record the fact that we have successfully sent the packet
+        rc = SUCCESS;
+    }
+    else
+        rc = FAILURE;
+    return rc;
+}
+
+
+int  decodePacket(MQTTClient* c, int* value, int timeout)
+{
+    unsigned char i;
+    int multiplier = 1;
+    int len = 0;
+    const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
+
+    *value = 0;
+    do
+    {
+        int rc = MQTTPACKET_READ_ERROR;
+
+        if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+        {
+            rc = MQTTPACKET_READ_ERROR; /* bad data */
+            goto exit;
+        }
+        rc = c->ipstack->mqttread(c->ipstack, &i, 1, timeout);
+        if (rc != 1)
+            goto exit;
+        *value += (i & 127) * multiplier;
+        multiplier *= 128;
+    } while ((i & 128) != 0);
+exit:
+    return len;
+}
+
+
+int  readPacket(MQTTClient* c, Timer* timer)
+{
+    int rc = FAILURE;
+    MQTTHeader header = {0};
+    int len = 0;
+    int rem_len = 0;
+
+    /* 1. read the header byte.  This has the packet type in it */
+    if (c->ipstack->mqttread(c->ipstack, c->readbuf, 1, left_ms(timer)) != 1)
+        goto exit;
+
+    len = 1;
+    /* 2. read the remaining length.  This is variable in itself */
+    decodePacket(c, &rem_len, left_ms(timer));
+    len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
+
+    /* 3. read the rest of the buffer using a callback to supply the rest of the data */
+    if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, left_ms(timer)) != rem_len))
+        goto exit;
+
+    header.byte = c->readbuf[0];
+    rc = header.bits.type;
+exit:
+    //dmsg_printf("readPacket=%d\r\n", rc);
+    return rc;
+}
+
+
+// assume topic filter and name is in correct format
+// # can only be at end
+// + and # can only be next to separator
+char  isTopicMatched(char* topicFilter, MQTTString* topicName)
+{
+    char* curf = topicFilter;
+    char* curn = topicName->lenstring.data;
+    char* curn_end = curn + topicName->lenstring.len;
+
+    while (*curf && curn < curn_end)
+    {
+        if (*curn == '/' && *curf != '/')
+            break;
+        if (*curf != '+' && *curf != '#' && *curf != *curn)
+            break;
+        if (*curf == '+')
+        {   // skip until we meet the next separator, or end of string
+            char* nextpos = curn + 1;
+            while (nextpos < curn_end && *nextpos != '/')
+                nextpos = ++curn + 1;
+        }
+        else if (*curf == '#')
+            curn = curn_end - 1;    // skip until end of string
+        curf++;
+        curn++;
+    };
+
+    return (curn == curn_end) && (*curf == '\0');
+}
+
+
+int  deliverMessage(MQTTClient* c, MQTTString* topicName, MQTTMessage* message)
+{
+    int i;
+    int rc = FAILURE;
+
+    // we have to find the right message handler - indexed by topic
+    for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+    {
+        if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) ||
+                isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName)))
+        {
+            if (c->messageHandlers[i].fp != NULL)
+            {
+                MessageData md;
+                NewMessageData(&md, topicName, message);
+                c->messageHandlers[i].fp(&md);
+                rc = SUCCESS;
+            }
+        }
+    }
+
+    if (rc == FAILURE && c->defaultMessageHandler != NULL)
+    {
+        MessageData md;
+        NewMessageData(&md, topicName, message);
+        c->defaultMessageHandler(&md);
+        rc = SUCCESS;
+    }
+
+    return rc;
+}
+
+
+int  keepalive(MQTTClient* c)
+{
+    int rc = SUCCESS;
+
+    if (c->keepAliveInterval == 0)
+    {
+        rc = SUCCESS;
+        goto exit;
+    }
+
+    if (expired(&(c->ping_timer)))
+    {
+        if (c->ping_outstanding)
+        {
+            // if ping failure accumulated above MAX_FAIL_ALLOWED, the connection is broken
+            ++(c->fail_count);
+            if (c->fail_count >= MAX_FAIL_ALLOWED)
+            {
+                rc = DISCONNECTED;
+                goto exit;
+            }
+        }
+        else
+        {
+            Timer timer;
+            InitTimer(&timer);
+            countdown_ms(&timer, 1000);
+            c->ping_outstanding = 1;
+            int len = MQTTSerialize_pingreq(c->buf, c->buf_size);
+            if (len > 0)
+                sendPacket(c, len, &timer);
+        }
+        // re-arm ping counter
+        countdown(&(c->ping_timer), c->keepAliveInterval);
+    }
+
+exit:
+    return rc;
+}
+
+
+int  cycle(MQTTClient* c, Timer* timer)
+{
+    // read the socket, see what work is due
+    unsigned short packet_type = readPacket(c, timer);
+
+    int len = 0,
+        rc = SUCCESS;
+
+    switch (packet_type)
+    {
+        case CONNACK:
+        case PUBACK:
+        case SUBACK:
+            break;
+        case PUBLISH:
+        {
+            MQTTString topicName;
+            MQTTMessage msg;
+            if (MQTTDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
+               (unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1)
+                goto exit;
+            deliverMessage(c, &topicName, &msg);
+            if (msg.qos != QOS0)
+            {
+                if (msg.qos == QOS1)
+                    len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id);
+                else if (msg.qos == QOS2)
+                    len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id);
+                if (len <= 0)
+                    rc = FAILURE;
+                else
+                    rc = sendPacket(c, len, timer);
+                if (rc == FAILURE)
+                    goto exit; // there was a problem
+            }
+            break;
+        }
+        case PUBREC:
+        {
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
+                rc = FAILURE;
+            else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREL, 0, mypacketid)) <= 0)
+                rc = FAILURE;
+            else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet
+                rc = FAILURE; // there was a problem
+            if (rc == FAILURE)
+                goto exit; // there was a problem
+            break;
+        }
+        case PUBCOMP:
+            break;
+        case PINGRESP:
+            {
+                c->ping_outstanding = 0;
+                c->fail_count = 0;
+            }
+            break;
+    }
+    if (c->isconnected)
+        rc = keepalive(c);
+exit:
+    if (rc == SUCCESS)
+        rc = packet_type;
+    return rc;
+}
+
+
+void  NewMQTTClient(MQTTClient* c, Network* network, unsigned int command_timeout_ms, unsigned char* buf, size_t buf_size, unsigned char* readbuf, size_t readbuf_size)
+{
+    int i;
+    c->ipstack = network;
+
+    for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+        c->messageHandlers[i].topicFilter = 0;
+    c->command_timeout_ms = command_timeout_ms;
+    c->buf = buf;
+    c->buf_size = buf_size;
+    c->readbuf = readbuf;
+    c->readbuf_size = readbuf_size;
+    c->isconnected = 0;
+    c->ping_outstanding = 0;
+    c->fail_count = 0;
+    c->defaultMessageHandler = NULL;
+    InitTimer(&(c->ping_timer));
+}
+
+
+int  MQTTYield(MQTTClient* c, int timeout_ms)
+{
+    int rc = SUCCESS;
+    Timer timer;
+
+    InitTimer(&timer);
+    countdown_ms(&timer, timeout_ms);
+    while (!expired(&timer))
+    {
+        rc = cycle(c, &timer);
+        // cycle could return 0 or packet_type or 65535 if nothing is read
+        // cycle returns DISCONNECTED only if keepalive() fails.
+        if (rc == DISCONNECTED)
+            break;
+        rc = SUCCESS;
+    }
+    return rc;
+}
+
+
+// only used in single-threaded mode where one command at a time is in process
+int  waitfor(MQTTClient* c, int packet_type, Timer* timer)
+{
+    int rc = FAILURE;
+
+    do
+    {
+        if (expired(timer))
+            break; // we timed out
+    }
+    while ((rc = cycle(c, timer)) != packet_type);
+
+    return rc;
+}
+
+
+int  MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options)
+{
+    Timer connect_timer;
+    int rc = FAILURE;
+    MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
+    int len = 0;
+
+    InitTimer(&connect_timer);
+    countdown_ms(&connect_timer, c->command_timeout_ms);
+
+    if (c->isconnected) // don't send connect packet again if we are already connected
+        goto exit;
+
+    if (options == 0)
+        options = &default_options; // set default options if none were supplied
+
+    c->keepAliveInterval = options->keepAliveInterval;
+    countdown(&(c->ping_timer), c->keepAliveInterval);
+
+    if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0)
+        goto exit;
+    if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS)  // send the connect packet
+        goto exit; // there was a problem
+
+    // this will be a blocking call, wait for the connack
+    if (waitfor(c, CONNACK, &connect_timer) == CONNACK)
+    {
+        unsigned char connack_rc = 255;
+        char sessionPresent = 0;
+        if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, c->readbuf, c->readbuf_size) == 1)
+            rc = connack_rc;
+        else
+            rc = FAILURE;
+    }
+    else
+        rc = FAILURE;
+
+exit:
+    if (rc == SUCCESS)
+        c->isconnected = 1;
+    return rc;
+}
+
+
+int  MQTTSubscribe(MQTTClient* c, const char* topic, enum QoS qos, messageHandler handler)
+{
+    int rc = FAILURE;
+    Timer timer;
+    int len = 0;
+    MQTTString topicStr = MQTTString_initializer;
+    topicStr.cstring = (char *)topic;
+
+    InitTimer(&timer);
+    countdown_ms(&timer, c->command_timeout_ms);
+    if (!c->isconnected)
+        goto exit;
+
+    len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topicStr, (int*)&qos);
+    if (len <= 0)
+        goto exit;
+    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
+    {
+        goto exit;             // there was a problem
+    }
+
+    if (waitfor(c, SUBACK, &timer) == SUBACK)      // wait for suback
+    {
+        int count = 0, grantedQoS = -1;
+        unsigned short mypacketid;
+        if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, c->readbuf, c->readbuf_size) == 1)
+            rc = grantedQoS; // 0, 1, 2 or 0x80
+        if (rc != 0x80)
+        {
+            int i;
+            for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+            {
+                if (c->messageHandlers[i].topicFilter == 0)
+                {
+                    c->messageHandlers[i].topicFilter = topic;
+                    c->messageHandlers[i].fp = handler;
+                    rc = 0;
+                    break;
+                }
+            }
+        }
+    }
+    else
+        rc = FAILURE;
+
+exit:
+    return rc;
+}
+
+
+int  MQTTUnsubscribe(MQTTClient* c, const char* topicFilter)
+{
+    int rc = FAILURE;
+    Timer timer;
+    MQTTString topic = MQTTString_initializer;
+    topic.cstring = (char *)topicFilter;
+    int len = 0;
+
+    InitTimer(&timer);
+    countdown_ms(&timer, c->command_timeout_ms);
+
+    if (!c->isconnected)
+        goto exit;
+
+    if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0)
+        goto exit;
+    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
+        goto exit; // there was a problem
+
+    if (waitfor(c, UNSUBACK, &timer) == UNSUBACK)
+    {
+        unsigned short mypacketid;  // should be the same as the packetid above
+        if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1)
+            rc = 0;
+    }
+    else
+        rc = FAILURE;
+
+exit:
+    return rc;
+}
+
+
+int  MQTTPublish(MQTTClient* c, const char* topic, MQTTMessage* message)
+{
+    int rc = FAILURE;
+    Timer timer;
+    MQTTString topicStr = MQTTString_initializer;
+    topicStr.cstring = (char *)topic;
+    int len = 0;
+
+    InitTimer(&timer);
+    countdown_ms(&timer, c->command_timeout_ms);
+
+    if (!c->isconnected)
+        goto exit;
+
+    if (message->qos == QOS1 || message->qos == QOS2)
+        message->id = getNextPacketId(c);
+
+    len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id, 
+              topicStr, (unsigned char*)message->payload, message->payloadlen);
+    if (len <= 0)
+        goto exit;
+    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
+    {
+        goto exit; // there was a problem
+    }
+
+    if (message->qos == QOS1)
+    {
+        if (waitfor(c, PUBACK, &timer) == PUBACK)
+        {
+            // We still can receive from broker, treat as recoverable
+            c->fail_count = 0;
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
+                rc = FAILURE;
+            else
+                rc = SUCCESS;
+        }
+        else
+        {
+            rc = FAILURE;
+        }
+
+    }
+    else if (message->qos == QOS2)
+    {
+        if (waitfor(c, PUBCOMP, &timer) == PUBCOMP)
+        {
+            // We still can receive from broker, treat as recoverable
+            c->fail_count = 0;
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)
+                rc = FAILURE;
+            else
+                rc = SUCCESS;
+        }
+        else
+        {
+            rc = FAILURE;
+        }
+    }
+
+exit:
+    return rc;
+}
+
+
+int  MQTTDisconnect(MQTTClient* c)
+{
+    int rc = FAILURE;
+    Timer timer;     // we might wait for incomplete incoming publishes to complete
+    int len = MQTTSerialize_disconnect(c->buf, c->buf_size);
+
+    InitTimer(&timer);
+    countdown_ms(&timer, c->command_timeout_ms);
+
+    if (len > 0)
+        rc = sendPacket(c, len, &timer);            // send the disconnect packet
+
+    c->isconnected = 0;
+    return rc;
+}
+
diff --git a/extras/paho_mqtt_c/MQTTClient.h b/extras/paho_mqtt_c/MQTTClient.h
new file mode 100644
index 0000000..f7ea424
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTClient.h
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#ifndef __MQTT_CLIENT_C_
+#define __MQTT_CLIENT_C_
+
+#include "MQTTPacket.h"
+#include "MQTTESP8266.h"
+
+#define MAX_PACKET_ID 65535
+#define MAX_MESSAGE_HANDLERS 5
+#define MAX_FAIL_ALLOWED  2
+
+enum QoS { QOS0, QOS1, QOS2 };
+
+// all failure return codes must be negative
+enum returnCode {DISCONNECTED = -3, BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
+
+void NewTimer(Timer*);
+
+typedef struct _MQTTMessage
+{
+    enum QoS qos;
+    char retained;
+    char dup;
+    unsigned short id;
+    void *payload;
+    size_t payloadlen;
+} MQTTMessage;
+
+typedef struct _MessageData
+{
+    MQTTString* topic;
+    MQTTMessage* message;
+} MessageData;
+
+typedef void (*messageHandler)(MessageData*);
+
+struct _MQTTClient
+{
+    unsigned int next_packetid;
+    unsigned int command_timeout_ms;
+    size_t buf_size, readbuf_size;
+    unsigned char *buf;
+    unsigned char *readbuf;
+    unsigned int keepAliveInterval;
+    char ping_outstanding;
+    int fail_count;
+    int isconnected;
+
+    struct MessageHandlers
+    {
+        const char* topicFilter;
+        void (*fp) (MessageData*);
+    } messageHandlers[MAX_MESSAGE_HANDLERS];      // Message handlers are indexed by subscription topic
+
+    void (*defaultMessageHandler) (MessageData*);
+
+    Network* ipstack;
+    Timer ping_timer;
+};
+
+
+typedef struct _MQTTClient MQTTClient;
+
+
+int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options);
+int MQTTPublish(MQTTClient* c, const char* topic, MQTTMessage* message);
+int MQTTSubscribe(MQTTClient* c, const char* topic, enum QoS qos, messageHandler handler);
+int MQTTUnsubscribe(MQTTClient* c, const char* topic);
+int MQTTDisconnect(MQTTClient* c);
+int MQTTYield(MQTTClient* c, int timeout_ms);
+
+void NewMQTTClient(MQTTClient*, Network*, unsigned int, unsigned char*, size_t, unsigned char*, size_t);
+
+#define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0}
+
+#endif
diff --git a/extras/paho_mqtt_c/MQTTConnect.h b/extras/paho_mqtt_c/MQTTConnect.h
new file mode 100644
index 0000000..3d8addd
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTConnect.h
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTCONNECT_H_
+#define MQTTCONNECT_H_
+
+#if !defined(DLLImport)
+  #define DLLImport
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+
+typedef union
+{
+	unsigned char all;	/**< all connect flags */
+#if defined(REVERSED)
+	struct
+	{
+		unsigned int username : 1;			/**< 3.1 user name */
+		unsigned int password : 1; 			/**< 3.1 password */
+		unsigned int willRetain : 1;		/**< will retain setting */
+		unsigned int willQoS : 2;				/**< will QoS value */
+		unsigned int will : 1;			    /**< will flag */
+		unsigned int cleansession : 1;	  /**< clean session flag */
+		unsigned int : 1;	  	          /**< unused */
+	} bits;
+#else
+	struct
+	{
+		unsigned int : 1;	     					/**< unused */
+		unsigned int cleansession : 1;	  /**< cleansession flag */
+		unsigned int will : 1;			    /**< will flag */
+		unsigned int willQoS : 2;				/**< will QoS value */
+		unsigned int willRetain : 1;		/**< will retain setting */
+		unsigned int password : 1; 			/**< 3.1 password */
+		unsigned int username : 1;			/**< 3.1 user name */
+	} bits;
+#endif
+} MQTTConnectFlags;	/**< connect flags byte */
+
+
+
+/**
+ * Defines the MQTT "Last Will and Testament" (LWT) settings for
+ * the connect packet.
+ */
+typedef struct
+{
+	/** The eyecatcher for this structure.  must be MQTW. */
+	char struct_id[4];
+	/** The version number of this structure.  Must be 0 */
+	int struct_version;
+	/** The LWT topic to which the LWT message will be published. */
+	MQTTString topicName;
+	/** The LWT payload. */
+	MQTTString message;
+	/**
+      * The retained flag for the LWT message (see MQTTAsync_message.retained).
+      */
+	unsigned char retained;
+	/**
+      * The quality of service setting for the LWT message (see
+      * MQTTAsync_message.qos and @ref qos).
+      */
+	char qos;
+} MQTTPacket_willOptions;
+
+
+#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
+
+
+typedef struct
+{
+	/** The eyecatcher for this structure.  must be MQTC. */
+	char struct_id[4];
+	/** The version number of this structure.  Must be 0 */
+	int struct_version;
+	/** Version of MQTT to be used.  3 = 3.1 4 = 3.1.1
+	  */
+	unsigned char MQTTVersion;
+	MQTTString clientID;
+	unsigned short keepAliveInterval;
+	unsigned char cleansession;
+	unsigned char willFlag;
+	MQTTPacket_willOptions will;
+	MQTTString username;
+	MQTTString password;
+} MQTTPacket_connectData;
+
+typedef union
+{
+	unsigned char all;	/**< all connack flags */
+#if defined(REVERSED)
+	struct
+	{
+		unsigned int sessionpresent : 1;    /**< session present flag */
+		unsigned int : 7;	  	          /**< unused */
+	} bits;
+#else
+	struct
+	{
+		unsigned int : 7;	     			/**< unused */
+		unsigned int sessionpresent : 1;    /**< session present flag */
+	} bits;
+#endif
+} MQTTConnackFlags;	/**< connack flags byte */
+
+#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
+		MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
+
+DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
+DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
+DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
+
+DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
+DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
+
+#endif /* MQTTCONNECT_H_ */
diff --git a/extras/paho_mqtt_c/MQTTConnectClient.c b/extras/paho_mqtt_c/MQTTConnectClient.c
new file mode 100644
index 0000000..4c1e862
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTConnectClient.c
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
+  * @param options the options to be used to build the connect packet
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int  MQTTSerialize_connectLength(MQTTPacket_connectData* options)
+{
+	int len = 0;
+
+	FUNC_ENTRY;
+
+	if (options->MQTTVersion == 3)
+		len = 12; /* variable depending on MQTT or MQIsdp */
+	else if (options->MQTTVersion == 4)
+		len = 10;
+
+	len += MQTTstrlen(options->clientID)+2;
+	if (options->willFlag)
+		len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
+	if (options->username.cstring || options->username.lenstring.data)
+		len += MQTTstrlen(options->username)+2;
+	if (options->password.cstring || options->password.lenstring.data)
+		len += MQTTstrlen(options->password)+2;
+
+	FUNC_EXIT_RC(len);
+	return len;
+}
+
+
+/**
+  * Serializes the connect options into the buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param len the length in bytes of the supplied buffer
+  * @param options the options to be used to build the connect packet
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	MQTTConnectFlags flags = {0};
+	int len = 0;
+	int rc = -1;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = CONNECT;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
+
+	if (options->MQTTVersion == 4)
+	{
+		writeCString(&ptr, "MQTT");
+		writeChar(&ptr, (char) 4);
+	}
+	else
+	{
+		writeCString(&ptr, "MQIsdp");
+		writeChar(&ptr, (char) 3);
+	}
+
+	flags.all = 0;
+	flags.bits.cleansession = options->cleansession;
+	flags.bits.will = (options->willFlag) ? 1 : 0;
+	if (flags.bits.will)
+	{
+		flags.bits.willQoS = options->will.qos;
+		flags.bits.willRetain = options->will.retained;
+	}
+
+	if (options->username.cstring || options->username.lenstring.data)
+		flags.bits.username = 1;
+	if (options->password.cstring || options->password.lenstring.data)
+		flags.bits.password = 1;
+
+	writeChar(&ptr, flags.all);
+	writeInt(&ptr, options->keepAliveInterval);
+	writeMQTTString(&ptr, options->clientID);
+	if (options->willFlag)
+	{
+		writeMQTTString(&ptr, options->will.topicName);
+		writeMQTTString(&ptr, options->will.message);
+	}
+	if (flags.bits.username)
+		writeMQTTString(&ptr, options->username);
+	if (flags.bits.password)
+		writeMQTTString(&ptr, options->password);
+
+	rc = ptr - buf;
+
+	exit: FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Deserializes the supplied (wire) buffer into connack data - return code
+  * @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
+  * @param connack_rc returned integer value of the connack return code
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param len the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int  MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+	MQTTConnackFlags flags = {0};
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != CONNACK)
+		goto exit;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+		goto exit;
+
+	flags.all = readChar(&curdata);
+	*sessionPresent = flags.bits.sessionpresent;
+	*connack_rc = readChar(&curdata);
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @param packettype the message type
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
+{
+	MQTTHeader header = {0};
+	int rc = -1;
+	unsigned char *ptr = buf;
+
+	FUNC_ENTRY;
+	if (buflen < 2)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = packettype;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_disconnect(unsigned char* buf, int buflen)
+{
+	return MQTTSerialize_zero(buf, buflen, DISCONNECT);
+}
+
+
+/**
+  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_pingreq(unsigned char* buf, int buflen)
+{
+	return MQTTSerialize_zero(buf, buflen, PINGREQ);
+}
diff --git a/extras/paho_mqtt_c/MQTTDeserializePublish.c b/extras/paho_mqtt_c/MQTTDeserializePublish.c
new file mode 100644
index 0000000..21b7314
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTDeserializePublish.c
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+#include <string.h>
+
+#define min(a, b) ((a < b) ? 1 : 0)
+
+/**
+  * Deserializes the supplied (wire) buffer into publish data
+  * @param dup returned integer - the MQTT dup flag
+  * @param qos returned integer - the MQTT QoS value
+  * @param retained returned integer - the MQTT retained flag
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param topicName returned MQTTString - the MQTT topic in the publish
+  * @param payload returned byte buffer - the MQTT publish payload
+  * @param payloadlen returned integer - the length of the MQTT payload
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success
+  */
+int  MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
+		unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen = 0;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != PUBLISH)
+		goto exit;
+	*dup = header.bits.dup;
+	*qos = header.bits.qos;
+	*retained = header.bits.retain;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	if (!readMQTTLenString(topicName, &curdata, enddata) ||
+		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
+		goto exit;
+
+	if (*qos > 0)
+		*packetid = readInt(&curdata);
+
+	*payloadlen = enddata - curdata;
+	*payload = curdata;
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Deserializes the supplied (wire) buffer into an ack
+  * @param packettype returned integer - the MQTT packet type
+  * @param dup returned integer - the MQTT dup flag
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int  MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	*dup = header.bits.dup;
+	*packettype = header.bits.type;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	if (enddata - curdata < 2)
+		goto exit;
+	*packetid = readInt(&curdata);
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
diff --git a/extras/paho_mqtt_c/MQTTESP8266.c b/extras/paho_mqtt_c/MQTTESP8266.c
new file mode 100644
index 0000000..09e645b
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTESP8266.c
@@ -0,0 +1,187 @@
+/**
+  ******************************************************************************
+  * @file    MQTTESP8266.c
+  * @author  Baoshi <mail(at)ba0sh1(dot)com>
+  * @version 0.1
+  * @date    Sep 9, 2015
+  * @brief   Eclipse Paho ported to ESP8266 RTOS
+  *
+  ******************************************************************************
+  * @copyright
+  *
+  * Copyright (c) 2015, Baoshi Zhu. All rights reserved.
+  * Use of this source code is governed by a BSD-style license that can be
+  * found in the LICENSE.txt file.
+  *
+  * THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
+  * WARRANTY.  IN NO EVENT WILL THE AUTHOR(S) BE HELD LIABLE FOR ANY DAMAGES
+  * ARISING FROM THE USE OF THIS SOFTWARE.
+  *
+  */
+
+#include <espressif/esp_common.h>
+#include <lwip/sockets.h>
+#include <lwip/inet.h>
+#include <lwip/netdb.h>
+#include <lwip/sys.h>
+#include <string.h>
+
+#include "MQTTESP8266.h"
+
+char  expired(Timer* timer)
+{
+    portTickType now = xTaskGetTickCount();
+    int32_t left = timer->end_time - now;
+    return (left < 0);
+}
+
+
+void  countdown_ms(Timer* timer, unsigned int timeout)
+{
+    portTickType now = xTaskGetTickCount();
+    timer->end_time = now + timeout / portTICK_RATE_MS;
+}
+
+
+void  countdown(Timer* timer, unsigned int timeout)
+{
+    countdown_ms(timer, timeout * 1000);
+}
+
+
+int  left_ms(Timer* timer)
+{
+    portTickType now = xTaskGetTickCount();
+    int32_t left = timer->end_time - now;
+    return (left < 0) ? 0 : left / portTICK_RATE_MS;
+}
+
+
+void  InitTimer(Timer* timer)
+{
+    timer->end_time = 0;
+}
+
+
+
+int  mqtt_esp_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
+{
+    struct timeval tv;
+    fd_set fdset;
+    int rc = 0;
+    int rcvd = 0;
+    FD_ZERO(&fdset);
+    FD_SET(n->my_socket, &fdset);
+    // It seems tv_sec actually means FreeRTOS tick
+    tv.tv_sec = timeout_ms / portTICK_RATE_MS;
+    tv.tv_usec = 0;
+    rc = select(n->my_socket + 1, &fdset, 0, 0, &tv);
+    if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset)))
+    {
+        rcvd = recv(n->my_socket, buffer, len, 0);
+    }
+    else
+    {
+        // select fail
+        return -1;
+    }
+    return rcvd;
+}
+
+
+int  mqtt_esp_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
+{
+    struct timeval tv;
+    fd_set fdset;
+    int rc = 0;
+
+    FD_ZERO(&fdset);
+    FD_SET(n->my_socket, &fdset);
+    // It seems tv_sec actually means FreeRTOS tick
+    tv.tv_sec = timeout_ms / portTICK_RATE_MS;
+    tv.tv_usec = 0;
+    rc = select(n->my_socket + 1, 0, &fdset, 0, &tv);
+    if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset)))
+    {
+        rc = send(n->my_socket, buffer, len, 0);
+    }
+    else
+    {
+        // select fail
+        return -1;
+    }
+    return rc;
+}
+
+
+
+void  NewNetwork(Network* n)
+{
+    n->my_socket = -1;
+    n->mqttread = mqtt_esp_read;
+    n->mqttwrite = mqtt_esp_write;
+}
+
+static int  host2addr(const char *hostname , struct in_addr *in)
+{
+    struct addrinfo hints, *servinfo, *p;
+    struct sockaddr_in *h;
+    int rv;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+    rv = getaddrinfo(hostname, 0 , &hints , &servinfo);
+    if (rv != 0)
+    {
+        return rv;
+    }
+
+    // loop through all the results and get the first resolve
+    for (p = servinfo; p != 0; p = p->ai_next)
+    {
+        h = (struct sockaddr_in *)p->ai_addr;
+        in->s_addr = h->sin_addr.s_addr;
+    }
+    freeaddrinfo(servinfo); // all done with this structure
+    return 0;
+}
+
+
+int  ConnectNetwork(Network* n, const char* host, int port)
+{
+    struct sockaddr_in addr;
+    int ret;
+
+    if (host2addr(host, &(addr.sin_addr)) != 0)
+    {
+        return -1;
+    }
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+
+    n->my_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if( n->my_socket < 0 )
+    {
+        // error
+        return -1;
+    }
+    ret = connect(n->my_socket, ( struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+    if( ret < 0 )
+    {
+        // error
+        close(n->my_socket);
+        return ret;
+    }
+
+    return ret;
+}
+
+
+int  DisconnectNetwork(Network* n)
+{
+    close(n->my_socket);
+    n->my_socket = -1;
+    return 0;
+}
diff --git a/extras/paho_mqtt_c/MQTTESP8266.h b/extras/paho_mqtt_c/MQTTESP8266.h
new file mode 100644
index 0000000..f29921e
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTESP8266.h
@@ -0,0 +1,59 @@
+/**
+  ******************************************************************************
+  * @file    MQTTESP8266.h
+  * @author  Baoshi <mail(at)ba0sh1(dot)com>
+  * @version 0.1
+  * @date    Sep 9, 2015
+  * @brief   Eclipse Paho ported to ESP8266 RTOS
+  *
+  ******************************************************************************
+  * @copyright
+  *
+  * Copyright (c) 2015, Baoshi Zhu. All rights reserved.
+  * Use of this source code is governed by a BSD-style license that can be
+  * found in the LICENSE.txt file.
+  *
+  * THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED
+  * WARRANTY.  IN NO EVENT WILL THE AUTHOR(S) BE HELD LIABLE FOR ANY DAMAGES
+  * ARISING FROM THE USE OF THIS SOFTWARE.
+  *
+  */
+#ifndef _MQTT_ESP8266_H_
+#define _MQTT_ESP8266_H_
+
+#include <FreeRTOS.h>
+#include <portmacro.h>
+
+typedef struct Timer Timer;
+
+struct Timer
+{
+    portTickType end_time;
+};
+
+typedef struct Network Network;
+
+struct Network
+{
+	int my_socket;
+	int (*mqttread) (Network*, unsigned char*, int, int);
+	int (*mqttwrite) (Network*, unsigned char*, int, int);
+};
+
+char expired(Timer*);
+void countdown_ms(Timer*, unsigned int);
+void countdown(Timer*, unsigned int);
+int left_ms(Timer*);
+
+void InitTimer(Timer*);
+
+int mqtt_esp_read(Network*, unsigned char*, int, int);
+int mqtt_esp_write(Network*, unsigned char*, int, int);
+void mqtt_esp_disconnect(Network*);
+
+void NewNetwork(Network* n);
+int ConnectNetwork(Network* n, const char* host, int port);
+int DisconnectNetwork(Network* n);
+
+
+#endif /* _MQTT_ESP8266_H_ */
diff --git a/extras/paho_mqtt_c/MQTTFormat.h b/extras/paho_mqtt_c/MQTTFormat.h
new file mode 100644
index 0000000..47b0c41
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTFormat.h
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#if !defined(MQTTFORMAT_H)
+#define MQTTFORMAT_H
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+
+const char* MQTTPacket_getName(unsigned short packetid);
+int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
+int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
+int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
+		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
+int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
+int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
+		MQTTString topicFilters[], int requestedQoSs[]);
+int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
+int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[]);
+char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
+char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
+
+#endif
diff --git a/extras/paho_mqtt_c/MQTTPacket.c b/extras/paho_mqtt_c/MQTTPacket.c
new file mode 100644
index 0000000..eb36a50
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTPacket.c
@@ -0,0 +1,409 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Sergio R. Caprile - non-blocking packet read functions for stream transport
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+#include <string.h>
+
+/**
+ * Encodes the message length according to the MQTT algorithm
+ * @param buf the buffer into which the encoded data is written
+ * @param length the length to be encoded
+ * @return the number of bytes written to buffer
+ */
+int  MQTTPacket_encode(unsigned char* buf, int length)
+{
+	int rc = 0;
+
+	FUNC_ENTRY;
+	do
+	{
+		char d = length % 128;
+		length /= 128;
+		/* if there are more digits to encode, set the top bit of this digit */
+		if (length > 0)
+			d |= 0x80;
+		buf[rc++] = d;
+	} while (length > 0);
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+ * Decodes the message length according to the MQTT algorithm
+ * @param getcharfn pointer to function to read the next character from the data source
+ * @param value the decoded length returned
+ * @return the number of bytes read from the socket
+ */
+int  MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
+{
+	unsigned char c;
+	int multiplier = 1;
+	int len = 0;
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
+
+	FUNC_ENTRY;
+	*value = 0;
+	do
+	{
+		int rc = MQTTPACKET_READ_ERROR;
+
+		if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+		{
+			rc = MQTTPACKET_READ_ERROR;	/* bad data */
+			goto exit;
+		}
+		rc = (*getcharfn)(&c, 1);
+		if (rc != 1)
+			goto exit;
+		*value += (c & 127) * multiplier;
+		multiplier *= 128;
+	} while ((c & 128) != 0);
+exit:
+	FUNC_EXIT_RC(len);
+	return len;
+}
+
+
+int  MQTTPacket_len(int rem_len)
+{
+	rem_len += 1; /* header byte */
+
+	/* now remaining_length field */
+	if (rem_len < 128)
+		rem_len += 1;
+	else if (rem_len < 16384)
+		rem_len += 2;
+	else if (rem_len < 2097151)
+		rem_len += 3;
+	else
+		rem_len += 4;
+	return rem_len;
+}
+
+
+static unsigned char* bufptr;
+
+int  bufchar(unsigned char* c, int count)
+{
+	int i;
+
+	for (i = 0; i < count; ++i)
+		*c = *bufptr++;
+	return count;
+}
+
+
+int  MQTTPacket_decodeBuf(unsigned char* buf, int* value)
+{
+	bufptr = buf;
+	return MQTTPacket_decode(bufchar, value);
+}
+
+
+/**
+ * Calculates an integer from two bytes read from the input buffer
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the integer value calculated
+ */
+int  readInt(unsigned char** pptr)
+{
+	unsigned char* ptr = *pptr;
+	int len = 256*(*ptr) + (*(ptr+1));
+	*pptr += 2;
+	return len;
+}
+
+
+/**
+ * Reads one character from the input buffer.
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the character read
+ */
+char  readChar(unsigned char** pptr)
+{
+	char c = **pptr;
+	(*pptr)++;
+	return c;
+}
+
+
+/**
+ * Writes one character to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param c the character to write
+ */
+void  writeChar(unsigned char** pptr, char c)
+{
+	**pptr = c;
+	(*pptr)++;
+}
+
+
+/**
+ * Writes an integer as 2 bytes to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param anInt the integer to write
+ */
+void  writeInt(unsigned char** pptr, int anInt)
+{
+	**pptr = (unsigned char)(anInt / 256);
+	(*pptr)++;
+	**pptr = (unsigned char)(anInt % 256);
+	(*pptr)++;
+}
+
+
+/**
+ * Writes a "UTF" string to an output buffer.  Converts C string to length-delimited.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param string the C string to write
+ */
+void  writeCString(unsigned char** pptr, const char* string)
+{
+	int len = strlen(string);
+	writeInt(pptr, len);
+	memcpy(*pptr, string, len);
+	*pptr += len;
+}
+
+
+int  getLenStringLen(char* ptr)
+{
+	int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
+	return len;
+}
+
+
+void  writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
+{
+	if (mqttstring.lenstring.len > 0)
+	{
+		writeInt(pptr, mqttstring.lenstring.len);
+		memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
+		*pptr += mqttstring.lenstring.len;
+	}
+	else if (mqttstring.cstring)
+		writeCString(pptr, mqttstring.cstring);
+	else
+		writeInt(pptr, 0);
+}
+
+
+/**
+ * @param mqttstring the MQTTString structure into which the data is to be read
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param enddata pointer to the end of the data: do not read beyond
+ * @return 1 if successful, 0 if not
+ */
+int  readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
+{
+	int rc = 0;
+
+	FUNC_ENTRY;
+	/* the first two bytes are the length of the string */
+	if (enddata - (*pptr) > 1) /* enough length to read the integer? */
+	{
+		mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
+		if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
+		{
+			mqttstring->lenstring.data = (char*)*pptr;
+			*pptr += mqttstring->lenstring.len;
+			rc = 1;
+		}
+	}
+	mqttstring->cstring = NULL;
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+ * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
+ * @param mqttstring the string to return the length of
+ * @return the length of the string
+ */
+int  MQTTstrlen(MQTTString mqttstring)
+{
+	int rc = 0;
+
+	if (mqttstring.cstring)
+		rc = strlen(mqttstring.cstring);
+	else
+		rc = mqttstring.lenstring.len;
+	return rc;
+}
+
+
+/**
+ * Compares an MQTTString to a C string
+ * @param a the MQTTString to compare
+ * @param bptr the C string to compare
+ * @return boolean - equal or not
+ */
+int  MQTTPacket_equals(MQTTString* a, char* bptr)
+{
+	int alen = 0,
+		blen = 0;
+	char *aptr;
+
+	if (a->cstring)
+	{
+		aptr = a->cstring;
+		alen = strlen(a->cstring);
+	}
+	else
+	{
+		aptr = a->lenstring.data;
+		alen = a->lenstring.len;
+	}
+	blen = strlen(bptr);
+
+	return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
+}
+
+
+/**
+ * Helper function to read packet data from some source into a buffer
+ * @param buf the buffer into which the packet will be serialized
+ * @param buflen the length in bytes of the supplied buffer
+ * @param getfn pointer to a function which will read any number of bytes from the needed source
+ * @return integer MQTT packet type, or -1 on error
+ * @note  the whole message must fit into the caller's buffer
+ */
+int  MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
+{
+	int rc = -1;
+	MQTTHeader header = {0};
+	int len = 0;
+	int rem_len = 0;
+
+	/* 1. read the header byte.  This has the packet type in it */
+	if ((*getfn)(buf, 1) != 1)
+		goto exit;
+
+	len = 1;
+	/* 2. read the remaining length.  This is variable in itself */
+	MQTTPacket_decode(getfn, &rem_len);
+	len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
+
+	/* 3. read the rest of the buffer using a callback to supply the rest of the data */
+	if((rem_len + len) > buflen)
+		goto exit;
+	if ((*getfn)(buf + len, rem_len) != rem_len)
+		goto exit;
+
+	header.byte = buf[0];
+	rc = header.bits.type;
+exit:
+	return rc;
+}
+
+/**
+ * Decodes the message length according to the MQTT algorithm, non-blocking
+ * @param trp pointer to a transport structure holding what is needed to solve getting data from it
+ * @param value the decoded length returned
+ * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
+ */
+static int  MQTTPacket_decodenb(MQTTTransport *trp)
+{
+	unsigned char c;
+	int rc = MQTTPACKET_READ_ERROR;
+
+	FUNC_ENTRY;
+	if(trp->len == 0){		/* initialize on first call */
+		trp->multiplier = 1;
+		trp->rem_len = 0;
+	}
+	do {
+		int frc;
+		if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+			goto exit;
+		if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
+			goto exit;
+		if (frc == 0){
+			rc = 0;
+			goto exit;
+		}
+		trp->rem_len += (c & 127) * trp->multiplier;
+		trp->multiplier *= 128;
+	} while ((c & 128) != 0);
+	rc = trp->len;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+/**
+ * Helper function to read packet data from some source into a buffer, non-blocking
+ * @param buf the buffer into which the packet will be serialized
+ * @param buflen the length in bytes of the supplied buffer
+ * @param trp pointer to a transport structure holding what is needed to solve getting data from it
+ * @return integer MQTT packet type, 0 for call again, or -1 on error
+ * @note  the whole message must fit into the caller's buffer
+ */
+int  MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
+{
+	int rc = -1, frc;
+	MQTTHeader header = {0};
+
+	switch(trp->state){
+	default:
+		trp->state = 0;
+		/*FALLTHROUGH*/
+	case 0:
+		/* read the header byte.  This has the packet type in it */
+		if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
+			goto exit;
+		if (frc == 0)
+			return 0;
+		trp->len = 0;
+		++trp->state;
+		/*FALLTHROUGH*/
+		/* read the remaining length.  This is variable in itself */
+	case 1:
+		if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
+			goto exit;
+		if(frc == 0)
+			return 0;
+		trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
+		if((trp->rem_len + trp->len) > buflen)
+			goto exit;
+		++trp->state;
+		/*FALLTHROUGH*/
+	case 2:
+		/* read the rest of the buffer using a callback to supply the rest of the data */
+		if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
+			goto exit;
+		if (frc == 0)
+			return 0;
+		trp->rem_len -= frc;
+		trp->len += frc;
+		if(trp->rem_len)
+			return 0;
+
+		header.byte = buf[0];
+		rc = header.bits.type;
+		break;
+	}
+
+exit:
+	trp->state = 0;
+	return rc;
+}
+
diff --git a/extras/paho_mqtt_c/MQTTPacket.h b/extras/paho_mqtt_c/MQTTPacket.h
new file mode 100644
index 0000000..c7909d9
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTPacket.h
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTPACKET_H_
+#define MQTTPACKET_H_
+
+#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
+extern "C" {
+#endif
+
+#if defined(WIN32_DLL) || defined(WIN64_DLL)
+  #define DLLImport __declspec(dllimport)
+  #define DLLExport __declspec(dllexport)
+#elif defined(LINUX_SO)
+  #define DLLImport extern
+  #define DLLExport  __attribute__ ((visibility ("default")))
+#else
+  #define DLLImport
+  #define DLLExport 
+#endif
+
+enum errors
+{
+	MQTTPACKET_BUFFER_TOO_SHORT = -2,
+	MQTTPACKET_READ_ERROR = -1,
+	MQTTPACKET_READ_COMPLETE
+};
+
+enum msgTypes
+{
+	CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
+	PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
+	PINGREQ, PINGRESP, DISCONNECT
+};
+
+/**
+ * Bitfields for the MQTT header byte.
+ */
+typedef union
+{
+	unsigned char byte;	                /**< the whole byte */
+#if defined(REVERSED)
+	struct
+	{
+		unsigned int type : 4;			/**< message type nibble */
+		unsigned int dup : 1;				/**< DUP flag bit */
+		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
+		unsigned int retain : 1;		/**< retained flag bit */
+	} bits;
+#else
+	struct
+	{
+		unsigned int retain : 1;		/**< retained flag bit */
+		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
+		unsigned int dup : 1;				/**< DUP flag bit */
+		unsigned int type : 4;			/**< message type nibble */
+	} bits;
+#endif
+} MQTTHeader;
+
+typedef struct
+{
+	int len;
+	char* data;
+} MQTTLenString;
+
+typedef struct
+{
+	char* cstring;
+	MQTTLenString lenstring;
+} MQTTString;
+
+#define MQTTString_initializer {NULL, {0, NULL}}
+
+int MQTTstrlen(MQTTString mqttstring);
+
+#include "MQTTConnect.h"
+#include "MQTTPublish.h"
+#include "MQTTSubscribe.h"
+#include "MQTTUnsubscribe.h"
+#include "MQTTFormat.h"
+
+int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
+int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
+
+int MQTTPacket_len(int rem_len);
+int MQTTPacket_equals(MQTTString* a, char* b);
+
+int MQTTPacket_encode(unsigned char* buf, int length);
+int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
+int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
+
+int readInt(unsigned char** pptr);
+char readChar(unsigned char** pptr);
+void writeChar(unsigned char** pptr, char c);
+void writeInt(unsigned char** pptr, int anInt);
+int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
+void writeCString(unsigned char** pptr, const char* string);
+void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
+
+DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
+
+typedef struct {
+	int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
+	void *sck;	/* pointer to whatever the system may use to identify the transport */
+	int multiplier;
+	int rem_len;
+	int len;
+	char state;
+}MQTTTransport;
+
+int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
+
+#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
+}
+#endif
+
+
+#endif /* MQTTPACKET_H_ */
diff --git a/extras/paho_mqtt_c/MQTTPublish.h b/extras/paho_mqtt_c/MQTTPublish.h
new file mode 100644
index 0000000..ffd8752
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTPublish.h
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTPUBLISH_H_
+#define MQTTPUBLISH_H_
+
+#if !defined(DLLImport)
+  #define DLLImport
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
+		MQTTString topicName, unsigned char* payload, int payloadlen);
+
+DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
+		unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
+DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
+DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
+
+#endif /* MQTTPUBLISH_H_ */
diff --git a/extras/paho_mqtt_c/MQTTSerializePublish.c b/extras/paho_mqtt_c/MQTTSerializePublish.c
new file mode 100644
index 0000000..c3343c8
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTSerializePublish.c
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+
+/**
+  * Determines the length of the MQTT publish packet that would be produced using the supplied parameters
+  * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
+  * @param topicName the topic name to be used in the publish
+  * @param payloadlen the length of the payload to be sent
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int  MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
+{
+	int len = 0;
+
+	len += 2 + MQTTstrlen(topicName) + payloadlen;
+	if (qos > 0)
+		len += 2; /* packetid */
+	return len;
+}
+
+
+/**
+  * Serializes the supplied publish data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param qos integer - the MQTT QoS value
+  * @param retained integer - the MQTT retained flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param topicName MQTTString - the MQTT topic in the publish
+  * @param payload byte buffer - the MQTT publish payload
+  * @param payloadlen integer - the length of the MQTT payload
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int   MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
+		MQTTString topicName, unsigned char* payload, int payloadlen)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.bits.type = PUBLISH;
+	header.bits.dup = dup;
+	header.bits.qos = qos;
+	header.bits.retain = retained;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeMQTTString(&ptr, topicName);
+
+	if (qos > 0)
+		writeInt(&ptr, packetid);
+
+	memcpy(ptr, payload, payloadlen);
+	ptr += payloadlen;
+
+	rc = ptr - buf;
+
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Serializes the ack packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param type the MQTT packet type
+  * @param dup the MQTT dup flag
+  * @param packetid the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
+{
+	MQTTHeader header = {0};
+	int rc = 0;
+	unsigned char *ptr = buf;
+
+	FUNC_ENTRY;
+	if (buflen < 4)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.bits.type = packettype;
+	header.bits.dup = dup;
+	header.bits.qos = (packettype == PUBREL) ? 1 : 0;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
+	writeInt(&ptr, packetid);
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a puback packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid);
+}
+
+
+/**
+  * Serializes a pubrel packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid);
+}
+
+
+/**
+  * Serializes a pubrel packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int  MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid);
+}
+
+
diff --git a/extras/paho_mqtt_c/MQTTSubscribe.h b/extras/paho_mqtt_c/MQTTSubscribe.h
new file mode 100644
index 0000000..9b8511d
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTSubscribe.h
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTSUBSCRIBE_H_
+#define MQTTSUBSCRIBE_H_
+
+#if !defined(DLLImport)
+  #define DLLImport
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[], int requestedQoSs[]);
+
+DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
+		int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
+
+DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
+
+
+#endif /* MQTTSUBSCRIBE_H_ */
diff --git a/extras/paho_mqtt_c/MQTTSubscribeClient.c b/extras/paho_mqtt_c/MQTTSubscribeClient.c
new file mode 100644
index 0000000..a76a4d3
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTSubscribeClient.c
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
+  * @param count the number of topic filter strings in topicFilters
+  * @param topicFilters the array of topic filter strings to be used in the publish
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int  MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
+{
+	int i;
+	int len = 2; /* packetid */
+
+	for (i = 0; i < count; ++i)
+		len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
+	return len;
+}
+
+
+/**
+  * Serializes the supplied subscribe data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied bufferr
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param count - number of members in the topicFilters and reqQos arrays
+  * @param topicFilters - array of topic filter names
+  * @param requestedQoSs - array of requested QoS
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int  MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
+		MQTTString topicFilters[], int requestedQoSs[])
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = 0;
+	int i = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = SUBSCRIBE;
+	header.bits.dup = dup;
+	header.bits.qos = 1;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeInt(&ptr, packetid);
+
+	for (i = 0; i < count; ++i)
+	{
+		writeMQTTString(&ptr, topicFilters[i]);
+		writeChar(&ptr, requestedQoSs[i]);
+	}
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Deserializes the supplied (wire) buffer into suback data
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param maxcount - the maximum number of members allowed in the grantedQoSs array
+  * @param count returned integer - number of members in the grantedQoSs array
+  * @param grantedQoSs returned array of integers - the granted qualities of service
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int  MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != SUBACK)
+		goto exit;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+		goto exit;
+
+	*packetid = readInt(&curdata);
+
+	*count = 0;
+	while (curdata < enddata)
+	{
+		if (*count > maxcount)
+		{
+			rc = -1;
+			goto exit;
+		}
+		grantedQoSs[(*count)++] = readChar(&curdata);
+	}
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
diff --git a/extras/paho_mqtt_c/MQTTUnsubscribe.h b/extras/paho_mqtt_c/MQTTUnsubscribe.h
new file mode 100644
index 0000000..2f8e829
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTUnsubscribe.h
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTUNSUBSCRIBE_H_
+#define MQTTUNSUBSCRIBE_H_
+
+#if !defined(DLLImport)
+  #define DLLImport
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[]);
+
+DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
+		unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
+
+DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
+
+#endif /* MQTTUNSUBSCRIBE_H_ */
diff --git a/extras/paho_mqtt_c/MQTTUnsubscribeClient.c b/extras/paho_mqtt_c/MQTTUnsubscribeClient.c
new file mode 100644
index 0000000..40b4733
--- /dev/null
+++ b/extras/paho_mqtt_c/MQTTUnsubscribeClient.c
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+#include <espressif/esp_common.h>
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
+  * @param count the number of topic filter strings in topicFilters
+  * @param topicFilters the array of topic filter strings to be used in the publish
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int  MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
+{
+	int i;
+	int len = 2; /* packetid */
+
+	for (i = 0; i < count; ++i)
+		len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
+	return len;
+}
+
+
+/**
+  * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param count - number of members in the topicFilters array
+  * @param topicFilters - array of topic filter names
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int  MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[])
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = -1;
+	int i = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = UNSUBSCRIBE;
+	header.bits.dup = dup;
+	header.bits.qos = 1;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeInt(&ptr, packetid);
+
+	for (i = 0; i < count; ++i)
+		writeMQTTString(&ptr, topicFilters[i]);
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Deserializes the supplied (wire) buffer into unsuback data
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int  MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
+{
+	unsigned char type = 0;
+	unsigned char dup = 0;
+	int rc = 0;
+
+	FUNC_ENTRY;
+	rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
+	if (type == UNSUBACK)
+		rc = 1;
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
diff --git a/extras/paho_mqtt_c/StackTrace.h b/extras/paho_mqtt_c/StackTrace.h
new file mode 100644
index 0000000..517d437
--- /dev/null
+++ b/extras/paho_mqtt_c/StackTrace.h
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - fix for bug #434081
+ *******************************************************************************/
+
+#ifndef STACKTRACE_H_
+#define STACKTRACE_H_
+
+#define NOSTACKTRACE 1
+
+#if defined(NOSTACKTRACE)
+#define FUNC_ENTRY
+#define FUNC_ENTRY_NOLOG
+#define FUNC_ENTRY_MED
+#define FUNC_ENTRY_MAX
+#define FUNC_EXIT
+#define FUNC_EXIT_NOLOG
+#define FUNC_EXIT_MED
+#define FUNC_EXIT_MAX
+#define FUNC_EXIT_RC(x)
+#define FUNC_EXIT_MED_RC(x)
+#define FUNC_EXIT_MAX_RC(x)
+
+#else
+
+#if defined(WIN32)
+#define inline __inline
+#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
+#else
+#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
+
+void StackTrace_entry(const char* name, int line, int trace);
+void StackTrace_exit(const char* name, int line, void* return_value, int trace);
+
+void StackTrace_printStack(FILE* dest);
+char* StackTrace_get(unsigned long);
+
+#endif
+
+#endif
+
+
+
+
+#endif /* STACKTRACE_H_ */
diff --git a/extras/paho_mqtt_c/component.mk b/extras/paho_mqtt_c/component.mk
new file mode 100644
index 0000000..6ecf410
--- /dev/null
+++ b/extras/paho_mqtt_c/component.mk
@@ -0,0 +1,9 @@
+# Component makefile for extras/paho_mqtt_c
+
+# expected anyone using bmp driver includes it as 'paho_mqtt_c/MQTT*.h'
+INC_DIRS += $(paho_mqtt_c_ROOT)..
+
+# args for passing into compile rule generation
+paho_mqtt_c_SRC_DIR =  $(paho_mqtt_c_ROOT)
+
+$(eval $(call component_compile_rules,paho_mqtt_c))
diff --git a/extras/rboot-ota/ota-tftp.c b/extras/rboot-ota/ota-tftp.c
index f8140f6..d772927 100644
--- a/extras/rboot-ota/ota-tftp.c
+++ b/extras/rboot-ota/ota-tftp.c
@@ -22,7 +22,8 @@
 #include <espressif/esp_system.h>
 
 #include "ota-tftp.h"
-#include "rboot-ota.h"
+#include "rboot.h"
+#include "rboot-api.h"
 
 #define TFTP_FIRMWARE_FILE "firmware.bin"
 #define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
@@ -112,7 +113,7 @@ static void tftp_task(void *listen_port)
         netbuf_delete(netbuf);
 
         /* Find next free slot - this requires flash unmapping so best done when no packets in flight */
-        rboot_config_t conf;
+        rboot_config conf;
         conf = rboot_get_config();
         int slot = (conf.current_rom + 1) % conf.count;
 
diff --git a/extras/rboot-ota/rboot-api.c b/extras/rboot-ota/rboot-api.c
new file mode 100644
index 0000000..7fe5340
--- /dev/null
+++ b/extras/rboot-ota/rboot-api.c
@@ -0,0 +1,214 @@
+//////////////////////////////////////////////////
+// rBoot OTA and config API for ESP8266.
+// Copyright 2015 Richard A Burton
+// richardaburton@gmail.com
+// See license.txt for license terms.
+// OTA code based on SDK sample from Espressif.
+//////////////////////////////////////////////////
+
+#include <rboot.h>
+#include <string.h>
+//#include <c_types.h>
+//#include <spi_flash.h>
+
+// detect rtos sdk (not ideal method!)
+#ifdef IRAM_ATTR
+#define os_free(s)   vPortFree(s)
+#define os_malloc(s) pvPortMalloc(s)
+#else
+#include <mem.h>
+#endif
+
+#ifdef RBOOT_INTEGRATION
+#include <rboot-integration.h>
+#endif
+
+#include "rboot-api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(BOOT_CONFIG_CHKSUM) || defined(BOOT_RTC_ENABLED)
+// calculate checksum for block of data
+// from start up to (but excluding) end
+static uint8 calc_chksum(uint8 *start, uint8 *end) {
+	uint8 chksum = CHKSUM_INIT;
+	while(start < end) {
+		chksum ^= *start;
+		start++;
+	}
+	return chksum;
+}
+#endif
+
+// get the rboot config
+rboot_config ICACHE_FLASH_ATTR rboot_get_config(void) {
+	rboot_config conf;
+	spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)&conf, sizeof(rboot_config));
+	return conf;
+}
+
+// write the rboot config
+// preserves the contents of the rest of the sector,
+// so the rest of the sector can be used to store user data
+// updates checksum automatically (if enabled)
+bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf) {
+	uint8 *buffer;
+	buffer = (uint8*)os_malloc(SECTOR_SIZE);
+	if (!buffer) {
+		//os_printf("No ram!\r\n");
+		return false;
+	}
+	
+#ifdef BOOT_CONFIG_CHKSUM
+	conf->chksum = calc_chksum((uint8*)conf, (uint8*)&conf->chksum);
+#endif
+	
+	spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
+	memcpy(buffer, conf, sizeof(rboot_config));
+	vPortEnterCritical();
+	spi_flash_erase_sector(BOOT_CONFIG_SECTOR);
+	vPortExitCritical();
+	taskYIELD();
+	vPortEnterCritical();
+	//spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
+	spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32*)((void*)buffer), SECTOR_SIZE);
+	vPortExitCritical();	
+	os_free(buffer);
+	return true;
+}
+
+// get current boot rom
+uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void) {
+	rboot_config conf;
+	conf = rboot_get_config();
+	return conf.current_rom;
+}
+
+// set current boot rom
+bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom) {
+	rboot_config conf;
+	conf = rboot_get_config();
+	if (rom >= conf.count) return false;
+	conf.current_rom = rom;
+	return rboot_set_config(&conf);
+}
+
+// create the write status struct, based on supplied start address
+rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr) {
+	rboot_write_status status = {0};
+	status.start_addr = start_addr;
+	status.start_sector = start_addr / SECTOR_SIZE;
+	status.last_sector_erased = status.start_sector - 1;
+	//status.max_sector_count = 200;
+	//os_printf("init addr: 0x%08x\r\n", start_addr);
+	
+	return status;
+}
+
+// function to do the actual writing to flash
+// call repeatedly with more data (max len per write is the flash sector size (4k))
+bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len) {
+	
+	bool ret = false;
+	uint8 *buffer;
+	int32 lastsect;
+	
+	if (data == NULL || len == 0) {
+		return true;
+	}
+	
+	// get a buffer
+	buffer = (uint8 *)os_malloc(len + status->extra_count);
+	if (!buffer) {
+		//os_printf("No ram!\r\n");
+		return false;
+	}
+
+	// copy in any remaining bytes from last chunk
+	memcpy(buffer, status->extra_bytes, status->extra_count);
+	// copy in new data
+	memcpy(buffer + status->extra_count, data, len);
+
+	// calculate length, must be multiple of 4
+	// save any remaining bytes for next go
+	len += status->extra_count;
+	status->extra_count = len % 4;
+	len -= status->extra_count;
+	memcpy(status->extra_bytes, buffer + len, status->extra_count);
+
+	// check data will fit
+	//if (status->start_addr + len < (status->start_sector + status->max_sector_count) * SECTOR_SIZE) {
+
+		// erase any additional sectors needed by this chunk
+		lastsect = ((status->start_addr + len) - 1) / SECTOR_SIZE;
+		while (lastsect > status->last_sector_erased) {
+			status->last_sector_erased++;
+			spi_flash_erase_sector(status->last_sector_erased);
+		}
+
+		// write current chunk
+		//os_printf("write addr: 0x%08x, len: 0x%04x\r\n", status->start_addr, len);
+		if (spi_flash_write(status->start_addr, (uint32 *)((void*)buffer), len) == SPI_FLASH_RESULT_OK) {
+			ret = true;
+			status->start_addr += len;
+		}
+	//}
+
+	os_free(buffer);
+	return ret;
+}
+
+#ifdef BOOT_RTC_ENABLED
+bool ICACHE_FLASH_ATTR rboot_get_rtc_data(rboot_rtc_data *rtc) {
+	if (system_rtc_mem_read(RBOOT_RTC_ADDR, rtc, sizeof(rboot_rtc_data))) {
+		return (rtc->chksum == calc_chksum((uint8*)rtc, (uint8*)&rtc->chksum));
+	}
+	return false;
+}
+
+bool ICACHE_FLASH_ATTR rboot_set_rtc_data(rboot_rtc_data *rtc) {
+	// calculate checksum
+	rtc->chksum = calc_chksum((uint8*)rtc, (uint8*)&rtc->chksum);
+	return system_rtc_mem_write(RBOOT_RTC_ADDR, rtc, sizeof(rboot_rtc_data));
+}
+
+bool ICACHE_FLASH_ATTR rboot_set_temp_rom(uint8 rom) {
+	rboot_rtc_data rtc;
+	// invalid data in rtc?
+	if (!rboot_get_rtc_data(&rtc)) {
+		// set basics
+		rtc.magic = RBOOT_RTC_MAGIC;
+		rtc.last_mode = MODE_STANDARD;
+		rtc.last_rom = 0;
+	}
+	// set next boot to temp mode with specified rom
+	rtc.next_mode = MODE_TEMP_ROM;
+	rtc.temp_rom = rom;
+
+	return rboot_set_rtc_data(&rtc);
+}
+
+bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom) {
+	rboot_rtc_data rtc;
+	if (rboot_get_rtc_data(&rtc)) {
+		*rom = rtc.last_rom;
+		return true;
+	}
+	return false;
+}
+
+bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
+	rboot_rtc_data rtc;
+	if (rboot_get_rtc_data(&rtc)) {
+		*mode = rtc.last_mode;
+		return true;
+	}
+	return false;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/extras/rboot-ota/rboot-api.h b/extras/rboot-ota/rboot-api.h
new file mode 100644
index 0000000..5a39b4d
--- /dev/null
+++ b/extras/rboot-ota/rboot-api.h
@@ -0,0 +1,136 @@
+#ifndef __RBOOT_API_H__
+#define __RBOOT_API_H__
+
+/** @defgroup rboot rBoot API
+ *  @brief      rBoot for ESP8266 API allows runtime code to access the rBoot configuration.
+ *              Configuration may be read to use within the main firmware or updated to
+ *              affect next boot behavior.
+ *  @copyright  2015 Richard A Burton
+ *  @author     richardaburton@gmail.com
+ *  @author     OTA code based on SDK sample from Espressif
+ *  @license    See licence.txt for license terms.
+ *  @{
+*/
+
+#include <rboot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**	@brief  Structure defining flash write status
+ *  @note   The user application should not modify the contents of this
+ *          structure.
+ *	@see    rboot_write_flash
+*/
+typedef struct {
+	uint32 start_addr;
+	uint32 start_sector;
+	//uint32 max_sector_count;
+	int32 last_sector_erased;
+	uint8 extra_count;
+	uint8 extra_bytes[4];
+} rboot_write_status;
+
+/**	@brief	Read rBoot configuration from flash
+ *	@retval rboot_config Copy of the rBoot configuration
+ *  @note   Returns rboot_config (defined in rboot.h) allowing you to modify any values
+ *          in it, including the ROM layout.
+*/
+rboot_config ICACHE_FLASH_ATTR rboot_get_config(void);
+
+/**	@brief	Write rBoot configuration to flash memory
+ *	@param	conf pointer to a rboot_config structure containing configuration to save
+ *	@retval bool True on success
+ *  @note   Saves the rboot_config structure back to configuration sector (BOOT_CONFIG_SECTOR)
+ *          of the flash, while maintaining the contents of the rest of the sector.
+ *          You can use the rest of this sector for your app settings, as long as you
+ *          protect this structure when you do so.
+*/
+bool ICACHE_FLASH_ATTR rboot_set_config(rboot_config *conf);
+
+/** @brief  Get index of current ROM
+ *  @retval uint8 Index of the current ROM
+ *  @note   Get the currently selected boot ROM (this will be the currently
+ *          running ROM, as long as you haven't changed it since boot or rBoot
+ *          booted the rom in temporary boot mode, see rboot_get_last_boot_rom).
+*/
+uint8 ICACHE_FLASH_ATTR rboot_get_current_rom(void);
+
+/**	@brief	Set the index of current ROM
+ *	@param	rom The index of the ROM to use on next boot
+ *	@retval	bool True on success
+ *  @note   Set the current boot ROM, which will be used when next restarted.
+ *  @note   This function re-writes the whole configuration to flash memory (not just the current ROM index)
+*/
+bool ICACHE_FLASH_ATTR rboot_set_current_rom(uint8 rom);
+
+/**	@brief  Initialise flash write process
+ *	@param  start_addr Address on the SPI flash to begin write to
+ *  @note   Call once before starting to pass data to write to flash memory with rboot_write_flash function.
+ *          start_addr is the address on the SPI flash to write from. Returns a status structure which
+ *          must be passed back on each write. The contents of the structure should not
+ *          be modified by the calling code.
+*/
+rboot_write_status ICACHE_FLASH_ATTR rboot_write_init(uint32 start_addr);
+
+/**	@brief  Write data to flash memory
+ *	@param  status Pointer to rboot_write_status structure defining the write status
+ *  @param  data Pointer to a block of uint8 data elements to be written to flash
+ *  @param  len Quantity of uint8 data elements to write to flash
+ *  @note   Call repeatedly to write data to the flash, starting at the address
+ *  specified on the prior call to rboot_write_init. Current write position is
+ *  tracked automatically. This method is likely to be called each time a packet
+ *  of OTA data is received over the network.
+ *  @note   Call rboot_write_init before calling this function to get the rboot_write_status structure
+*/
+bool ICACHE_FLASH_ATTR rboot_write_flash(rboot_write_status *status, uint8 *data, uint16 len);
+
+#ifdef BOOT_RTC_ENABLED
+/** @brief  Get rBoot status/control data from RTC data area
+ *  @param  rtc Pointer to a rboot_rtc_data structure to be populated
+ *  @retval bool True on success, false if no data/invalid checksum (in which
+ *          case do not use the contents of the structure)
+*/
+bool ICACHE_FLASH_ATTR rboot_get_rtc_data(rboot_rtc_data *rtc);
+
+/** @brief  Set rBoot status/control data in RTC data area
+ *  @param  rtc pointer to a rboot_rtc_data structure
+ *  @retval bool True on success
+ *  @note   The checksum will be calculated automatically for you.
+*/
+bool ICACHE_FLASH_ATTR rboot_set_rtc_data(rboot_rtc_data *rtc);
+
+/** @brief  Set temporary rom for next boot
+ *  @param  rom Rom slot number for next boot
+ *  @retval bool True on success
+ *  @note   This call will tell rBoot to temporarily boot the specified rom on
+ *          the next boot. This is does not update the stored rBoot config on
+ *          the flash, so after another reset it will boot back to the original
+ *          rom.
+*/
+bool ICACHE_FLASH_ATTR rboot_set_temp_rom(uint8 rom);
+
+/** @brief  Get the last booted rom slot number
+ *  @param  rom Pointer to rom slot number variable to populate
+ *  @retval bool True on success, false if no data/invalid checksum
+ *  @note   This will find the currently running rom, even if booted as a
+ *          temporary rom.
+*/
+bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom);
+
+/** @brief  Get the last boot mode
+ *  @param  mode Pointer to mode variable to populate
+ *  @retval bool True on success, false if no data/invalid checksum
+ *  @note   This will indicate the type of boot: MODE_STANDARD, MODE_GPIO_ROM or
+ *          MODE_TEMP_ROM.
+*/
+bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+#endif
diff --git a/extras/rboot-ota/rboot-ota.c b/extras/rboot-ota/rboot-integration.c
similarity index 69%
rename from extras/rboot-ota/rboot-ota.c
rename to extras/rboot-ota/rboot-integration.c
index c8b27c4..266d566 100644
--- a/extras/rboot-ota/rboot-ota.c
+++ b/extras/rboot-ota/rboot-integration.c
@@ -10,17 +10,10 @@
 #include <FreeRTOS.h>
 #include <task.h>
 
-#include "rboot-ota.h"
+#include <rboot.h>
 
 #define ROM_MAGIC_OLD 0xe9
 #define ROM_MAGIC_NEW 0xea
-#define CHECKSUM_INIT 0xef
-
-#if 0
-#define RBOOT_DEBUG(f_, ...) printf((f_), __VA_ARGS__)
-#else
-#define RBOOT_DEBUG(f_, ...)
-#endif
 
 typedef struct __attribute__((packed)) {
     uint8_t magic;
@@ -35,67 +28,6 @@ typedef struct __attribute__((packed)) {
 } section_header_t;
 
 
-// get the rboot config
-rboot_config_t rboot_get_config() {
-	rboot_config_t conf;
-	sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)&conf, sizeof(rboot_config_t));
-	return conf;
-}
-
-// write the rboot config
-// preserves contents of rest of sector, so rest
-// of sector can be used to store user data
-// updates checksum automatically, if enabled
-bool rboot_set_config(rboot_config_t *conf) {
-	uint8_t *buffer;
-#ifdef RBOOT_CONFIG_CHKSUM
-	uint8_t chksum;
-	uint8_t *ptr;
-#endif
-
-	buffer = (uint8_t*)malloc(SECTOR_SIZE);
-	if (!buffer) {
-		printf("rboot_set_config: Failed to allocate sector buffer\r\n");
-		return false;
-	}
-
-#ifdef BOOT_CONFIG_CHKSUM
-	chksum = CHKSUM_INIT;
-	for (ptr = (uint8_t*)conf; ptr < &conf->chksum; ptr++) {
-		chksum ^= *ptr;
-	}
-	conf->chksum = chksum;
-#endif
-
-	sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
-	memcpy(buffer, conf, sizeof(rboot_config_t));
-        vPortEnterCritical();
-	sdk_spi_flash_erase_sector(BOOT_CONFIG_SECTOR);
-        vPortExitCritical();
-        taskYIELD();
-        vPortEnterCritical();
-	sdk_spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE);
-        vPortExitCritical();
-	free(buffer);
-	return true;
-}
-
-// get current boot rom
-uint8_t rboot_get_current_rom() {
-	rboot_config_t conf;
-	conf = rboot_get_config();
-	return conf.current_rom;
-}
-
-// set current boot rom
-bool rboot_set_current_rom(uint8_t rom) {
-	rboot_config_t conf;
-	conf = rboot_get_config();
-	if (rom >= conf.count) return false;
-	conf.current_rom = rom;
-	return rboot_set_config(&conf);
-}
-
 // Check that a valid-looking rboot image is found at this offset on the flash, and
 // takes up 'expected_length' bytes.
 bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
@@ -120,7 +52,7 @@ bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **
 
     int remaining_sections = image_header.section_count;
 
-    uint8_t checksum = CHECKSUM_INIT;
+    uint8_t checksum = CHKSUM_INIT;
 
     while(remaining_sections > 0 && offset < end_offset)
     {
diff --git a/extras/rboot-ota/rboot-integration.h b/extras/rboot-ota/rboot-integration.h
new file mode 100644
index 0000000..00c1bcb
--- /dev/null
+++ b/extras/rboot-ota/rboot-integration.h
@@ -0,0 +1,34 @@
+// The rboot project provides this file for making rboot fit other projects
+
+#ifndef __RBOOT_INTEGRATION_H__
+#define __RBOOT_INTEGRATION_H__
+#include <stdint.h>
+#include <stdbool.h>
+#include <espressif/spi_flash.h>
+#include <espressif/esp_system.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define int32 int32_t
+#define ICACHE_FLASH_ATTR
+#define spi_flash_read sdk_spi_flash_read
+#define spi_flash_erase_sector sdk_spi_flash_erase_sector
+#define spi_flash_write sdk_spi_flash_write
+#define os_malloc malloc
+#define os_free free
+#define system_rtc_mem_read sdk_system_rtc_mem_read
+#define system_rtc_mem_write sdk_system_rtc_mem_write
+
+#if 0
+#define RBOOT_DEBUG(f_, ...) printf((f_), __VA_ARGS__)
+#else
+#define RBOOT_DEBUG(f_, ...)
+#endif
+
+// Check that a valid-looking rboot image is found at this offset on the flash, and
+// takes up 'expected_length' bytes.
+bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
+#endif // __RBOOT_INTEGRATION_H__
diff --git a/extras/rboot-ota/rboot-ota.h b/extras/rboot-ota/rboot-ota.h
deleted file mode 100644
index 5586a30..0000000
--- a/extras/rboot-ota/rboot-ota.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef __RBOOT_OTA_H__
-#define __RBOOT_OTA_H__
-/* rboot-ota client API
- *
- * Ported from https://github.com/raburton/esp8266/ to esp-open-rtos
- *
- * BSD Licensed as per the file LICENSE in the top-level directory.
- * Copyright (c) 2015 Richard A Burton & SuperHouse Pty Ltd
- */
-#include <stdint.h>
-#include <stdbool.h>
-#include <rboot-config.h>
-
-/* rboot config block structure (stored in flash offset 0x1000)
- *
- * Structure taken from rboot.h revision a4724ede22
- * The version of rboot you're using has to match this structure
- */
-typedef struct {
-        uint8_t magic;               // our magic
-        uint8_t version;             // config struct version
-        uint8_t mode;                        // boot loader mode
-        uint8_t current_rom;         // currently selected rom
-        uint8_t gpio_rom;            // rom to use for gpio boot
-        uint8_t count;               // number of roms in use
-        uint8_t unused[2];           // padding
-        uint32_t roms[RBOOT_MAX_ROMS]; // flash addresses of the roms
-#ifdef RBOOT_CONFIG_CHKSUM
-        uint8_t chksum;              // config chksum
-#endif
-} rboot_config_t;
-
-
-#define SECTOR_SIZE 0x1000
-#define BOOT_CONFIG_SECTOR 1
-
-// timeout for the initial connect (in ms)
-#define OTA_CONNECT_TIMEOUT  10000
-
-// timeout for the download and flash to complete (in ms), once connected
-#define OTA_DOWNLOAD_TIMEOUT 20000
-
-#define UPGRADE_FLAG_IDLE		0x00
-#define UPGRADE_FLAG_START		0x01
-#define UPGRADE_FLAG_FINISH		0x02
-
-#define FLASH_BY_ADDR 0xff
-
-rboot_config_t rboot_get_config();
-bool rboot_set_config(rboot_config_t *conf);
-uint8_t rboot_get_current_rom();
-bool rboot_set_current_rom(uint8_t rom);
-bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
-
-#endif
diff --git a/extras/rboot-ota/rboot.h b/extras/rboot-ota/rboot.h
new file mode 100644
index 0000000..e09aa7e
--- /dev/null
+++ b/extras/rboot-ota/rboot.h
@@ -0,0 +1,111 @@
+#ifndef __RBOOT_H__
+#define __RBOOT_H__
+
+//////////////////////////////////////////////////
+// rBoot open source boot loader for ESP8266.
+// Copyright 2015 Richard A Burton
+// richardaburton@gmail.com
+// See license.txt for license terms.
+//////////////////////////////////////////////////
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RBOOT_INTEGRATION
+
+// uncomment to use only c code
+// if you aren't using gcc you may need to do this
+//#define BOOT_NO_ASM
+
+// uncomment to have a checksum on the boot config
+//#define BOOT_CONFIG_CHKSUM
+
+// uncomment to enable big flash support (>1MB)
+#define BOOT_BIG_FLASH
+
+// uncomment to enable 2 way communication between
+// rBoot and the user app via the esp rtc data area
+#define BOOT_RTC_ENABLED
+
+// uncomment to enable GPIO booting
+//#define BOOT_GPIO_ENABLED
+
+// uncomment to include .irom0.text section in the checksum
+// roms must be built with esptool2 using -iromchksum option
+//#define BOOT_IROM_CHKSUM
+
+// uncomment to add a boot delay, allows you time to connect
+// a terminal before rBoot starts to run and output messages
+// value is in microseconds
+//#define BOOT_DELAY_MICROS 2000000
+
+// increase if required
+#define MAX_ROMS 4
+
+#define CHKSUM_INIT 0xef
+
+#define SECTOR_SIZE 0x1000
+#define BOOT_CONFIG_SECTOR 1
+
+#define BOOT_CONFIG_MAGIC 0xe1
+#define BOOT_CONFIG_VERSION 0x01
+
+#define MODE_STANDARD 0x00
+#define MODE_GPIO_ROM 0x01
+#define MODE_TEMP_ROM 0x02
+
+#define RBOOT_RTC_MAGIC 0x2334ae68
+#define RBOOT_RTC_READ 1
+#define RBOOT_RTC_WRITE 0
+#define RBOOT_RTC_ADDR 64
+
+#ifdef RBOOT_INTEGRATION
+#include <rboot-integration.h>
+#endif
+
+/** @brief  Structure containing rBoot configuration
+ *  @note   ROM addresses must be multiples of 0x1000 (flash sector aligned).
+ *          Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will
+ *          be memory mapped so ROM slots containing .irom0.text sections must
+ *          remain below 0x100000. Slots beyond this will only be accessible via
+ *          spi read calls, so use these for stored resources, not code. With
+ *          BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so
+ *          ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.
+ *  @ingroup rboot
+*/
+typedef struct {
+	uint8 magic;           ///< Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC
+	uint8 version;         ///< Version of configuration structure - should be BOOT_CONFIG_VERSION
+	uint8 mode;            ///< Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM)
+	uint8 current_rom;     ///< Currently selected ROM (will be used for next standard boot)
+	uint8 gpio_rom;        ///< ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM
+	uint8 count;           ///< Quantity of ROMs available to boot
+	uint8 unused[2];       ///< Padding (not used)
+	uint32 roms[MAX_ROMS]; ///< Flash addresses of each ROM
+#ifdef BOOT_CONFIG_CHKSUM
+	uint8 chksum;          ///< Checksum of this configuration structure (if BOOT_CONFIG_CHKSUM defined)
+#endif
+} rboot_config;
+
+#ifdef BOOT_RTC_ENABLED
+/** @brief  Structure containing rBoot status/control data
+ *  @note   This structure is used to, optionally, communicate between rBoot and
+ *          the user app. It is stored in the ESP RTC data area.
+ *  @ingroup rboot
+*/
+typedef struct {
+	uint32 magic;           ///< Magic, identifies rBoot RTC data - should be RBOOT_RTC_MAGIC
+	uint8 next_mode;        ///< The next boot mode, defaults to MODE_STANDARD - can be set to MODE_TEMP_ROM
+	uint8 last_mode;        ///< The last (this) boot mode - can be MODE_STANDARD, MODE_GPIO_ROM or MODE_TEMP_ROM
+	uint8 last_rom;         ///< The last (this) boot rom number
+	uint8 temp_rom;         ///< The next boot rom number when next_mode set to MODE_TEMP_ROM
+	uint8 chksum;           ///< Checksum of this structure this will be updated for you passed to the API
+} rboot_rtc_data;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/extras/rboot-ota/readme.txt b/extras/rboot-ota/readme.txt
index 8721112..e5513e9 100644
--- a/extras/rboot-ota/readme.txt
+++ b/extras/rboot-ota/readme.txt
@@ -1,4 +1,4 @@
-*NOTE: This rboot-ota and the TFTP server ota-tftp.h are specific to esp-open-rtos. The below Makefile is from the upstream rboot-ota project.*
+*NOTE: This rboot-ota and the TFTP server ota-tftp.h are specific to esp-open-rtos. The below Makefile is from the upstream rboot-ota project and the rboot code is taken from #75ca33b.*
 
 For more details on OTA in esp-open-rtos, see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
 
diff --git a/include/ssid_config.h b/include/ssid_config.h
index edad5a9..a13e09a 100644
--- a/include/ssid_config.h
+++ b/include/ssid_config.h
@@ -20,7 +20,7 @@
 //   https://www.kernel.org/pub/software/scm/git/docs/git-update-index.html
 //
 
-#warning "You need to enter your wifi credentials in this file and follow the instructions here to keep the password safe from Github commits."
+#error "You need to enter your wifi credentials in this file and follow the instructions here to keep the password safe from Github commits."
 
 #ifndef __SSID_CONFIG_H__
 #define __SSID_CONFIG_H__
diff --git a/ld/common.ld b/ld/common.ld
index 1413c1c..cc8a8ce 100644
--- a/ld/common.ld
+++ b/ld/common.ld
@@ -139,13 +139,34 @@ SECTIONS
        (except for libgcc which is matched above.)
     */
     *(.literal .text .literal.* .text.*)
+    /* Anything explicitly marked as "irom" or "irom0" should go here */
+    *(.irom.* .irom.*.* .irom0.*)
+    _irom0_text_end = ABSOLUTE(.);
+
+    /* Temporary .rodata hacks start here, eventually all rodata will
+    be in irom by default */
     /* mbedtls rodata */
     *mbedtls.a:*.o(.rodata.* .rodata)
     /* actual certificate in example (TEMPORARY HACK) */
     *:cert.o(.rodata.* .rodata)
-    /* Anything explicitly marked as "irom" or "irom0" should go here */
-    *(.irom.* .irom.*.* .irom0.*)
-    _irom0_text_end = ABSOLUTE(.);
+    /*  C++ constructor and destructor tables, properly ordered:  */
+    __init_array_start = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+    __init_array_end = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+    /*  C++ exception handlers table:  */
+    __XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
+    *(.xt_except_desc)
+    *(.gnu.linkonce.h.*)
+    __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
+    *(.xt_except_desc_end)
+
   } >irom0_0_seg :irom0_0_phdr
 
   .data : ALIGN(4)
@@ -179,23 +200,6 @@ SECTIONS
     *(.gnu.version_r)
     *(.eh_frame)
     . = (. + 3) & ~ 3;
-    /*  C++ constructor and destructor tables, properly ordered:  */
-    __init_array_start = ABSOLUTE(.);
-    KEEP (*crtbegin.o(.ctors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
-    KEEP (*(SORT(.ctors.*)))
-    KEEP (*(.ctors))
-    __init_array_end = ABSOLUTE(.);
-    KEEP (*crtbegin.o(.dtors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
-    KEEP (*(SORT(.dtors.*)))
-    KEEP (*(.dtors))
-    /*  C++ exception handlers table:  */
-    __XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
-    *(.xt_except_desc)
-    *(.gnu.linkonce.h.*)
-    __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
-    *(.xt_except_desc_end)
     *(.dynamic)
     *(.gnu.version_d)
     . = ALIGN(4);		/* this table MUST be 4-byte aligned */
@@ -230,7 +234,6 @@ SECTIONS
   } >dram0_0_seg :dram0_0_bss_phdr
 /* __stack = 0x3ffc8000; */
 
-
   .lit4 : ALIGN(4)
   {
     _lit4_start = ABSOLUTE(.);
diff --git a/libc/xtensa-lx106-elf/lib/libc.a b/libc/xtensa-lx106-elf/lib/libc.a
index 6a256cd..a43b5ed 100644
Binary files a/libc/xtensa-lx106-elf/lib/libc.a and b/libc/xtensa-lx106-elf/lib/libc.a differ
diff --git a/libc/xtensa-lx106-elf/lib/libg.a b/libc/xtensa-lx106-elf/lib/libg.a
index 6a256cd..4d3d50a 100644
Binary files a/libc/xtensa-lx106-elf/lib/libg.a and b/libc/xtensa-lx106-elf/lib/libg.a differ
diff --git a/libc/xtensa-lx106-elf/lib/libm.a b/libc/xtensa-lx106-elf/lib/libm.a
index 7d96963..2e9a859 100644
Binary files a/libc/xtensa-lx106-elf/lib/libm.a and b/libc/xtensa-lx106-elf/lib/libm.a differ
diff --git a/lwip/include/arch/cc.h b/lwip/include/arch/cc.h
index a09ea0c..04dab26 100644
--- a/lwip/include/arch/cc.h
+++ b/lwip/include/arch/cc.h
@@ -94,7 +94,7 @@ typedef int sys_prot_t;
         _Pragma("GCC diagnostic pop")                   \
     } while(0)
 #define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
-					    x, __LINE__, __FILE__); while(1) {} } while(0)
+                                            x, __LINE__, __FILE__); abort(); } while(0)
 
 #define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
   printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
diff --git a/lwip/sys_arch.c b/lwip/sys_arch.c
index f625fc6..ad936e4 100644
--- a/lwip/sys_arch.c
+++ b/lwip/sys_arch.c
@@ -1,585 +1,592 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-//*****************************************************************************
-//
-// Include OS functionality.
-//
-//*****************************************************************************
-
-/* ------------------------ System architecture includes ----------------------------- */
-#include "arch/sys_arch.h"
-
-/* ------------------------ lwIP includes --------------------------------- */
-#include "lwip/opt.h"
-
-#include "lwip/err.h"
-#include "lwip/debug.h"
-#include "lwip/def.h"
-#include "lwip/sys.h"
-#include "lwip/mem.h"
-#include "lwip/stats.h"
-
-/* Very crude mechanism used to determine if the critical section handling
-functions are being called from an interrupt context or not.  This relies on
-the interrupt handler setting this variable manually. */
-portBASE_TYPE xInsideISR = pdFALSE;
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Creates a new mailbox
- * Inputs:
- *      int size                -- Size of elements in the mailbox
- * Outputs:
- *      sys_mbox_t              -- Handle to new mailbox
- *---------------------------------------------------------------------------*/
-err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
-{
-err_t xReturn = ERR_MEM;
-
-	*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
-
-	if( *pxMailBox != NULL )
-	{
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( mbox );
-	}
-
-	return xReturn;
-}
-
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_free
- *---------------------------------------------------------------------------*
- * Description:
- *      Deallocates a mailbox. If there are messages still present in the
- *      mailbox when the mailbox is deallocated, it is an indication of a
- *      programming error in lwIP and the developer should be notified.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- * Outputs:
- *      sys_mbox_t              -- Handle to new mailbox
- *---------------------------------------------------------------------------*/
-void sys_mbox_free( sys_mbox_t *pxMailBox )
-{
-unsigned long ulMessagesWaiting;
-
-	ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
-	configASSERT( ( ulMessagesWaiting == 0 ) );
-
-	#if SYS_STATS
-	{
-		if( ulMessagesWaiting != 0UL )
-		{
-			SYS_STATS_INC( mbox.err );
-		}
-
-		SYS_STATS_DEC( mbox.used );
-	}
-	#endif /* SYS_STATS */
-
-	vQueueDelete( *pxMailBox );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_post
- *---------------------------------------------------------------------------*
- * Description:
- *      Post the "msg" to the mailbox.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void *data              -- Pointer to data to post
- *---------------------------------------------------------------------------*/
-void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
-{
-	while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_trypost
- *---------------------------------------------------------------------------*
- * Description:
- *      Try to post the "msg" to the mailbox.  Returns immediately with
- *      error if cannot.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void *msg               -- Pointer to data to post
- * Outputs:
- *      err_t                   -- ERR_OK if message posted, else ERR_MEM
- *                                  if not.
- *---------------------------------------------------------------------------*/
-err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
-{
-err_t xReturn;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( xInsideISR != pdFALSE )
-	{
-		xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
-	}
-
-	if( xReturn == pdPASS )
-	{
-		xReturn = ERR_OK;
-	}
-	else
-	{
-		/* The queue was already full. */
-		xReturn = ERR_MEM;
-		SYS_STATS_INC( mbox.err );
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_mbox_fetch
- *---------------------------------------------------------------------------*
- * Description:
- *      Blocks the thread until a message arrives in the mailbox, but does
- *      not block the thread longer than "timeout" milliseconds (similar to
- *      the sys_arch_sem_wait() function). The "msg" argument is a result
- *      parameter that is set by the function (i.e., by doing "*msg =
- *      ptr"). The "msg" parameter maybe NULL to indicate that the message
- *      should be dropped.
- *
- *      The return values are the same as for the sys_arch_sem_wait() function:
- *      Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
- *      timeout.
- *
- *      Note that a function with a similar name, sys_mbox_fetch(), is
- *      implemented by lwIP.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void **msg              -- Pointer to pointer to msg received
- *      u32_t timeout           -- Number of milliseconds until timeout
- * Outputs:
- *      u32_t                   -- SYS_ARCH_TIMEOUT if timeout, else number
- *                                  of milliseconds until received.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_fetch( sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeOut )
-{
-void *pvDummy;
-portTickType xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
-
-	xStartTime = xTaskGetTickCount();
-
-	if( NULL == ppvBuffer )
-	{
-		ppvBuffer = &pvDummy;
-	}
-
-	if( ulTimeOut != 0UL )
-	{
-		configASSERT( xInsideISR == ( portBASE_TYPE ) 0 );
-
-		if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
-		{
-			xEndTime = xTaskGetTickCount();
-			xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
-
-			ulReturn = xElapsed;
-		}
-		else
-		{
-			/* Timed out. */
-			*ppvBuffer = NULL;
-			ulReturn = SYS_ARCH_TIMEOUT;
-		}
-	}
-	else
-	{
-		while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
-		xEndTime = xTaskGetTickCount();
-		xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
-
-		if( xElapsed == 0UL )
-		{
-			xElapsed = 1UL;
-		}
-
-		ulReturn = xElapsed;
-	}
-
-	return ulReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_mbox_tryfetch
- *---------------------------------------------------------------------------*
- * Description:
- *      Similar to sys_arch_mbox_fetch, but if message is not ready
- *      immediately, we'll return with SYS_MBOX_EMPTY.  On success, 0 is
- *      returned.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void **msg              -- Pointer to pointer to msg received
- * Outputs:
- *      u32_t                   -- SYS_MBOX_EMPTY if no messages.  Otherwise,
- *                                  return ERR_OK.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_tryfetch( sys_mbox_t *pxMailBox, void **ppvBuffer )
-{
-void *pvDummy;
-unsigned long ulReturn;
-long lResult;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( ppvBuffer== NULL )
-	{
-		ppvBuffer = &pvDummy;
-	}
-
-	if( xInsideISR != pdFALSE )
-	{
-		lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
-	}
-
-	if( lResult == pdPASS )
-	{
-		ulReturn = ERR_OK;
-	}
-	else
-	{
-		ulReturn = SYS_MBOX_EMPTY;
-	}
-
-	return ulReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Creates and returns a new semaphore. The "ucCount" argument specifies
- *      the initial state of the semaphore.
- *      NOTE: Currently this routine only creates counts of 1 or 0
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      u8_t ucCount              -- Initial ucCount of semaphore (1 or 0)
- * Outputs:
- *      sys_sem_t               -- Created semaphore or 0 if could not create.
- *---------------------------------------------------------------------------*/
-err_t sys_sem_new( sys_sem_t *pxSemaphore, u8_t ucCount )
-{
-err_t xReturn = ERR_MEM;
-
-	vSemaphoreCreateBinary( ( *pxSemaphore ) );
-
-	if( *pxSemaphore != NULL )
-	{
-		if( ucCount == 0U )
-		{
-			xSemaphoreTake( *pxSemaphore, 1UL );
-		}
-
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( sem );
-	}
-	else
-	{
-		SYS_STATS_INC( sem.err );
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_sem_wait
- *---------------------------------------------------------------------------*
- * Description:
- *      Blocks the thread while waiting for the semaphore to be
- *      signaled. If the "timeout" argument is non-zero, the thread should
- *      only be blocked for the specified time (measured in
- *      milliseconds).
- *
- *      If the timeout argument is non-zero, the return value is the number of
- *      milliseconds spent waiting for the semaphore to be signaled. If the
- *      semaphore wasn't signaled within the specified time, the return value is
- *      SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
- *      (i.e., it was already signaled), the function may return zero.
- *
- *      Notice that lwIP implements a function with a similar name,
- *      sys_sem_wait(), that uses the sys_arch_sem_wait() function.
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to wait on
- *      u32_t timeout           -- Number of milliseconds until timeout
- * Outputs:
- *      u32_t                   -- Time elapsed or SYS_ARCH_TIMEOUT.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
-{
-portTickType xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
-
-	xStartTime = xTaskGetTickCount();
-
-	if( ulTimeout != 0UL )
-	{
-		if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
-		{
-			xEndTime = xTaskGetTickCount();
-			xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
-			ulReturn = xElapsed;
-		}
-		else
-		{
-			ulReturn = SYS_ARCH_TIMEOUT;
-		}
-	}
-	else
-	{
-		while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
-		xEndTime = xTaskGetTickCount();
-		xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
-
-		if( xElapsed == 0UL )
-		{
-			xElapsed = 1UL;
-		}
-
-		ulReturn = xElapsed;
-	}
-
-	return ulReturn;
-}
-
-/** Create a new mutex
- * @param mutex pointer to the mutex to create
- * @return a new mutex */
-err_t sys_mutex_new( sys_mutex_t *pxMutex )
-{
-err_t xReturn = ERR_MEM;
-
-	*pxMutex = xSemaphoreCreateMutex();
-
-	if( *pxMutex != NULL ) 
-	{
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( mutex );
-	} 
-	else 
-	{
-		SYS_STATS_INC( mutex.err );
-	}
-	
-	return xReturn;
-}
-
-/** Lock a mutex
- * @param mutex the mutex to lock */
-void sys_mutex_lock( sys_mutex_t *pxMutex )
-{
-	while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
-}
-
-/** Unlock a mutex
- * @param mutex the mutex to unlock */
-void sys_mutex_unlock(sys_mutex_t *pxMutex )
-{
-	xSemaphoreGive( *pxMutex );
-}
-
-
-/** Delete a semaphore
- * @param mutex the mutex to delete */
-void sys_mutex_free( sys_mutex_t *pxMutex )
-{
-	SYS_STATS_DEC( mutex.used );
-	vQueueDelete( *pxMutex );
-}
-
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_signal
- *---------------------------------------------------------------------------*
- * Description:
- *      Signals (releases) a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to signal
- *---------------------------------------------------------------------------*/
-void sys_sem_signal( sys_sem_t *pxSemaphore )
-{
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( xInsideISR != pdFALSE )
-	{
-		xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		xSemaphoreGive( *pxSemaphore );
-	}
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_free
- *---------------------------------------------------------------------------*
- * Description:
- *      Deallocates a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to free
- *---------------------------------------------------------------------------*/
-void sys_sem_free( sys_sem_t *pxSemaphore )
-{
-	SYS_STATS_DEC(sem.used);
-	vQueueDelete( *pxSemaphore );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_init
- *---------------------------------------------------------------------------*
- * Description:
- *      Initialize sys arch
- *---------------------------------------------------------------------------*/
-void sys_init(void)
-{
-}
-
-u32_t sys_now(void)
-{
-	return xTaskGetTickCount();
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_thread_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Starts a new thread with priority "prio" that will begin its
- *      execution in the function "thread()". The "arg" argument will be
- *      passed as an argument to the thread() function. The id of the new
- *      thread is returned. Both the id and the priority are system
- *      dependent.
- * Inputs:
- *      char *name              -- Name of thread
- *      void (* thread)(void *arg) -- Pointer to function to run.
- *      void *arg               -- Argument passed into function
- *      int stacksize           -- Required stack amount in bytes
- *      int prio                -- Thread priority
- * Outputs:
- *      sys_thread_t            -- Pointer to per-thread timeouts.
- *---------------------------------------------------------------------------*/
-sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
-{
-xTaskHandle xCreatedTask;
-portBASE_TYPE xResult;
-sys_thread_t xReturn;
-
-	xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
-
-	if( xResult == pdPASS )
-	{
-		xReturn = xCreatedTask;
-	}
-	else
-	{
-		xReturn = NULL;
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_protect
- *---------------------------------------------------------------------------*
- * Description:
- *      This optional function does a "fast" critical region protection and
- *      returns the previous protection level. This function is only called
- *      during very short critical regions. An embedded system which supports
- *      ISR-based drivers might want to implement this function by disabling
- *      interrupts. Task-based systems might want to implement this by using
- *      a mutex or disabling tasking. This function should support recursive
- *      calls from the same task or interrupt. In other words,
- *      sys_arch_protect() could be called while already protected. In
- *      that case the return value indicates that it is already protected.
- *
- *      sys_arch_protect() is only required if your port is supporting an
- *      operating system.
- * Outputs:
- *      sys_prot_t              -- Previous protection level (not used here)
- *---------------------------------------------------------------------------*/
-sys_prot_t sys_arch_protect( void )
-{
-	if( xInsideISR == pdFALSE )
-	{
-		taskENTER_CRITICAL();
-	}
-	return ( sys_prot_t ) 1;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_unprotect
- *---------------------------------------------------------------------------*
- * Description:
- *      This optional function does a "fast" set of critical region
- *      protection to the value specified by pval. See the documentation for
- *      sys_arch_protect() for more information. This function is only
- *      required if your port is supporting an operating system.
- * Inputs:
- *      sys_prot_t              -- Previous protection level (not used here)
- *---------------------------------------------------------------------------*/
-void sys_arch_unprotect( sys_prot_t xValue )
-{
-	(void) xValue;
-	if( xInsideISR == pdFALSE )
-	{
-		taskEXIT_CRITICAL();
-	}
-}
-
-/*
- * Prints an assertion messages and aborts execution.
- */
-void sys_assert( const char *pcMessage )
-{
-	(void) pcMessage;
-
-	for (;;)
-	{
-	}
-}
-/*-------------------------------------------------------------------------*
- * End of File:  sys_arch.c
- *-------------------------------------------------------------------------*/
-
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+//*****************************************************************************
+//
+// Include OS functionality.
+//
+//*****************************************************************************
+
+/* ------------------------ System architecture includes ----------------------------- */
+#include "arch/sys_arch.h"
+
+/* ------------------------ lwIP includes --------------------------------- */
+#include "lwip/opt.h"
+
+#include "lwip/err.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/stats.h"
+
+extern bool esp_in_isr;
+
+/* Based on the default xInsideISR mechanism to determine
+   if an ISR is running.
+
+   Doesn't support the possibility that LWIP functions are called from the NMI
+   handler (none are called from NMI when using current/SDK implementation.)
+*/
+static inline bool is_inside_isr()
+{
+    return esp_in_isr;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_mbox_new
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Creates a new mailbox
+ * Inputs:
+ *      int size                -- Size of elements in the mailbox
+ * Outputs:
+ *      sys_mbox_t              -- Handle to new mailbox
+ *---------------------------------------------------------------------------*/
+err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
+{
+    err_t xReturn = ERR_MEM;
+
+    *pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
+
+    if( *pxMailBox != NULL )
+    {
+        xReturn = ERR_OK;
+        SYS_STATS_INC_USED( mbox );
+    }
+
+    return xReturn;
+}
+
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_mbox_free
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Deallocates a mailbox. If there are messages still present in the
+ *      mailbox when the mailbox is deallocated, it is an indication of a
+ *      programming error in lwIP and the developer should be notified.
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ * Outputs:
+ *      sys_mbox_t              -- Handle to new mailbox
+ *---------------------------------------------------------------------------*/
+void sys_mbox_free( sys_mbox_t *pxMailBox )
+{
+unsigned long ulMessagesWaiting;
+
+    ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
+    configASSERT( ( ulMessagesWaiting == 0 ) );
+
+    #if SYS_STATS
+    {
+        if( ulMessagesWaiting != 0UL )
+        {
+            SYS_STATS_INC( mbox.err );
+        }
+
+        SYS_STATS_DEC( mbox.used );
+    }
+    #endif /* SYS_STATS */
+
+    vQueueDelete( *pxMailBox );
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_mbox_post
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Post the "msg" to the mailbox.
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      void *data              -- Pointer to data to post
+ *---------------------------------------------------------------------------*/
+void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
+{
+    while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_mbox_trypost
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Try to post the "msg" to the mailbox.  Returns immediately with
+ *      error if cannot.
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      void *msg               -- Pointer to data to post
+ * Outputs:
+ *      err_t                   -- ERR_OK if message posted, else ERR_MEM
+ *                                  if not.
+ *---------------------------------------------------------------------------*/
+err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
+{
+err_t xReturn;
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+
+    if( is_inside_isr() != pdFALSE )
+    {
+        xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
+    }
+    else
+    {
+        xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 );
+    }
+
+    if( xReturn == pdPASS )
+    {
+        xReturn = ERR_OK;
+    }
+    else
+    {
+        /* The queue was already full. */
+        xReturn = ERR_MEM;
+        SYS_STATS_INC( mbox.err );
+    }
+
+    return xReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_mbox_fetch
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Blocks the thread until a message arrives in the mailbox, but does
+ *      not block the thread longer than "timeout" milliseconds (similar to
+ *      the sys_arch_sem_wait() function). The "msg" argument is a result
+ *      parameter that is set by the function (i.e., by doing "*msg =
+ *      ptr"). The "msg" parameter maybe NULL to indicate that the message
+ *      should be dropped.
+ *
+ *      The return values are the same as for the sys_arch_sem_wait() function:
+ *      Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+ *      timeout.
+ *
+ *      Note that a function with a similar name, sys_mbox_fetch(), is
+ *      implemented by lwIP.
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      void **msg              -- Pointer to pointer to msg received
+ *      u32_t timeout           -- Number of milliseconds until timeout
+ * Outputs:
+ *      u32_t                   -- SYS_ARCH_TIMEOUT if timeout, else number
+ *                                  of milliseconds until received.
+ *---------------------------------------------------------------------------*/
+u32_t sys_arch_mbox_fetch( sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeOut )
+{
+void *pvDummy;
+portTickType xStartTime, xEndTime, xElapsed;
+unsigned long ulReturn;
+
+    xStartTime = xTaskGetTickCount();
+
+    if( NULL == ppvBuffer )
+    {
+        ppvBuffer = &pvDummy;
+    }
+
+    if( ulTimeOut != 0UL )
+    {
+        configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 );
+
+        if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) )
+        {
+            xEndTime = xTaskGetTickCount();
+            xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
+
+            ulReturn = xElapsed;
+        }
+        else
+        {
+            /* Timed out. */
+            *ppvBuffer = NULL;
+            ulReturn = SYS_ARCH_TIMEOUT;
+        }
+    }
+    else
+    {
+        while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
+        xEndTime = xTaskGetTickCount();
+        xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
+
+        if( xElapsed == 0UL )
+        {
+            xElapsed = 1UL;
+        }
+
+        ulReturn = xElapsed;
+    }
+
+    return ulReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_mbox_tryfetch
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Similar to sys_arch_mbox_fetch, but if message is not ready
+ *      immediately, we'll return with SYS_MBOX_EMPTY.  On success, 0 is
+ *      returned.
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      void **msg              -- Pointer to pointer to msg received
+ * Outputs:
+ *      u32_t                   -- SYS_MBOX_EMPTY if no messages.  Otherwise,
+ *                                  return ERR_OK.
+ *---------------------------------------------------------------------------*/
+u32_t sys_arch_mbox_tryfetch( sys_mbox_t *pxMailBox, void **ppvBuffer )
+{
+void *pvDummy;
+unsigned long ulReturn;
+long lResult;
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+
+    if( ppvBuffer== NULL )
+    {
+        ppvBuffer = &pvDummy;
+    }
+
+    if( is_inside_isr() != pdFALSE )
+    {
+        lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
+    }
+    else
+    {
+        lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
+    }
+
+    if( lResult == pdPASS )
+    {
+        ulReturn = ERR_OK;
+    }
+    else
+    {
+        ulReturn = SYS_MBOX_EMPTY;
+    }
+
+    return ulReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_new
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Creates and returns a new semaphore. The "ucCount" argument specifies
+ *      the initial state of the semaphore.
+ *      NOTE: Currently this routine only creates counts of 1 or 0
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      u8_t ucCount              -- Initial ucCount of semaphore (1 or 0)
+ * Outputs:
+ *      sys_sem_t               -- Created semaphore or 0 if could not create.
+ *---------------------------------------------------------------------------*/
+err_t sys_sem_new( sys_sem_t *pxSemaphore, u8_t ucCount )
+{
+err_t xReturn = ERR_MEM;
+
+    vSemaphoreCreateBinary( ( *pxSemaphore ) );
+
+    if( *pxSemaphore != NULL )
+    {
+        if( ucCount == 0U )
+        {
+            xSemaphoreTake( *pxSemaphore, 1UL );
+        }
+
+        xReturn = ERR_OK;
+        SYS_STATS_INC_USED( sem );
+    }
+    else
+    {
+        SYS_STATS_INC( sem.err );
+    }
+
+    return xReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_sem_wait
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Blocks the thread while waiting for the semaphore to be
+ *      signaled. If the "timeout" argument is non-zero, the thread should
+ *      only be blocked for the specified time (measured in
+ *      milliseconds).
+ *
+ *      If the timeout argument is non-zero, the return value is the number of
+ *      milliseconds spent waiting for the semaphore to be signaled. If the
+ *      semaphore wasn't signaled within the specified time, the return value is
+ *      SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+ *      (i.e., it was already signaled), the function may return zero.
+ *
+ *      Notice that lwIP implements a function with a similar name,
+ *      sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to wait on
+ *      u32_t timeout           -- Number of milliseconds until timeout
+ * Outputs:
+ *      u32_t                   -- Time elapsed or SYS_ARCH_TIMEOUT.
+ *---------------------------------------------------------------------------*/
+u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
+{
+portTickType xStartTime, xEndTime, xElapsed;
+unsigned long ulReturn;
+
+    xStartTime = xTaskGetTickCount();
+
+    if( ulTimeout != 0UL )
+    {
+        if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE )
+        {
+            xEndTime = xTaskGetTickCount();
+            xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS;
+            ulReturn = xElapsed;
+        }
+        else
+        {
+            ulReturn = SYS_ARCH_TIMEOUT;
+        }
+    }
+    else
+    {
+        while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
+        xEndTime = xTaskGetTickCount();
+        xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS;
+
+        if( xElapsed == 0UL )
+        {
+            xElapsed = 1UL;
+        }
+
+        ulReturn = xElapsed;
+    }
+
+    return ulReturn;
+}
+
+/** Create a new mutex
+ * @param mutex pointer to the mutex to create
+ * @return a new mutex */
+err_t sys_mutex_new( sys_mutex_t *pxMutex )
+{
+err_t xReturn = ERR_MEM;
+
+    *pxMutex = xSemaphoreCreateMutex();
+
+    if( *pxMutex != NULL )
+    {
+        xReturn = ERR_OK;
+        SYS_STATS_INC_USED( mutex );
+    }
+    else
+    {
+        SYS_STATS_INC( mutex.err );
+    }
+
+    return xReturn;
+}
+
+/** Lock a mutex
+ * @param mutex the mutex to lock */
+void sys_mutex_lock( sys_mutex_t *pxMutex )
+{
+    while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
+}
+
+/** Unlock a mutex
+ * @param mutex the mutex to unlock */
+void sys_mutex_unlock(sys_mutex_t *pxMutex )
+{
+    xSemaphoreGive( *pxMutex );
+}
+
+
+/** Delete a semaphore
+ * @param mutex the mutex to delete */
+void sys_mutex_free( sys_mutex_t *pxMutex )
+{
+    SYS_STATS_DEC( mutex.used );
+    vQueueDelete( *pxMutex );
+}
+
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_signal
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Signals (releases) a semaphore
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to signal
+ *---------------------------------------------------------------------------*/
+void sys_sem_signal( sys_sem_t *pxSemaphore )
+{
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+
+    if( is_inside_isr() != pdFALSE )
+    {
+        xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
+    }
+    else
+    {
+        xSemaphoreGive( *pxSemaphore );
+    }
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_free
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Deallocates a semaphore
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to free
+ *---------------------------------------------------------------------------*/
+void sys_sem_free( sys_sem_t *pxSemaphore )
+{
+    SYS_STATS_DEC(sem.used);
+    vQueueDelete( *pxSemaphore );
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_init
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Initialize sys arch
+ *---------------------------------------------------------------------------*/
+void sys_init(void)
+{
+}
+
+u32_t sys_now(void)
+{
+    return xTaskGetTickCount();
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_thread_new
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Starts a new thread with priority "prio" that will begin its
+ *      execution in the function "thread()". The "arg" argument will be
+ *      passed as an argument to the thread() function. The id of the new
+ *      thread is returned. Both the id and the priority are system
+ *      dependent.
+ * Inputs:
+ *      char *name              -- Name of thread
+ *      void (* thread)(void *arg) -- Pointer to function to run.
+ *      void *arg               -- Argument passed into function
+ *      int stacksize           -- Required stack amount in bytes
+ *      int prio                -- Thread priority
+ * Outputs:
+ *      sys_thread_t            -- Pointer to per-thread timeouts.
+ *---------------------------------------------------------------------------*/
+sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
+{
+xTaskHandle xCreatedTask;
+portBASE_TYPE xResult;
+sys_thread_t xReturn;
+
+    xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
+
+    if( xResult == pdPASS )
+    {
+        xReturn = xCreatedTask;
+    }
+    else
+    {
+        xReturn = NULL;
+    }
+
+    return xReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_protect
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      This optional function does a "fast" critical region protection and
+ *      returns the previous protection level. This function is only called
+ *      during very short critical regions. An embedded system which supports
+ *      ISR-based drivers might want to implement this function by disabling
+ *      interrupts. Task-based systems might want to implement this by using
+ *      a mutex or disabling tasking. This function should support recursive
+ *      calls from the same task or interrupt. In other words,
+ *      sys_arch_protect() could be called while already protected. In
+ *      that case the return value indicates that it is already protected.
+ *
+ *      sys_arch_protect() is only required if your port is supporting an
+ *      operating system.
+ * Outputs:
+ *      sys_prot_t              -- Previous protection level (not used here)
+ *---------------------------------------------------------------------------*/
+sys_prot_t sys_arch_protect( void )
+{
+    if( is_inside_isr() == pdFALSE )
+    {
+        taskENTER_CRITICAL();
+    }
+    return ( sys_prot_t ) 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_unprotect
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      This optional function does a "fast" set of critical region
+ *      protection to the value specified by pval. See the documentation for
+ *      sys_arch_protect() for more information. This function is only
+ *      required if your port is supporting an operating system.
+ * Inputs:
+ *      sys_prot_t              -- Previous protection level (not used here)
+ *---------------------------------------------------------------------------*/
+void sys_arch_unprotect( sys_prot_t xValue )
+{
+    (void) xValue;
+    if( is_inside_isr() == pdFALSE )
+    {
+        taskEXIT_CRITICAL();
+    }
+}
+
+/*
+ * Prints an assertion messages and aborts execution.
+ */
+void sys_assert( const char *pcMessage )
+{
+    (void) pcMessage;
+
+    for (;;)
+    {
+    }
+}
+/*-------------------------------------------------------------------------*
+ * End of File:  sys_arch.c
+ *-------------------------------------------------------------------------*/
diff --git a/utils/filteroutput.py b/utils/filteroutput.py
new file mode 100755
index 0000000..954fd9a
--- /dev/null
+++ b/utils/filteroutput.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+#
+# A thin Python wrapper around addr2line, can monitor esp-open-rtos
+# output and uses gdb to convert any suitable looking hex numbers
+# found in the output into function and line numbers.
+#
+# Works with a serial port if the --port option is supplied.
+# Otherwise waits for input on stdin.
+#
+import serial
+import argparse
+import re
+import os
+import os.path
+import subprocess
+import termios
+import sys
+import time
+
+# Try looking up anything in the executable address space
+RE_EXECADDR = r"(0x)?40([0-9]|[a-z]){6}"
+
+def find_elf_file():
+    out_files = []
+    for top,_,files in os.walk('.', followlinks=False):
+        for f in files:
+            if f.endswith(".out"):
+                out_files.append(os.path.join(top,f))
+    if len(out_files) == 1:
+        return out_files[0]
+    elif len(out_files) > 1:
+        print("Found multiple .out files: %s. Please specify one with the --elf option." % out_files)
+    else:
+        print("No .out file found under current directory. Please specify one with the --elf option.")
+    sys.exit(1)
+
+def main():
+    parser = argparse.ArgumentParser(description='esp-open-rtos output filter tool', prog='filteroutput')
+    parser.add_argument(
+        '--elf', '-e',
+        help="ELF file (*.out file) to load symbols from (if not supplied, will search for one)"),
+    parser.add_argument(
+        '--port', '-p',
+        help='Serial port to monitor (will monitor stdin if None)',
+        default=None)
+    parser.add_argument(
+        '--baud', '-b',
+        help='Baud rate for serial port',
+        type=int,
+        default=74880)
+    parser.add_argument(
+        '--reset-on-connect', '-r',
+        help='Reset ESP8266 (via DTR) on serial connect',
+        action='store_true')
+
+    args = parser.parse_args()
+
+    if args.elf is None:
+        args.elf = find_elf_file()
+    elif not os.path.exists(args.elf):
+        print("ELF file '%s' not found" % args.elf)
+        sys.exit(1)
+
+    if args.port is not None:
+        print("Opening %s at %dbps..." % (args.port, args.baud))
+        port = serial.Serial(args.port, baudrate=args.baud)
+        if args.reset_on_connect:
+            print("Resetting...")
+            port.setDTR(False)
+            time.sleep(0.1)
+            port.setDTR(True)
+
+    else:
+        print("Reading from stdin...")
+        port = sys.stdin
+        # disable echo
+	try:
+            old_attr = termios.tcgetattr(sys.stdin.fileno())
+	    attr = termios.tcgetattr(sys.stdin.fileno())
+	    attr[3] = attr[3] & ~termios.ECHO
+	    termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attr)
+	except termios.error:
+	     pass
+
+    try:
+        while True:
+            line = port.readline()
+            if line == '':
+                break
+            print(line.strip())
+            for match in re.finditer(RE_EXECADDR, line, re.IGNORECASE):
+                addr = match.group(0)
+                if not addr.startswith("0x"):
+                    addr = "0x"+addr
+                    # keeping addr2line and feeding it addresses on stdin didn't seem to work smoothly
+                addr2line = subprocess.check_output(["xtensa-lx106-elf-addr2line","-pfia","-e","%s" % args.elf, addr], cwd=".").strip()
+                if not addr2line.endswith(": ?? ??:0"):
+                    print("\n%s\n" % addr2line.strip())
+    finally:
+        if args.port is None:
+            # restore echo
+            termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_attr)
+
+if __name__ == "__main__":
+    main()