ESP8266 WS2812B LEDs on a Plate

LEDSWell, now that I’ve cracked using the WS2812B LEDs – I guess I’d better share the results – especially as it was the work of others that started me off on this road!

I went down a LOT of dead routes with this – firstly I was going to have it programmable how many outputs I would dedicate to the WS2812B LEDs – then ultimately I realised, because we’re talking about a memory buffer feeding the LEDS I might as well just do one long run and split it up as I need!! That also got around some timing issues.  If you look at previous items on this- one fellow wrote a driver for the LEDs and it kind of worked – then if you looked at his ISSUES others said it had problems and another guy wrote an improved version and fretted about needing a capacitor on the output to stop one of the LEDs (the first one) flashing. You don’t need any of that – it’s ALL down to timing.

So – why read further than this line – because I’ve had 300 LEDs on a 5m strip working PERFECTLY and the only reason I’ve not tried 600 LEDs is I don’t have a power supply powerful enough to run 600 of the things!!!  And how quick is the update? Well, to turn all from off to on, you can’t SEE the update.

So…. I#m assuming you’re happy programming in C and know a little about the ESP8266. In my case I’m using the Eclipse environment on a Windows 8.1 PC and having a ball (as against my first steps in a Linux virtual environment which was enough to make a grown man eat his own bairns). Follow me and let’s get started.  I’m using GPIO12 on the ESP-12 – I’ve also used GPIO4, GPIO5 and there’s no reason that I can think of why you can’t use GPIO0 on an ESP-01.

To start the ball rolling you need some place to store info – I find structs handy for that so create this.

typedef struct{
    uint8_t reda;
    uint8_t greena;
    uint8_t bluea;

    uint8_t red;
    uint8_t green;
    uint8_t blue;

    uint32_t rainbow;
    uint16_t rgbnum;
    uint16_t rgbdelay;
    uint8_t  buffer[900];
} LEDS;

LEDS rgb;

 

LEDS[6]That 900 byte buffer.. depending on what you’re doing you may not need more than 3 bytes so by all means change it. A single LED uses 3 bytes – for RED, GREEN and BLUE.  If you want all the LEDS the same colour at any time you can just repeat using the same 3 bytes – but if you want them all different colours then you need 3 bytes per LED. Given that if you’re using C as against Lua (I’m only talking C here, I gave up on Lua) then you probably have bags of RAM, this will hardly make a dent (and we put up with Arduinos for years for WHAT reason?).

So, and you might want to do everything differently to me – but this is what I’ve done… I like to FADE LEDs over time as against turning them on and off so I need the ACTUAL colour of the LED against what I WANT it to be – hence REDA and RED in the above struct.

For GPIO12 I defined this lot..

#define LED_GPIO_12 12
#define LED_GPIO_MUX_12 PERIPHS_IO_MUX_MTDI_U
#define LED_GPIO_FUNC_12 FUNC_GPIO12
#define OUT_ON 1
#define OUT_OFF 0

and in my INIT routine I have this..

PIN_FUNC_SELECT(LED_GPIO_MUX_12, LED_GPIO_FUNC_12);

and that’s it basically – if you want to turn the output on:

GPIO_OUTPUT_SET(LED_GPIO_5, OUT_ON);

or off

GPIO_OUTPUT_SET(LED_GPIO_5, OUT_OFF);

or at least historically that’s how I did it – but you’ll need the initialisation anyway.

You need a callback function to do your timing for you – I have one which debounces keys and handles the RGB delays… so in your INIT you need this.

 

os_timer_disarm(&bounce_timer);
os_timer_setfn(&bounce_timer, (os_timer_func_t *) bounce_cb, (void *) 0);
os_timer_arm(&bounce_timer, 50, 1);

 

Well, you don’t have to call it bounce_timer of course.

