Simple WS2812B LED Interface/Controller in one chip

Simple WS2812B LED Interface/Controller in one chip

Home

Copyright 2016 Unique Design Concepts

udc.aus@gmail.com

The WS2812B is an RGB LED with a built-in integrated control circuit. Each of the three LEDs in the device could be set to 256 intensity levels to create a total of 16777216 colours. Up to 1024 LEDs could be serially chained in a strip and individually controlled. This is an amazing product with limitless areas of application.

The LED uses proprietary serial interface, which requires specialized device to drive it. It could be implemented in a programmable logic device or a general purpose processor running real time firmware.

A number of different interfaces for WS2812B are available at the moment, including popular single board computers. All of them require a relatively complex electronic controller and relatively advanced programming skills to use them.

Other software solutions designed for WS2812B LED strips are using primarily three serial interface techniques:

This project is is using PIC12F1840 microcontroller and code entirely written in C language without a single assembler instruction. It uses the free non optimized Microchip XC8 compiler, which is slow and using the bit banging method is not practical.

The PIC12F1840 does not have a DMA controller. 

The PIC12F1840 SPI controller is not capable of transmitting back to back characters, which is a critical requirement to meet the WS2812B LED timing specifications.

The above limitations created a significant challenge to write firmware that fully satisfies the timing specifications of the WS2812B LED chip. Non trivial techniques had to be used to achieve the desired result.

The goal of this project is to deliver the simplest possible way to interface with WS2812B. People with no programming skills will be able to use it in their Do It Yourself projects.

Interface specifications summary:

Areas of application:

The following schematic diagrams illustrate how to build different circuits with the PIC12F1840 interface chip. These schematics exclude the filtering, surge protection and other components, typically used in high reliability designs.

Example 1

Figure 1 shows simple lighting application. A strip of 1 to 1024 WS2812B LEDs is turned ON (all LEDs set to maximum intensity white colour) when +5V is applied. The LEDs could be turned OFF by closing the optional low power switch connected to pin 4 of PIC12F1840. If more than 1024 LEDs need to be controlled, additional LED strips could be connected to pin 7 of PIC12F1840. The number of strips is limited by the driving capability of the PIC12F1840 output pin. Additional LEDs (and strips) could also be connected to each WS2812B LED output pin.

Figure 1

Video 1 demonstrates a breadboard circuit implementing Figure 1 schematic and controlling a strip of 144 WS2812B LEDs.

Video 1

Example 2

Figure 2 shows lighting application with brightness control. A low power potentiometer R2 is used to set the LEDs white colour intensity from OFF to maximum level. More than 1024 LEDs could be controlled as explained in Example 1.

Figure 2

Video 2 demonstrates a breadboard circuit implementing Figure 2 schematic and controlling a strip of 144 WS2812B LEDs.

Video 2

Example 3

Figure 3 shows application with 8 colours LED control. Each of the three WS2812B internal RGB LEDs could be turned OFF by closing the corresponding RED, BLUE and GREEN switch (button). This way up to 8 colours (black, red, green, blue, yellow, cyan, magenta and white) could be set. More than 1024 LEDs could be controlled as explained in Example 1.

Figure 3

Video 3 demonstrates a breadboard circuit implementing Figure 3 schematic and controlling a strip of 144 WS2812B LEDs.

Video 3

Example 4

Figure 4 shows application with full LED colour control. The intensity of each of the three WS2812B internal RGB LEDs could be set between 0 and 255 via the corresponding low power RED, BLUE and GREEN potentiometers. This way, any of the WS2812B 16777216 colours could be configured. More than 1024 LEDs could be controlled as explained in Example 1.

Figure 4

Video 4 demonstrates a breadboard circuit implementing Figure 4 schematic and controlling a strip of 144 WS2812B LEDs.

Video 4

Example 5

Figure 5 shows application with full LEDs colour control via UART interface. Each LED in the strip could be individually set to any of 16777216 colours. Only one simple ASCII text command is used to set all strip LED colours. The UART baud rate is set to 115200 and the flow control is RTS/CTS.

The Rx input signal is where the UART serial command is sent to. The RTS (Request To Send) output is set to high (+5V) when the PIC12F1840 is ready to receive a command. The EN (enable) input has to be driven high (+5V) or left open for the command to be processed by PIC12F1840. If EN is driven low, the command sent to Rx input is ignored. The EN input could be used to select one of many PIC12F1840 interfaces (multiple LED strips) driven by a common UART interface.

