Magic Rainbow Xmas/Party Lights - NeoPixel, Arduino

1,316

19

4

Published

Introduction: Magic Rainbow Xmas/Party Lights - NeoPixel, Arduino

About: Engineer, retired. Have always had an interest in electronics, often related to radio control. This evolved into a part time business that I still practice. I do some voluntary work in schools to encourag...

It is time to enter the wonderful world of NeoPixels. With over 65,000 colours and the ability to change the colours in a whole string 100 times a second there are few constraints on what can be done. Programming an Arduino is very easy with the Adafruit NeoPixel library.

The theme here is colours of the rainbow - that is not covered in the Adafruit guides. The code shown here overcomes the challenges of getting the 4 mixed colours and then moving through the whole rainbow spectrum evenly.

The first objective of this project was to make some rainbow coloured lights for a Xmas tree and then add some magic using the power of NeoPixels. The magic is that you can ask someone to pick a LED and ask them if they can will it to change colour – which in a second or so it will do. Each LED has a nominal colour of the rainbow but continually drifts to a new random colour value that is set every couple of seconds or so. The new colour is no more than one colour up or down. So an ‘orange’ LED colour will drift to randomly set colours between red and yellow, and a ‘yellow’ LED will drift between orange and green, and so on. The new target colours are set in sequence down the 60 LED string - but only one is changed between each string update so all the changes are taking place at different times, and by using a PRBS (pseudo-random binary sequence) all the target colours will be different. So the outcome is some very pretty and mesmerising lights. Added to this is the option to have the LEDs twinkle, again at random. And if someone asks how it works you can tell them that it is driven by a special type of pseudo random number generator called a Fibonacci Linear-Feedback Shift Register.

Then, as there was space on the board for a second switch, another pair of schemes were devised that smoothly cycle each LED through the whole rainbow spectrum – but all starting at different points.

There is scope to tune the code to deliver variations on these themes.

To do this you will need:

  • An Arduino (e.g. Uno for testing and/or pro mini for final arrangement)
  • 5v (>1.5A) power supply
  • 1000uf 6.3v capacitor, 330 ohm resistor.
  • NeoPixel LED string WS2812B (60 LEDs) or WS2811 string (50 LEDs) or PL9823 LEDs (DIY string)

Optional:

Circuit board as attached with

  • Micro USB socket
  • 2 off 0.1uF 0805 SMD capacitors
  • 2 off surface mount switches 7mm x 3mm

In the quest for the best lighting option I started with a 60 LED WS2812B string. This was good for developing the code but the string is not long enough to use on a tree as the LEDs are too close. Next I got a WS2811 string that had 8mm RGB LEDs. These diffused LEDs were better as the colour can be seen from a greater viewing angle. The total string length was around 4m (8cm between LEDs) and hence could be used on a tree and are great for room decoration. I had hoped I might be able to rewire these with a longer distance between the LEDs for the tree application. However they were potted (that is good for wire retention and for outside use) and hence this looked a difficult option. So I lastly got some PL9823 LEDs (that include a WS2812 equivalent controller) and made up my own string. To drive all these I made a bespoke circuit board to hold an Arduino Pro Mini and make a neat solution that just needs connecting by a USB lead to a USB power supply (1.5A or greater).

Step 1: Circuit Board

The PCB shown here is optional. The same outcome could be delivered by hard wiring pretty well any Arduino. In addition to the Arduino and the LED string Adafruit recommend adding a 1000uf capacitor (6.3v or greater) to the power lines to the string. They also recommend a resistor of around 330 ohms in series with the data connection. This provides some protection if the string is not powered up with the Arduino. Note that the power to the string must be provided directly. Arduinos are unlikely to be able to deliver the current required. Many have fuses and/or diodes in their supplies. I considered using a Nano or a Leonardo but neither can deliver the current required from their 5v lines.

The circuit board adds these components, and a micro USB socket, plus a couple of switches and holds an Arduino Pro Mini. I got the components on ebay. The switches are sold as 'SMD Switch 7mm x 3mm'. See photo below as double check:

My approach to PCB making is to print the artwork twice onto tracing paper. I then overlay these to double the contrast and cover any small imperfections in the printing (I use a laser printer). I punch holes in the edge of the upper layer, place Sellotape across the holes, align and then press on the holes to stick. I have a UV exposure unit. I used to use a UV black light that worked fine with spray coated PCBs and should work fine with positive photoresist boards. I use weak sodium hydroxide solution (drain cleaner) to develop and Di-Sodium Peroxodisulphate Hexahydrate to etch. Take special precautions with the chemicals, especially the sodium hydroxide that attacks flesh instantly. I then expose again and develop to get rid of the film over the tracks and finish off with some immerse tin (quite expensive – and limited life). The latter step is optional.