So – I have a callback (ie a function that gets called by the system every 50ms (see last line).

Here’s what it looks like – I’ll give you a filled in one later.

LOCAL void ICACHE_FLASH_ATTR bounce_cb(void *arg) {

}

Hey, make loads of these with different times and names – they’re really useful. All that ICACHE stuff – if you don’t put that in, you’ll start eating into RAM – so just include that in your functions.

Ok, here’s the real version..

LOCAL void ICACHE_FLASH_ATTR bounce_cb(void *arg) {

if ((rgb.red != rgb.reda) || (rgb.green != rgb.greena) || (rgb.blue != rgb.bluea)) {
    if (rgb.reda < rgb.red)
        rgb.reda += ((rgb.red – rgb.reda) / (rgb.rgbdelay * 20)) + 1;
    if (rgb.greena < rgb.green)
        rgb.greena += ((rgb.green – rgb.greena) / (rgb.rgbdelay * 20)) + 1;
    if (rgb.bluea < rgb.blue)
        rgb.bluea += ((rgb.blue – rgb.bluea) / (rgb.rgbdelay * 20)) + 1;
    if (rgb.reda > rgb.red)
        rgb.reda -= ((rgb.reda – rgb.red) / (rgb.rgbdelay * 20)) + 1;
    if (rgb.greena > rgb.green)
        rgb.greena -= ((rgb.greena – rgb.green) / (rgb.rgbdelay * 20)) + 1;
    if (rgb.bluea > rgb.blue)
        rgb.bluea -= ((rgb.bluea – rgb.blue) / (rgb.rgbdelay * 20)) + 1;

    if (rgb.rgbnum == 0) { rgb.rgbnum = 1; rgb.reda=rgb.red; rgb.greena=rgb.green; rgb.bluea=rgb.blue; } // instant
    rgb.buffer[0] = ledTable[rgb.reda];
    rgb.buffer[1] = ledTable[rgb.greena];
    rgb.buffer[2] = ledTable[rgb.bluea];

    WS2812OutBuffer(rgb.buffer, 3, rgb.rgbnum); // 3 leds in array, number of repetitions
  }

}

What on EARTH is all that about – well the first line reads – IF the expected colour is in any way from the ACTUAL colour…. DO this lot.

So there’s a little maths depending on rgb.rgbdelay that sets what you want rgb.red, rgb.green and rgb.blue to be. and when that’s done – you fill in the first 3 bytes of the buffer – not with the actual value of the LED but with that put through a lookup table – why? Because the sensitivity of your eyes is not linear. If you don’t use the lookup table, the fade will appear pretty good up to 20% brilliance then will seem to slow down.

Here’s the table – put it anywhere in the same file.

static const uint8_t ledTable[256] = {
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
        3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
        9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16,
        16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26,
        26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39,
        40, 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
        56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75,
        76, 77, 78, 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98,
        99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117, 119,
        121, 122, 124, 125, 127, 129, 130, 132, 134, 135, 137, 139, 141, 142,
        144, 146, 148, 150, 151, 153, 155, 157, 159, 161, 163, 165, 166, 168,
        170, 172, 174, 176, 178, 180, 182, 184, 186, 189, 191, 193, 195, 197,
        199, 201, 204, 206, 208, 210, 212, 215, 217, 219, 221, 224, 226, 228,
        231, 233, 235, 238, 240, 243, 245, 248, 250, 253, 255 };

 

SO – every 50ms, you call a routine pointing to the buffer, 3 bytes (which is how much of the buffer you want to use without starting again and the number of LEDS you want to light up.  So in my case – and you can do things differently once you get this running and do your own thing – I only put the same value (the first 3 locations in the table) into all the LEDS I want to light.   If you use 30 instead of 3 for 10 LEDs, basically whatever you put into those 30 locations (3 per LED) will be what you see. So I’m just repeating the use of the 3 bytes for all the LEDs I’m lighting up – this has been tested from one LED to 300 LEDs.

So all you need now (and this is the one we’ve been waiting for) is the routine to actually drive the LEDs.

I’ll just say that you need some things to be FAST – so don’t say “why did you use that macro to drive the LEDs – another one is more understandable – because then I’d have trouble lighting the LEDs like others.

You need this at the start of your file.. and it MUST be a static.

static uint32_t rainb12;

and in you’re init do this.

rainb12=1<<12;

I know – why do a calculation at compile time – because it makes it easier for you to understand – it’s port 12 !!  Why not a define? Because you might want to use more than one pin! I’m not getting into that one but this is the right starting point. Here’s the routine. Not original – just fixed from the versions that don’t work.

void ICACHE_FLASH_ATTR SEND_WS_12_0() {
    uint8_t time;
    time = 4;
    while (time–)
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 4, rainb12);
    time = 9;
    while (time–)
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, rainb12);
}

