diff --git a/esp8266/Makefile b/esp8266/Makefile
index b30bb2f95b2a947e1b48b5be3a298eb8a2ff0a9f..1836e7eb4aef737eff0287e07b3adeaa2bdbf0a2 100644
--- a/esp8266/Makefile
+++ b/esp8266/Makefile
@@ -58,6 +58,7 @@ SRC_C = \
 	lexerstr32.c \
 	uart.c \
 	esppwm.c \
+	espneopixel.c \
 	modpyb.c \
 	modpybpin.c \
 	modpybpwm.c \
diff --git a/esp8266/espneopixel.c b/esp8266/espneopixel.c
new file mode 100644
index 0000000000000000000000000000000000000000..26776f025e41c816886960323ab008e328cda59d
--- /dev/null
+++ b/esp8266/espneopixel.c
@@ -0,0 +1,64 @@
+// Original version from https://github.com/adafruit/Adafruit_NeoPixel
+// Modifications by dpgeorge to support auto-CPU-frequency detection
+
+// This is a mash-up of the Due show() code + insights from Michael Miller's
+// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus
+// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution.
+
+#include "c_types.h"
+#include "eagle_soc.h"
+#include "user_interface.h"
+#include "espneopixel.h"
+
+#define NEO_KHZ400 (1)
+
+static uint32_t _getCycleCount(void) __attribute__((always_inline));
+static inline uint32_t _getCycleCount(void) {
+  uint32_t ccount;
+  __asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
+  return ccount;
+}
+
+void /*ICACHE_RAM_ATTR*/ esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) {
+
+  uint8_t *p, *end, pix, mask;
+  uint32_t t, time0, time1, period, c, startTime, pinMask;
+
+  pinMask   = 1 << pin;
+  p         =  pixels;
+  end       =  p + numBytes;
+  pix       = *p++;
+  mask      = 0x80;
+  startTime = 0;
+
+  uint32_t fcpu = system_get_cpu_freq() * 1000000;
+
+#ifdef NEO_KHZ400
+  if(is800KHz) {
+#endif
+    time0  = fcpu / 2500000; // 0.4us
+    time1  = fcpu / 1250000; // 0.8us
+    period = fcpu /  800000; // 1.25us per bit
+#ifdef NEO_KHZ400
+  } else { // 400 KHz bitstream
+    time0  = fcpu / 2000000; // 0.5uS
+    time1  = fcpu /  833333; // 1.2us
+    period = fcpu /  400000; // 2.5us per bit
+  }
+#endif
+
+  for(t = time0;; t = time0) {
+    if(pix & mask) t = time1;                             // Bit high duration
+    while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start
+    GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask);       // Set high
+    startTime = c;                                        // Save start time
+    while(((c = _getCycleCount()) - startTime) < t);      // Wait high duration
+    GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask);       // Set low
+    if(!(mask >>= 1)) {                                   // Next bit/byte
+      if(p >= end) break;
+      pix  = *p++;
+      mask = 0x80;
+    }
+  }
+  while((_getCycleCount() - startTime) < period); // Wait for last bit
+}
diff --git a/esp8266/espneopixel.h b/esp8266/espneopixel.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b20afda589f7e1b28c11f59b315f6a9caa70d67
--- /dev/null
+++ b/esp8266/espneopixel.h
@@ -0,0 +1 @@
+void esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
diff --git a/esp8266/modesp.c b/esp8266/modesp.c
index 73cfc4275c5d2e3a0402db1e138e6be2d30554af..18859ead8a11c06a3ba962a4fd6ef585b4a2d6db 100644
--- a/esp8266/modesp.c
+++ b/esp8266/modesp.c
@@ -39,6 +39,8 @@
 #include "espconn.h"
 #include "spi_flash.h"
 #include "utils.h"
+#include "espneopixel.h"
+#include "modpyb.h"
 
 #define MODESP_ESPCONN (0)
 
@@ -573,6 +575,15 @@ STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_flash_erase_obj, esp_flash_erase);
 
+STATIC mp_obj_t esp_neopixel_write_(mp_obj_t pin, mp_obj_t buf, mp_obj_t is800k) {
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
+    esp_neopixel_write(mp_obj_get_pin_obj(pin)->phys_port,
+        (uint8_t*)bufinfo.buf, bufinfo.len, mp_obj_is_true(is800k));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_neopixel_write_obj, esp_neopixel_write_);
+
 STATIC const mp_map_elem_t esp_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_esp) },
 
@@ -586,6 +597,7 @@ STATIC const mp_map_elem_t esp_module_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&esp_socket_type },
     { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&esp_getaddrinfo_obj },
     #endif
+    { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write), (mp_obj_t)&esp_neopixel_write_obj },
 
 #if MODESP_INCLUDE_CONSTANTS
     { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11B),
diff --git a/esp8266/qstrdefsport.h b/esp8266/qstrdefsport.h
index 77e66d85cccf43612dcbb61ac5be419aec4424d8..f82e0d1a535213b596c136f4bdd3e04c2e48c444 100644
--- a/esp8266/qstrdefsport.h
+++ b/esp8266/qstrdefsport.h
@@ -87,6 +87,7 @@ Q(onconnect)
 Q(onrecv)
 Q(onsent)
 Q(ondisconnect)
+Q(neopixel_write)
 Q(MODE_11B)
 Q(MODE_11G)
 Q(MODE_11N)