Figure 5

Video 5 demonstrates a breadboard circuit implementing Figure 5 schematic and controlling a strip of 144 WS2812B LEDs.

Video 5

The animation in the video is achieved by sending multiple commands back to back. The commands are sent from a Windows PC running PuTTY terminal emulator:

http://www.putty.org/

The PIC12F1840 serial interface is connected to the PC via USB to TTL-232 cable:

http://www.digikey.com/product-detail/en/TTL-232R-3V3/768-1015-ND/1836393

Here is an example of a command setting a strip of one LED to maximum intensity white colour:

S0001255255255

The above command is constructed by the following fields:

S              # strip command header character

0001        # strip length value in the range 1 to 1024

255          # green colour intensity value in the range 0 to 255

255          # red colour intensity value in the range 0 to 255

255          # blue colour intensity value in the range 0 to 255

Here is an example of a command setting a strip of three LEDs:

S0003255000000000255000000000255

The above command is constructed by the following fields:

S              # strip command header character

0003        # strip length

255         # first LED green colour intensity

000         # first LED red colour intensity

000         # first LED blue colour intensity

000         # second LED green colour intensity

255         # second LED red colour intensity

000         # second LED blue colour intensity

000         # last LED green colour intensity

000         # last LED red colour intensity

255         # last LED blue colour intensity

The “first” LED is the one at the end of the strip. The “last” LED is the one labeled as LED1 on Figure 5.

All LEDs in the strip can be loaded quickly with the same colour by specifying strip length 0 followed by the colour. For example the following command will set all LEDs in the strip to maximum intensity green colour.

S0000255000000

Any “white” characters such as space, tab, carriage return or new line are ignored by the PIC12F1840 interface. Any characters following (and including) the character # will be ignored until the end of the line is reached. The # character could be used to include comments between the command fields as in the two examples above.

Example 6

Figure 6 shows application of a simple animation controller. The additional 23LCV1024 memory chip is used to store the colour settings for up to 16384 LEDs. The LED strip colours could be reloaded directly from the memory chip without the involvement of the UART interface. The battery connected to the memory chip is to preserve the memory content, when the +5V power supply is switched off.

Two ASCII text commands are used to program an animation sequence via the UART interface.

The first command is used to write the desired LED strip colours into the 23LCV1024 memory chip. This command could be sent as many times as needed to fill the required LED colours in the 23LCV1024 memory chip. The second command specifies up to 84 program sequences. Each program sequence will load all LEDs in the strip from a specified 23LCV1024 memory LED location. Each sequence also includes a delay time (in 50 milliseconds units), before the next sequence is loaded, allowing variable speed of animation. Every time a program sequence command is sent, it will overwrite any settings from a previous program sequence command.

Figure 6

Video 6 demonstrates a breadboard circuit implementing Figure 6 schematic and controlling a strip of 144 WS2812B LEDs.

Video 6

The UART interface is connected to a PC in the same way as described in Example 5.

The following two commands will create and store LED strip animation that will play automatically in a loop every time after the +5V is applied. 

The first command will write the colours (red and blue) of two LEDs into 23LCV1024 memory location 1 and 2 respectively: 

W00001000255000000000255

The above command is constructed by the following fields:

W  # write command header character

00001  # write command start LED address in the range 1 to 16384

000         # first LED green colour intensity

255         # first LED red colour intensity

000         # first LED blue colour intensity

000         # second LED green colour intensity

000         # second LED red colour intensity

255         # second LED blue colour intensity

The following command will create a program with two sequences for a strip with one LED. The LED will blink with red and blue colours as specified with the write command earlier.

P00010000102000002040L

The above command is constructed by the following fields:

P         # program sequence command header character

0001        # strip length value in the range 1 to 1024

00001  # first sequence memory LED start address in the range 1 to 16384

020         # first sequence delay is 1 second (range is 0 to 999 in 50 milliseconds units)

00002      # second (last) sequence memory LED start address in the range 1 to 16384

040          # second (last) sequence delay is 2 second (range is 0 to 999 in 50 milliseconds units)

L         # start program sequence loop

Performance

For Example 1 to Example 4, all strip LEDs are updated 28.2 ms after a control input (e.g. LEDs ON button) changes state.

Table 1 shows the times for updating different strip lengths using the schematic in Example 5. The time is measured from sending the first command character ‘S’ to the time all LEDs in the strip are updated. The numbers (e.g. strip length and LED colours) in the command string could be encoded in ASCII hex or binary format to reduce the number of transmitted characters over the UART. 