void ICACHE_FLASH_ATTR SEND_WS_12_1() {
    uint8_t time;
    time = 8;
    while (time–)
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 4, rainb12);
    time = 6;
    while (time–)
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, rainb12);
}

void ICACHE_FLASH_ATTR WS2812OutBuffer(uint8_t * buffer, uint16_t length, uint16_t repetition) {
    uint16_t i;
    os_intr_lock();

        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, rainb12);
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, rainb12);
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, rainb12);
        while (repetition–) {
            for (i = 0; i < length; i++) {
                uint8_t mask = 0x80;
                uint8_t byte = buffer[i];
                while (mask) {
                    (byte & mask) ? SEND_WS_12_1() : SEND_WS_12_0();
                    mask >>= 1;
                }
            }
        }

    os_intr_unlock();
}

Ok, so 2 routines for setting the output – the first one turns the output OFF then on (more off than on) – the second one does the same – but more on than off… and it is that kind of timing that, fired into the WS2812B that makes them work.

The third routine sends a reset pulse to the chips then loops through – if you study the loop you’ll see what I meant about repeating the same buffer section over and over again if you don’t actually want separate colours per LED.

Now to make sense of this – you need to know how these chips work. So let’s say you have a strip of 10 of them. You have on the input power, signal in and ground. You CAN run these on 3v3 – but good luck getting a high power 3v3 supply and they are brighter on 5v anyway. If you buy the LEDs and make your own strip – be REALLY careful as they die even looking at a large soldering iron. Each LED has an input and an output and you string them together in series.

As you can imagine – flashing them when you have to send a signal from one to the next takes time – that’s why they are FAST and why the code is critical.

So basically a wide pulse resets ALL the LEDs so they are listening for info. You send an RGB value to the first input – it sucks it in and lights itself up – from that point on it merely buffers and passes on the message – so when you fire the next 3 bytes worth – the SECOND LED lights up and from there on passes everything on – rather than replicate the wheel…