The switch locating holes are 0.7mm, the capacitor 0.7 or 0.8mm, holes to the Arduino 0.7 to 0.9mm (use latter if using pinstrip pins to connect), holes for leads to the Led strip 1.2mm.

For component placement see photos below:

Step 2: Arduino Code 1

The final code is attached and has some options for tuning to one’s own needs. Create a folder called NeoPixLEDs in your sketchbook folder and then add the attached NeoPixLEDs.ino .

The code needs the Adafruit NeoPixel library. The download instructions can be found at:

https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation

The code here started off just setting the LEDs to the 7 colours of the rainbow in repeating sequence down the string. As mentioned above this required some significant tuning to get the 4 colours that are created by mixes of R G and/or B.

For reference the settings I chose were:

ColourRGB
Red25500
Orange255320
Yellow2551270
Green02550
Blue00255
Indigo400255
Violet255080

This looked great and the colours nice and bright. However with 65,000 or so more colours to choose from this seemed to be ignoring the possible options with these LEDs. Hence the idea to have them slowly changing, to values within a range of +/- 1 colour. For this I needed a random number generator and decided I would use a Pseudo Random Binary Sequence (PRBS) or more specifically a Fibonacci Linear-Feedback Shift Register. These have many benefits. The code is very simple and fast. They appear to be random but actually deliver a specific sequence. The sequence cleverly includes every number in the range, except zero, just once before repeating. I went with a 9 bit PRBS that gives 511 different numbers before repeating. And then because when this goes around again it will be at a different position/colour LED the overall colour sequence is 7*511 or 3577 changes long – which is plenty long enough. And note these are just the target colours. The code takes 64 steps to get to the target.

For more information on PRBS and Linear Feedback Shift Register look on Wikipedia: https://en.wikipedia.org/wiki/Linear-feedback_shift_register.

Another benefit of using 9 bits is that I could test the top bit and then if high use the low byte to direct to a colour value down the rainbow sequence and if low use the low byte to direct to a colour up the rainbow sequence. Note that size ‘steps’ taken are often different as the rainbow colours are not at all evenly spaced around the RGB colour wheel. Plus this is made worse by the mismatch of the strengths of the R G and B LEDs.

I created a function (random9() ) that when called gives the next number in the sequence. There is a section of code that is commented out that can be uncommented to send the number sequence to the serial port and show the number of numbers given before it comes around to the original ‘seed’ value. If you try this do comment it out again afterwards so the function will work as intended.

Before explaining the ‘twinkle’ operation I should cover how the code works. The code has been set for up for 60 LEDs. These are all updated around 70 times a second. (This can be slowed down of speeded up by changing a delay value). Each time the string is updated just one LED gets a new target value. The target value is derived from the new random number. From this a ‘fade’ value is calculated that gives the linear rate of change needed to get to the target value over 60 cycles. I changed this to 64 steps as this enabled the use of a number shift right to achieve the division that is considerably faster from a code perspective. I had to hold the colour values (two per LED) as 16 bit integers to avoid rounding errors. In addition to one LED getting a new target value all the other LEDs are incremented by the fade values between each string update. One has to be a little careful with Neopixel code as with 60 off the memory requirement can grow quickly.

In addition to the continuous random colours it seemed a nice idea to have the option for the LEDs to twinkle. This can be done by setting all of R G and B for a LED to their maximum values for one cycle (a few milliseconds) and then return to the colour previous colour setting. The randomness here is delivered by comparing the LED number with 60 numbers in the middle of the random 1 - 511 range. If these are equal then the code saves the colour value and sets the R G and B high. Since there can only be one match per update it is only necessary to save one value. This also means that only one LED will twinkle at and time. However there is not always a match so there are many updates where no LED twinkles. This adds to the apparent randomness.

Lastly, when looking at the bespoke circuit board, I added a switch so that rather than having to set the twinkle of/off in code it might be activated by a switch. There was space for a second switch so another setting can be adjusted. So I added an additional couple of schemes where each LED cycles though the whole rainbow. The routine above is driven by void NeoRand() while the second routine is driven by void NeoSmth() (see next step).

Step 3: Arduino Code 2

The second set of schemes cycles each LED through the whole rainbow - with each LED starting at a different point - so every LED is a different colour - all the time. The the string can show one rainbow or several, all changing continually so the rainbow moves along the string.