Table 2 shows the times for updating different strip lengths using the schematic in Example 6. The strip colours are first loaded into the 23LCV1024 memory via UART and the write command described in Example 6. Then the LED strip colours are read from the memory via a read command:

S0001R00001

The above command is constructed by the following fields:

S         # strip command header character

0001        # strip length value in the range 1 to 1024 

R         # read command header character

00001 # read command start LED address in the range 1 to 16384 

The time is measured from sending the write command character ‘W’ to the time all LEDs in the strip are updated. The numbers (e.g. strip length and LED colours) in the command string could be encoded in ASCII hex or binary format to reduce the number of transmitted characters over the UART.

Once the 23LCV1024 memory is loaded with LED colour information, it could be read as many times as needed. Reading the memory from different memory location will load the strip with different colour patterns. Table 3 shows the strip loading times excluding the write command. The time is measured from sending the strip length command character ‘S’ to the time all LEDs in the strip are updated.

The strip length command could be sent once only, followed by a series of read memory commands.

More details of the UART hex interface protocol

The ASCII text commands in the above examples are using decimal numbers to specify LED colours, LED strip length, LED address and sequence delay.

To speed up the LED strip colour updates, the number of bytes sent over the UART interface can be reduced by replacing the decimal numbers with hex numbers.

For example the following command uses decimal numbers to set individual LED colours of a strip of 16 LEDs:

S00160000001230001230000001231231230000001230001231231230001231231230270000000000002340002340000002342342340000002340002342342

34000234234234000088000

The equivalent command using hex numbers is:

s01000007b007b00007b7b7b00007b007b7b7b007b7b7b1b00000000ea00ea0000eaeaea0000ea00eaeaea00eaeaea005800

The decimal command uses 149 ASCII characters, while the hex command uses only 100.

Here is the commented version of both commands:

Decimal command:

S                   # strip command header character (note it is in upper case)

0016             # strip length 16

000000123   # first LED green (0), red (0) and blue (123) colour intensity

000123000   # second LED green (0), red (123) and blue (0) colour intensity

000123123   # third LED green (0), red (123) and blue (123) colour intensity

123000000   # fourth LED green (123), red (0) and blue (0) colour intensity

123000123   # fifth LED green (123), red (0) and blue (123) colour intensity

123123000   # sixth LED green (123), red (123) and blue (0) colour intensity

123123123   # seventh LED green (123), red (123) and blue (123) colour intensity

270000000   # eight LED green (27), red (0) and blue (0) colour intensity

000000234   # ninth LED green (0), red (0) and blue (234) colour intensity

000234000   # tenth LED green (0), red (234) and blue (0) colour intensity

000234234   # eleventh LED green (0), red (234) and blue (234) colour intensity

234000000   # twelfth LED green (234), red (0) and blue (0) colour intensity

234000234   # thirteenth LED green (234), red (0) and blue (234) colour intensity

234234000   # fourteenth LED green (234), red (234) and blue (0) colour intensity

234234234   # fifteenth LED green (234), red (234) and blue (234) colour intensity

000088000   # sixteenth LED green (0), red (88) and blue (0) colour intensity

Hex command:

s                 # strip command header character (note it is in lower case)

010            # strip length 16

00007b      # first LED green (0), red (0) and blue (123) colour intensity

007b00      # second LED green (0), red (123) and blue (0) colour intensity

007b7b      # third LED green (0), red (123) and blue (123) colour intensity

7b0000      # fourth LED green (123), red (0) and blue (0) colour intensity

7b007b      # fifth LED green (123), red (0) and blue (123) colour intensity

7b7b00      # sixth LED green (123), red (123) and blue (0) colour intensity

7b7b7b      # seventh LED green (123), red (123) and blue (123) colour intensity

1b0000      # eight LED green (27), red (0) and blue (0) colour intensity

0000ea      # ninth LED green (0), red (0) and blue (234) colour intensity

00ea00      # tenth LED green (0), red (234) and blue (0) colour intensity

00eaea      # eleventh LED green (0), red (234) and blue (234) colour intensity

ea0000      # twelfth LED green (234), red (0) and blue (0) colour intensity

ea00ea      # thirteenth LED green (234), red (0) and blue (234) colour intensity

eaea00      # fourteenth LED green (234), red (234) and blue (0) colour intensity

eaeaea      # fifteenth LED green (234), red (234) and blue (234) colour intensity