Notice I’ve written that reset pulse 3 times – in the original code, there was a flickering I could not explain on the first LED (I’m running ground and signal into one end of the LED strip, +5v and ground into the other end. I extended that reset pulse a little and completely eliminated the issue.

 

timing

So as you can see – the timings are simple but critical – 0, 1 and reset… then send out bunches of 3 bytes per LED. That’s it.

In my code I might send an instruction like – I want purple full brilliance, repeated across 10 LEDs – and take 10 seconds to do it…

rgb.red=255;

rgb.green=0;

rgb.blue=255;

rgbnum=10;

rgbdelay=10;

That’s it – it happens in the background;

Enjoy and if this works for you – be sure to let others know the link!

Around the corner – code that does this, responds to both serial and MQTT commands and allows extending the controls to external Arduinos, handles both types of temperature sensor, a debounced input used to count electricity meter pulses OR to manually over-ride an output, central heating control and much more – all working and all at the same time…. more on this later. We’re even getting a board made that handles the lot and includes a mains power supply.

30 thoughts on “ESP8266 WS2812B LEDs on a Plate

  1. @moodgorning;
    is there any chance to see your project/hw/code for the “wifi-esp-w22812b-node”.
    do you consider to open it up to the community?do i overlooked a link here?
    would really be appreciated,
    tozett

    1. So the answer is yes without support. It is detailed in the blog. Check bitbucket.com/scargill. It might be .org. But the version up there will soon be replaced. However the ws2812 code has not changed. If you are able to extract what you need you are welcome. I am just about to board a plane hence limited info. Code is in c, works with latest sdk 1.51 on PC. Have since added 2nd serial and more. Should be up there in a couple of weeks when I get back.

  2. Just in case it may be interesting for anyone. I have just tried to use this code with RGBW sk6812 LEDs. It seems to work like a charm. I didn’t even had to change the timings, even though the datasheet mentions different timings. The only thing I did was to extend the reset pulse a little, since I got the flashing first led again. I am still running my app on rgb values, but I transform them to rgbw to save power and get smoother colors for not fully saturated colors.
    here’s my code:
    void SEND_SK_0() {
    SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, leds_gpio[strip_id][1]);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);

    CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, leds_gpio[strip_id][1]);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
    }

    void SEND_SK_1() {
    SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, leds_gpio[strip_id][1]);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
    CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, leds_gpio[strip_id][1]);

    }

    void ICACHE_FLASH_ATTR rgb2rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t* out){
    uint8_t minimum = r < g ? (r < b ? r : b) : (g < b ? g : b);
    out[0] = r-minimum;
    out[1] = g-minimum;
    out[2] = b-minimum;
    out[3] = minimum;
    }

    void ICACHE_FLASH_ATTR ledOutBuffer(uint8_t * buffer, uint16_t length, uint16_t repetition, uint8_t strip_id_p) {
    uint16_t i;
    strip_id = strip_id_p;
    os_intr_lock();

    uint8_t time = 7;
    while (time–){
    WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, leds_gpio[strip_id][1]);
    }
    while (repetition–) {
    for (i = 0; i < length; i+=3) {
    int rgb=0;
    uint8_t rgbw[4] = {0,0,0,0};
    uint16_t k;
    rgb2rgbw(buffer[i],buffer[i+1],buffer[i+2],rgbw);
    for(k = 0; k >= 1;
    }
    }
    }
    }
    os_intr_unlock();
    }

    1. Hi – that’s an interesting variation – I’ve not tried the rgbw LEDs but make constant use of rgb LEDs – worth adding in – thanks.

      1. argh just realized half of the code went missing in copy paste error…
        here’s the complete ledOutBuffer method:
        void ICACHE_FLASH_ATTR ledOutBuffer(uint8_t * buffer, uint16_t length, uint16_t repetition, uint8_t strip_id_p) {
        uint16_t i;
        strip_id = strip_id_p;
        os_intr_lock();

        uint8_t time = 7;
        while (time–){
        WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, leds_gpio[strip_id][1]);
        }
        while (repetition–) {
        for (i = 0; i < length; i+=3) {
        int rgb=0;
        uint8_t rgbw[4] = {0,0,0,0};
        uint16_t k;
        rgb2rgbw(buffer[i],buffer[i+1],buffer[i+2],rgbw);
        for(k = 0; k >= 1;
        }
        }
        }
        }
        os_intr_unlock();
        }

  3. i adopt Array shook hands with WS2812B led strip http://www.ledlightmake.com/rgb-addressable-led-strip-c-80_87/led-ws2812b-addressable-digital-stripsmd5050-strips-60-pcsm-p-216.html always get the Pointer Overflow.
    the Array is static const.Be similar as you define.
    static const uint8_t ledTable[256] = {
    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
    3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
    9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16,
    16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26,
    26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39,
    40, 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
    56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75,
    76, 77, 78, 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98,
    99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117, 119,
    121, 122, 124, 125, 127, 129, 130, 132, 134, 135, 137, 139, 141, 142,
    144, 146, 148, 150, 151, 153, 155, 157, 159, 161, 163, 165, 166, 168,
    170, 172, 174, 176, 178, 180, 182, 184, 186, 189, 191, 193, 195, 197,
    199, 201, 204, 206, 208, 210, 212, 215, 217, 219, 221, 224, 226, 228,
    231, 233, 235, 238, 240, 243, 245, 248, 250, 253, 255 };

    1. Check our code and how we use it – you CANNOT just use 8 bit static FLASH variables – if you read off the 32-bit boundary you will get crashes. See our examples for reading the whole 32 bit chunk and then taking what we need. I have absolutely no problems and my thanks to those who helped us understand this.

  4. Hi

    I have got a matrix diplay.with sk6812 built-in ic smd led.http://www.rgbledcolor.com/

    It seems like the same CMOS process,but I have a question:how to use madrix to control them ?for I want to make a big led wall with these display.

    It there any better solution that it is much simple ?

  5. I remember you having frustration changing the pin from 0 to 12. I can’t for the life of me see in this code how to change the pin from 12 to 4 🙂
    In particular the magic numbers 4 and 8 in
    WRITE_PERI_REG( …BASEADDR + 4, rainb12)
    WRITE_PERI_REG( …BASEADDR + 8, rainb12)
    versus cnlohr’s code
    WRITE_PERI_REG( …BASEADDR + GPIO_ID_PIN(WSGPIO), 1 )
    WRITE_PERI_REG( …BASEADDR + GPIO_ID_PIN(WSGPIO), 0 )

    Seems *way* different. Was his code just “wrong” (and that’s why it happened to only work on GPIO0?

    Cheers,
    John

  6. All solved – it had nothing to do with timing though I HAVE adopted the NOP solution above. But see in the code example ICACHE_FLASH_ATTR in the 2 function definitions – get rid of that and the problem utterly and completely disappears. I’m now ready to use serial LEDS for some serious household mood lighting!

    1. would you mind sharing the adjusted code that came out of these endeavours? I am using your methods in my project to drive my ws2812s and am curious if this improves performance. Thanks in advance.

      1. This is is the only bit that changed. Don’t be tempted to add a cache prefix..

        void SEND_WS_12_0() {
        SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, rainbow12);
        __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);

        CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, rainbow12);
        __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
        }

        void SEND_WS_12_1() {
        SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, rainbow12);
        __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
        __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);

        CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, rainbow12);

        }

        Rainbow 12 is a static – and can be 1 for GPIO0 or 1<<12 for GPIO12 etc.

  7. As mentioned, I’m not using the WS28xx chipset, but I’m not seeing this problem, so it may be a timing issue or a chip set issue.
    In my setup() function (I am using the Arduino development platform) I set the pin mode for the relevant GPIO port :
    pinMode(0, OUTPUT);
    and don’t bother re-setting that at the start of each send in the WS2812OutBuffer() function, but I DO have a delayMicroseconds(25) at the end of the send function to hold the line low to trigger the LED changes.

    1. Well, I’ve tried various combinations and timings and my own code and I cannot stop the first LED from flashing (always green then back to the colour it is supposed to be) as colours change… so if I fade from black to bright red, the first LED will flicker from black to bright green all the way up to bright green to bright red – then settling on red like the others. Got me lost…

  8. I’ve been working with this code to drive some older LED strings I have that needed a bit more tweaking on the timing front, and I noticed that this code continues to set/clear the GPIO port in the while loop, which is actually only required once.
    Removing this set/clear operation from the while loop, the compiler will optimise out a ‘while(time–) {;}’ loop unless the time variable is declared as volatile (this actually instructs the gcc compiler to generate thread safe code when accessing this variable), however greater timing resolution can be gained by using the machine code ‘nop’ instruction (No OPeration) which takes precisely one cpu clock cycle, hence using a logic analyser I was able to get exactly the pauses required to trigger the LED’s, where as they could get a bit unpredictable at the end of the string of 160 LEDS I’m working with (i.e. the last 20 would go to random colours).

    In addition, I have found it quicker to set/clear bits in the GPIO peripheral register than it is to use the set/clear GPIO registers.

    My output functions (NOT WS12xx LED timings) are changed to:

    void ICACHE_FLASH_ATTR SEND_WS_12_0(uint32_t pinMask) {
    SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, pinMask);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);

    CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, pinMask);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
    }

    void ICACHE_FLASH_ATTR SEND_WS_12_1(uint32_t pinMask) {
    SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, pinMask);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);
    __asm__ __volatile__ (“nop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop””\n\tnop”);

    CLEAR_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR, pinMask); // 0x08 = clear
    // __asm__ __volatile__ (“nop”); // NOT NEEDED IN MY CASE
    }

    BTW the pinMask variable is a mask of the GPIO ports, so you can send the same data simultaneously to multiple GPIO’s, or just pass variable rainb12 from the original code.

    I hope that’s useful, and thanks for posting your code for me to work with.

    1. I’ll give that a shot next time I’m doing serial led stuff – thanks for that… yes, the masking is the strangest thing…

    2. I’ve put your code in – and I must admit I do prefer the use of NOPS for a bit more accuracy – but I’m getting the same issue that I’m having with my code (for some reason didn’t originally happen) – when ramping the LED brilliance up in a series of calls – the FIRST LED flashes. So If I’m going from zero to bright red, 10 leds, over say 5 seconds, the first led will flash green on the way up – at the end they all work – but the flashing is really annoying.

  9. Thanks so much for putting up all these great resources on the ESP8266. They have been of tremendous help. I am building a little esp based ARtNet controler for the WS2812B LEDs. I have started from cnlohr’s implementation and was able to correct the flickering using your code. I then merged it with Frans willem’s ESPLightNode code @ https://github.com/Frans-Willem/EspLightNode to enable artnet control on WS2812
    So far this looks very promising, with the esp node’s being auto discovered in standard ArtNet software and running smooth animations on them also works. I am currently running 72 Pixels on one ESP8266 Node and am planning to have 35 of theses setups in total, battery powered inside acrylic tubes. All of these together will then be driven by an ArtNet LED Matrix driver, to form one big display. However I am experiencing random freezes from the nodes. without any crash, or debug messages, so I was wondering if you know a good way to debug these kind of situations. Any hint on how to figure out whats happening would be great.
    I have a little I’m alive timer that prints out one character on the console every 500ms to see if the chip is still running. If the whole thing freezes, the timer also freezes. So it seems as if the whole thing just silently crashes…
    Any ideas would be greatly appreciated. Thanks in advance.

    1. I’d love to help but to be honest I’ve found the ESPs to be very reliable hence I’ve not developed useful debugging skills for the devices yet. I guess you just have to keep looking for a pattern of failure.

      1. Ok after a lot of puzzeled hours, I have now stabilized the situation. Turns out that my chips had problems with the wonky wifi in my basement. I had a repeater installed which seemingly did a bad job at getting enough signal from the main router, which seems to have had some influence on the ESPs. I have since changed my wifi setup, so that I have a strong signal in my basement without the repeater and now the chips are connecting to my wifi fine and don’t crash anymore. Maybe that helps someone else.

  10. Dear Peter,

    I was looking for hints on programming the esp8266 so it could control a WS2812 strip and I tried your code. While i understand your code, i cant seem to get it to work. I’ve tried numerous different configurations and projects. Is it possible for you to share the part of your code which controls the ledstrip and works for you? or at least give a few hints on how to get this to work?

    Kind regards,
    Martijn.

      1. Thank you so much for your fast reply. I don’t know exactly where i went wrong but after changing a few parts with the code from your github it is working! I still have some minor things to work out, but i’ve managed to get them working finally. Thank you again, and keep up the good work!

  11. All great stuff Peter …. must admit I’m still using the switching the port to input on the first 32 bits before sending a total stream in output mode. It seems to be the only way for me. But there again, it seems making any changes anywhere can have pretty dramatic consequences!
    Instead of the table I tried a cube law function, ie (rgb.n ^3) / 65025. Pretty usable actually, in dimming and colour changing. I agree with you about node-red, it’s a pretty handy tool!

Comments are closed.