For this I created a function that steps from colour to colour through the whole rainbow, taking 64 steps to move to the next colour and hence 7*64 steps to cycle through the rainbow. The function takes care of the different size steps required at different parts of the colour wheel. For example steps of 8 RGB units are needed between Green and Blue but only 0.5 RGB units between Red and Orange.

The code then needs two variables - one to set the colour index of the first LED and the second to set how far away the colours of the next LEDs will be. I set the first variable (cnt) to index by 1 each string update. This could be incremented by a bigger number to give faster movement of the colours - but I would reduce the delay first. The increment from LED to LED sets the range of colours shown along the string. If thus is set to 1 the end of the string will just about be one colour from the first. If it is set to 7 the string will show one rainbow set of colours. A setting of 65 will mean that each LED is just a tiny bit more that 1 colour away from the previous one - so every set of 7 LEDs shows the whole rainbow. These latter two values are set in the code depending on the switch input.

The RAM requirement is under 1k and hence the code should run on pretty well any Arduino.

Note also that the Adafruit NeoPixel library can drive a load of different types of LED string. These are set by just one line of code. I have included the lines required for the three types used here (uncomment the one needed). See the library guide for other options if required. The code as attached is good for strings up to 60 LEDs. It could be modified for longer strings. This needs more than just changing the value of NUMPIXELS. If one wanted to drive 120 LEDs then in addition to setting NUMPIXELS to 120 the fade value also has to be halved – so add a right shift (divide by 2) to the calculation here. The code should work ‘out of the box’. However see Testing/setup below for how to tweak it. However I hope that from the description of the code other ideas might be tried as well.

Step 4: Testing/setup

I expect that the code as attached will probably be close enough to use as is, with or without the switches. The type of NeoPixels does need to be set - by uncommenting the appropriate line starting:

Adafruit_NeoPixel pixels =

The NeoPixel library can drive a wide range of types. For more details see the Adafruit 'uberguide' at: https://cdn-learn.adafruit.com/downloads/pdf/adaf...

The colours can be tuned if necessary. The red, green and blue LEDs in the strings I have tried were not well balanced when it comes to making mixed colours - orange, yellow, indigo and violet. For example one would expect a mix of full power green and full power red to give yellow. The result is a bright green because the green is relatively more powerful than the red. So I ended up using 255 red with 127 green to get yellow. Orange was even more elusive, and getting indigo and violet took quite a few tries. So the firmware can be set to disable the random element to enable tuning to make sure the core colours are a good as they can be. However I found that the same settings worked for the WS2811 and PL9823 strings as for the NeoPixel (WS2812B) string so this may not be necessary.

If you want to check the basic rainbow colours uncomment line 20 #define test and download. Also set the switches off. This will give the centre colours that should be the 7 colours of the rainbow. If you are not happy with any of these go to the section in ‘void NeoRand()’ starting:

