Tag Archives: MQTT Mood Lighting with ESP8266 and WS2812B

WS2812b Success on the ESP-12

This one has been long in the making.  I’ve had a fair bit of success with the little ESP-12 and it is now my favourite despite the 2mm centres.

WS2812BWhy, because it HAS everything – lots of port bits, A/D convertor etc. why on EARTH we all went off and used the little ESP-01 I can no longer understand especially as the ESP-12 is the same price!

Ok, so getting ports on and off was no big deal and even reading them was simple – but getting the timings right for the WE2812b LEDs has been a pain.

I eventually found a blog (all of this is referred to in the comments section of a blog a couple of days ago in here) which detailed how to use the WS2812b. Then I found a guy saying it was no good – and offering his slight modification – and I tested it and.. lo and behold – on GPIO0 it works.           

But could I HELL get it to work on other ports – as I was stumbling around in the dark as to how to use the PORTS – the problem is timings – the WS2812b need VERY short pulses and they are not very forgiving if you don’t get the timings right. There are LOADS of ways to turn ports on and off on the ESP8266 but they all take differing times and if the port operation is part of your (very tight) timing – then any change will stop this from working.

Here is the original code that worked – on GPIO2 -  this is just the bit for individual pulses and is commented out

//void ICACHE_FLASH_ATTR SEND_WS_0()
//{
//    uint8_t time;
//    time = 4; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 1 );
//    time = 9; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 0 );
//}

//void ICACHE_FLASH_ATTR SEND_WS_1()
//{
//    uint8_t time;
//    time = 8; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 1 );
//    time = 6; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 0 );
//}

So you see, that statement WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 1 ); is an integral part of the loop timing. Can’t go messing with it… but here’s the thing – this UTTERLY MEANINGLESS gook is actually incredibly simple…

Taking the first one it is this..

(*((volatile uint32_t *)(0x60000300 + (0+(0))))) = (uint32_t)(1)

and narrowing that down it is simply..

*(0x60000300)=1

Ok, so this made NO sense to me AT ALL…. until I hit THIS link thanks to one of you kind people.

https://github.com/esp8266/esp8266-wiki/wiki/gpio-registers

STUDY THIS… IT WILL REMOVE ALL HEADACHES.

It turns out that there are two magical locations on the chip – which I’m now using to turn things on and off. They are 0x6000304 to turn things on and 0x6000308 to turn things off – go ahead and define pretty names for them. This is weird. If you fire a 32 bit number at these locations – the first one will turn on any port who’ bit is HIGH while ignoring the rest. The second address does the same to turn them back off.

So if you consider binary 1 to be GPIO0, 2 to be GPIO1 etc etc this makes sense.

These two lines will turn the port 12 (GPIO12) on then off VERY, VERY quickly.

WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 4, 0x1000 );
WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 8, 0x1000 );

EH? 0x1000 in binary is 1000000000000

Port 0 would be 000000000001 or just 0.

Weird, yes, FAST, certainly. Now you might say well that’s all good for one port but what happens if I want to use another port – well as long as you don’t want to handle them at the same time, it turns out you can use a STATIC variable and it appears to work just as well!!!

So – enough bull – here’s some code. I do not claim ownership – little bits are mine, little bits are from other people etc… well done to all I would say.

 

Somewhere up at the top – some global vars

uint16_t reda;
uint16_t greena;
uint16_t bluea;

uint16_t red;
uint16_t green;
uint16_t blue;

uint16_t rgbnum;
uint16_t rgbdelay;

 

static uint32_t theport;

Now the bits that do the work…

void ICACHE_FLASH_ATTR SEND_WS_0()
{
    uint8_t time;
    time = 4; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 4, theport );
    time = 9; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 8, theport );
}

void ICACHE_FLASH_ATTR SEND_WS_1()
{
    uint8_t time;
    time = 8; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 4, theport );
    time = 6; while(time--) WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 8, theport );
}

void ICACHE_FLASH_ATTR WS2812OutBuffer( uint8_t * buffer, uint16_t length, uint16_t repetition )
{
    uint16_t i;
    os_intr_lock();
    //GPIO_OUTPUT_SET(GPIO_ID_PIN(WSGPIO), 0);
    WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 8, theport );

while (repetition--)
    {
    for( i = 0; i < length; i++ )
    {
        uint8_t mask = 0x80;
        uint8_t byte = buffer[i];
        while (mask)
        {
            ( byte & mask ) ? SEND_WS_1() : SEND_WS_0();
            mask >>= 1;
            }
    }
    }
    os_intr_unlock();
}

Now why do I have REDA and RED -  well I don’t want these lights instantly changing colour – that’s boring – I want it to happen in the background – so – make yourself a 50ms timer callback and dump this inside.

/// rgb to fade from any colour to any other colour for any number of LEDS for any given period (rgbdelay)

if ((red!=reda)||(green!=greena)||(blue!=bluea))
    {
    uint8_t myBuffer[3];
    if (reda<red) reda+=((red-reda)/(rgbdelay*20))+1;
    if (greena<green)  greena+=((green-greena)/(rgbdelay*20))+1;
    if (bluea<blue)  bluea+=((blue-bluea)/(rgbdelay*20))+1;
    if (reda>red) reda-=((reda-red)/(rgbdelay*20))+1;
    if (greena>green)  greena-=((greena-green)/(rgbdelay*20))+1;
    if (bluea>blue)  bluea-=((bluea-blue)/(rgbdelay*20))+1;

    myBuffer[0]=ledTable[reda];
    myBuffer[1]=ledTable[greena];
    myBuffer[2]=ledTable[bluea];
    if (rgbnum==0) rgbnum=1;
    WS2812OutBuffer(myBuffer,3,rgbnum);
    }

I’ve only allocated 3 bytes for the RDB buffer as I’m not interested in individual colours – just changing them at once – and I repeat the whole thing by rgbnum – the number of LEDS and rgbdelay the time you want this to take to change from one colour to the next. It will do a nice job and smoothly without any horror colour changes – basically for each colour if the desired colour does not match the actual colour, adjust it by 10%.

oh and you’ll need this table… I found that on the web too. It’s simple enough – note that increasing values have little effect at first, getting more and more relevant as you get higher up. That’s because the difference in brilliance between 10 and 20 is large, the difference between 240 and 250 is almost zilch – to our eyes anyway.

So this is non-lossy –as the colours fade down, colour accuracy goes down but as you fade them back up it returns.

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 };

Oh, you also need to ensure that the output you use is actually an output!!!! So why are you waiting – moodlighting – SAD LIGHTS ---  I’ve not tested more than 30 LEDS so do some tests before going wild.

Another thing to note – avoid the callback fading starting while the WIFI is setting up… I found using this immediately at startup caused issues – once you have connections it’s fine.

Facebooktwittergoogle_pluspinterestlinkedin