005800      # sixteenth LED green (0), red (88) and blue (0) colour intensity

Note that the hex commands use lower case non-digit characters while the decimal commands - use upper case.

It is important to remember that sending a comment as part of the command, starts with a ‘#’ ASCII character and must be terminated with a new line. If the new line after a comment is forgotten, any command characters following will be treated as a continuation of the comment and will not be processed.

More details of the UART binary interface protocol

Further reduction of the number of bytes sent over the UART interface can be achieved by using binary numbers in the commands. This would be typically used when the commands are generated programmatically by a program running on external device or computer.

Using the same hex command as above to set individual LED colours of a strip of 16 LEDs can be programmatically coded as a binary array in “C” language as shown below.

Hex command:

s01000007b007b00007b7b7b00007b007b7b7b007b7b7b1b00000000ea00ea0000eaeaea0000ea00eaeaea00eaeaea005800

Binary array in “C” language:

char set_16_leds_strip_command[ ] =

{

0x1b, // escape character (precedes any non digit character in a command and puts the interface in binary command mode)

's', // strip command header character

0x00, 0x10, // strip length 16

0x00, 0x00, 0x7b, // first LED green (0), red (0) and blue (123) colour intensity

0x00, 0x7b, 0x00, // second LED green (0), red (123) and blue (0) colour intensity

0x00, 0x7b, 0x7b, // third LED green (0), red (123) and blue (123) colour intensity

0x7b, 0x00, 0x00, // fourth LED green (123), red (0) and blue (0) colour intensity

0x7b, 0x00, 0x7b, // fifth LED green (123), red (0) and blue (123) colour intensity

0x7b, 0x7b, 0x00, // sixth LED green (123), red (123) and blue (0) colour intensity

0x7b, 0x7b, 0x7b, // seventh LED green (123), red (123) and blue (123) colour intensity

0x1b, // escape character (precedes any digit with value of 27 (0x1b))

0x1b, // eight LED green (27) colour intensity (because the colour value is 27, it needs the escape character above)

0x00, // eight LED red (0) colour intensity

0x00, // eight LED blue (0) colour intensity

0x00, 0x00, 0xea, // ninth LED green (0), red (0) and blue (234) colour intensity

0x00, 0xea, 0x00, // tenth LED green (0), red (234) and blue (0) colour intensity

0x00, 0xea, 0xea, // eleventh LED green (0), red (234) and blue (234) colour intensity

0xea, 0x00, 0x00, // twelfth LED green (234), red (0) and blue (0) colour intensity

0xea, 0x00, 0xea, // thirteenth LED green (234), red (0) and blue (234) colour intensity

0xea, 0xea, 0x00, // fourteenth LED green (234), red (234) and blue (0) colour intensity

0xea, 0xea, 0xea, // fifteenth LED green (234), red (234) and blue (234) colour intensity

0x00, 0x58, 0x00, // sixteenth LED green (0), red (88) and blue (0) colour intensity

0x1b, // escape character followed by 0 (the following character), turns the interface binary mode off

0x00

};

The ASCII hex command uses 100 ASCII characters (bytes), while the binary equivalent command is using only 55 bytes. 

The binary protocol does not support embedded comment characters like the ASCII ones.

The conversion from ASCII commands to binary commands requires insertion of ESC character (decimal 27, or hex 0x1b) in front of each non digit character of the command. The supported non digit characters used in commands are:

‘S’ (or ‘s’) – strip command header

‘W’ (or ‘w’) – write command header

‘R’ (or ‘r’) – read command header

‘P’ (or ‘p’) – program sequence header

‘L’ – start program sequence in a loop

‘G’ – play the program sequence once and stop the sequence 

The conversion from ASCII commands to binary commands also requires insertion of ESC character (decimal 27, or hex 0x1b) in front of each digit character of value 27 (or hex 0x1b).

The following table specifies the number of characters (bytes) used to encode different command number fields for the decimal, hex and binary protocols:

For the binary protocol, if the number field contains byte of value 27 (0x1b), then an ESC character 27 (0x1b) has to be inserted. 

Each binary command should start with ESC (0x1b) followed by the non-digit command header character. This will switch the binary mode on.

Each binary command should finish with ESC 0 (0x1b, 0x00) to switch the binary mode off.

How to buy the WS2812B LED Interface/Controller chip

The chip is available for sale on eBay. Please search on eBay for:

"Simple WS2812B LED Interface/Controller in one chip"