switch (c) { // update LED colours

then edit the red/green/blue under each colour and download again. Note that at least one value for the three colours will be set to 0 (mix only 2 colours) and the numbers entered must be between 0 and 255 (min – max).

The data output and switch pins have been set for the custom PCB but can be changed to suit other boards/arrangements. Avoid using D0, D1 as these are used for the serial port when programming.

The Arduino Pro Mini is most easily programmed using a FTDI board, as below:

Take care when wiring to the LED strip. See black, white and red wires above for ground, data and 5v.

The WS2811 LED string came with a 2 pin connector and a 2 wire lead. The former is the data and ground and the latter the power. Check by looking at the labels on the LED circuit board, and make sure you are connecting the data wire to DI rather than DO.

The WS2812 LED strip came with a 3 pin JST SM connector. This is rated at 3A and seemed like a good standard to use. I got some more of these from Farnell, codes as below. These are also available on ebay.

pins3357661
pin header3357739
sockets3357673
socket header3357697

I removed the data and ground lead and soldered a data wire to the 3 pin JST connector:

I also trimmed the leads at the far end as I didn't see my extending the length. The voltage drop is pretty high and adding second string would need a fresh power connection.

Video:

Step 5: PL9823 LED String

This took quite a bit more work than I anticipated - several evenings of soldering. There are 50 small circuit boards plus 150+ wires to connect. The wires need tinning before soldering. Overall there are 700 solder joints. The better quality wires double the cost compared to the WS2811 string! One wonders how these can be made at such low cost. However one has little choice if one wants the greater LED spacing (although in retrospect cutting the wires and replacing with the longer/better ones is worth considering). I used a wire length of 18cm to give a string nearly 9m long, plus a 1m lead to the driver board. I used black 22AWG and 20AWG wires, 20m of 22AWG plus 10m of the 20 AWG. I used the thicker wire for the power leads to the string and the power leads to the first 22 LEDs. Voltage drop is a concern, especially when extending the overall length. On the WS2811 string the voltage had dropped from 5v to 3.7v at the far end. The better wires here led to a drop of just 0.6v despite the string being twice as long.

There was a little bit of thought into the spacing. If these are to go onto a Xmas tree with branches over height of 1.5m and base radius of 0.5m - then the cone area is pi*1.5*0.5/2 m2 or 1.18m2. So with 50 LEDs each has to cover an area of 0.024 m2 - or a circle of diameter just over 17cm. Using 18cm leads would get close to this - and use 30m in total - so none would be wasted.

The PL9823 LEDs look like standard RGB LEDs but also include a LED controller similar to the WS2812 LEDs. They come in 5mm and 8mm sizes. I used the latter. The 4 wires are data in, positive, ground and data out, with the latter next to the flat on the package.

I designed an 8x10mm double sided circuit board to mount this and to connect to the incoming and outgoing power and data wires. I did the artwork for 10 off to make the etching a bit easier. I have attached the artwork. The input side of the board also holds a 0.1uF 0805 capacitor. There is no harm including this when the data is flying around at 0.8 Mhz.

After making the boards and drilling the holes at 0.8mm I cut them into separate boards and added the capacitor.

The LED leads need to be splayed out a bit and then bent a right angles. I found a piece of 3mm aluminium (an old heatsink) that was ideal for getting the bend at the right place:

These were then inserted on the PCBs: and soldered in place: Note the orientation of the flat on the case.

I had decided on black wires for power and data. So the potential for mis-wiring seemed quite high! In the event following leads by position worked fine and no mix-ups occurred. The first step was to connect the three wires to the input sides (with the capacitor) of all the boards. The order is ground, positive and data in (from left to right):

I cut some heatshrink tube to 18mm lengths to cover the PCB and give some support to the wires. This has to be threaded on to the first LEDs wires and then the wires from the next LED threaded though so they can be soldered onto the output side of the board (no capacitor). The wire order is positive, ground and data out (from left to right):

I tested the string every 5 LEDs or so to make sure I had the connections right. When all done I added some hot melt glue over the wires and then shrunk the tube.

The complete system looks like:

Video:

I hope this get you creating with NeoPixels. Once you have an Arduino connected to a NeoPixel string you can make a display to your choosing. I now have a set for a Xmas tree and a set I hung up inside for New Year.

Happy creating.

Mike

Share

    Recommendations

    • Oil Contest

      Oil Contest
    • Water Contest

      Water Contest
    • Clocks Contest

      Clocks Contest

    4 Discussions

    Hi Mike

    Thanks for your instructable. Idownloaded the ino file tried to compile on a NodeMCU using Arduino IDE and came up with numerous errors. Can I send you the first three that looks to be a problem. Here goes.

    NeoPixLEDsTest:38: error: 'unsigned int index' redeclared as different kind of symbol

    unsigned int index; // used to set colours, range 0-0x1FF

    ^

    In file included from C:\Users\Bob_Office\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0/tools/sdk/libc/xtensa-lx106-elf/include/stdlib.h:11:0,

    from C:\Users\Bob_Office\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0\cores\esp8266/Arduino.h:27,

    from C:\Users\BOB_OF~1\AppData\Local\Temp\arduino_build_44580\sketch\NeoPixLEDsTest.ino.cpp:1:

    C:\Users\Bob_Office\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0/tools/sdk/libc/xtensa-lx106-elf/include/string.h:54:15: error: previous declaration of 'char* index(const char*, int)'

    char *_EXFUN(index,(const char *, int));

    ^

    C:\Users\Bob_Office\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0/tools/sdk/libc/xtensa-lx106-elf/include/_ansi.h:65:30: note: in definition of macro '_EXFUN'

    #define _EXFUN(name, proto) name proto

    ^

    Any chance you can help me out a bit with this?

    Regards and thanks in advance

    Bob (Sydney Australia)

    2 more answers

    Bob. The NodeMCU uses a diffferent compiler and so the original code may need changing. It does not seem to like the 'unsigned int' declaration. Try replacing these with uint16_t . You may well have to make further changes for the different compiler. The code should work fine with all the Arduino boards. Mike

    Thanks FDM you're right - strange that happened - I thought the NodeMCU board would be taken care of by the Arduino IDE and it's compiler. Seems there is some differences somewhere. Anyway I'll run it on an arduino and thanks again, it works just fine on a uno.

    That's a fun lighting idea! I love lots of colors in our Christmas lights :)