Lighten up your ESP8266

For a workshop I’m giving at my employer I’ve been wanting to interface a nice NeoPixel (WS2812) LED ring with an ESP8266. Using the NodeMCU boards without the actual firmware provides a nice platform that gives a small packages, with breadboard ready pin spacing & USB to serial chip. It has been a struggle to get the code working on the platform, so for the benefit of all I’m posting a very barebone working driver in case you want to interface with some WS2812’s natively.

#include "user_interface.h"
#include "osapi.h"

// Settings for GPIO5
#define IO_PIN 5
#define IO_MUX PERIPHS_IO_MUX_GPIO5_U
#define IO_FUNC FUNC_GPIO5

// ---------------------------------------------------------------------------------------------------
// -- This WS2812 code must be compiled with -O2 to get the timing right.  Read this:
// -- http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/

// The ICACHE_FLASH_ATTR is there to trick the compiler and get the very first pulse width correct.
static void ICACHE_FLASH_ATTR send_ws_0(uint8_t gpio) {
    uint8_t i;
    i = 4; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio);
    i = 9; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio);
}

static void ICACHE_FLASH_ATTR send_ws_1(uint8_t gpio) {
    uint8_t i;
    i = 8; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio);
    i = 6; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio);
}

// Byte triples in the buffer are interpreted as R G B values and sent to the hardware as G R B.
static int ICACHE_FLASH_ATTR ws2812_writergb(uint8_t gpio, char *buffer, size_t length) {
    // Initialize the output pin:
    PIN_FUNC_SELECT(IO_MUX, IO_FUNC);
    PIN_PULLUP_EN(IO_MUX);
    GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, (1 << gpio));
    GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (1 << gpio));
    
    // Ignore incomplete Byte triples at the end of buffer:
    length -= length % 3;
    
    // Rearrange R G B values to G R B order needed by WS2812 LEDs:
    size_t i;
    for (i = 0; i < length; i += 3) {
        const char r = buffer[i];
        const char g = buffer[i + 1];
        buffer[i] = g;
        buffer[i + 1] = r;
    }
    
    // Do not remove these:
    os_delay_us(1);
    os_delay_us(1);
    
    // Send the buffer:
    ets_intr_lock();
    const char * const end = buffer + length;
    while (buffer != end) {
        uint8_t mask = 0x80;
        while (mask) {
            (*buffer & mask) ? send_ws_1(gpio) : send_ws_0(gpio);
            mask >>= 1;
        }
        ++buffer;
    }
    ets_intr_unlock();
}
// ---------------------------------------------------------------------------------------------------

void user_init(void) {
    // Because we reorder the R G B to G R B this table will change each time the loop runs :-)
    char buffer1[] = {
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0x00, 0x00, 0xff,
    };
    
    while (true) {
        ws2812_writergb(IO_PIN, buffer1, sizeof(buffer1));
        os_delay_us(500000);
    }
}

One downside of it all, I was hoping that the breadboard power supply from China would pack enough power to be able to properly light this thing, however I had to chain two together, as one would drop the power resulting in a stroboscope. Doing the math it should’ve been able to power it: 60mA * 24 = 1440mA, resulting in 7,2W on 5V while the 12V adapter should be able to provide 1000mA, resulting in 12W. Too bad those 12W are Chinese Watts, which seem to be roughly half of normal world Watts.

Leave a Reply