RGB LEDs, TI TLC5971 driver and PIC18 controlled with Android and HC-05 Bluetooth

Bluetooth controlled, RGB LEDs system with Android application or Python script.

This post is about my solution for controlling RGB LEDs with an LED driver, a microcontroller (PIC18) and a Bluetooth module (HC-05). Finally I control everything with either a Python script or an Android application.

Overview of the system


LEDs Driver – TI TLC5971

Controlling a LED is quite simple: just use a PWM channel and you’re done! But when you have to deal with an RGB LED, you need three PWM channels to do the job, but many microcontrollers don’t have such an availability. Sure there are many software tricks to obtain 3 or more PWM channels, but the these are generally not efficient because they keep the CPU busy for doing almost anything, so it consumes power and has no time to accomplish other operations like listening to serial connections and other! At the end, I decided to use an external driver for controlling the LEDs: this solution is cheaper and simpler than dedicating an entire micro to the LEDs and another for the control logic, also LED drivers have been designed on purpose and obviously are better than any hand crafted solution.

TLC-5971 with the adapter and 1 RGB LED

I chose the TLC5971 from Texas Instrument which has 4 RGB channels (that is, 12 totally) with 16bits DAC resolution and provides a global brightness control. It is quite cheap, well even free if you order the samples on TI website. The PWM resolution is quite high because I think the IC is used also for controlling LED displays, but who cares…?!?! The driver is easy to be controlled as it needs just two pins: clock and data. There is no specific interface built into the IC (I2C, SPI,…), there is just a specification on the packet it expects to receive, and it is actually pretty simple, as it is a sequence of the colors of all the 4 LEDs. If you need to control more than 4 RGB LEDs you can just connect more TLC5971 in cascade. The IC comes in an SMD package (20 HTSSOP .65mm pitch), so I soldered it on a PCB adapter (2$ for 5 PCB on ebay). The absence of a particular interface and the presence of a clock port and a data port is great advantage in flexibility: it may be used anything to control the driver, for example I tested it toggling a pin for the clock signal and streaming data on a GPIO pin. Anyway also SPI or I2C works. I used the second connection scheme proposed in the datasheet, but this depends on your system configuration and supply:

Schematic suggested in the datasheet
Schematic suggested in the datasheet

The important thing on controlling the driver is understanding its messaging protocol: actually only 1 message is allowed. The message is 28 bytes long and is divided as follow:

  • 6 bits dedicated to the start symbol 25h hexadecimal
  • 5 bits for configuration flags
  • 3 x 7 bits each of one controls the global brightness of the R,G and B channels of all LEDs
  • 4 x packets each for one LED, composed of 3 x 16 bits where each color of each LED is controlled with 2 bytes allowing 65536 shades on each channel, the first LED comes last.

The images below (from the datasheet) lack the 6 bits of the write command (0x25). Also notice that the LED3 comes before the LED0 and for each LED, the blue channel comes before the green and red.

TLC-5971 protocol. Function control and global brightness.
TLC-5971 protocol. Function control and global brightness.
TLC-5971 protocol. Channels levels.
TLC-5971 protocol. Channels levels.

Bluetooth – HC05

This project has not a specific application, my intention was to build an RGB light for room lighting or desktop notifications or whatever. Then the Bluetooth connectivity seems to be the best trade-off on price, simplicity, interoperability (any device can use it) and usability (you don’t want to hang a wire from the ceiling to your laptop!). I used the HC-05 module, which seems to be pretty famous due to its low price (something like 5$ on ebay). I bought the user-unfriendly version, which is the one without the 2.54mm header, so I soldered it with the trick you can see in the pictures. To be honest the plug-and-play version is not worth the double cost for just few soldering and resistors, if you have good soldering skills, the rough version is fine, but this is up to you. If want a hint on soldering the raw board, well be aware that the pitch is pretty odd, so at the end I ended up with a prototyping board and some male headers. I used the headers to fix the Bluetooth board on the prototyping one using the same approach of embedding a gem on a jewel.

Back view. Only the GND, 3.3V, TX, RX and KEY pins are needed.


PCB embedded like a gem on a ring.

The module is configurable through AT commands, anyway it should run without any further configuration. Please notice that by default the baudrate is 9600 and the password is 1234.

Microcontroller – PIC18

For this project I used a PIC18F4550, but honestly is highly oversized. This MCU has so many features and computational capability that is really a waste for this application. You can use any microcontroller with the only requirement of providing an USART port for the Bluetooth or more in general to be able to communicate with a Bluetooth module or with another radio module or whatever exotic connection you chose.

The main breadboard featuring the MCU and the BT.

The MCU has two main tasks to accomplish: first of all it listens to the UART for incoming messages and when a message arrives, it colors a LED. I defined a messaging protocol on the purpose, that is, a sequence of bytes containing a color in the RGB form. The message is the following:

  • 1st byte is the Start symbol (0xFE)
  • 6 bytes are contains the color, 2 bytes each for R,G and B channels. Any of these 6 bytes which is 0xFE must be escaped with the Escape byte (0xFD) and also the escape byte itself must be escaped!

The Escape and Start bytes are arbitrary. The following code shows the serial message parser which is called as soon as a new byte is received on the UART:

#define ST_CHAR 0xFE
#define ESC_CHAR 0xFD
#define MSG_LEN 6

unsigned char rxCounter = 0, escaped = 0, in_msg = 0, rxBuffer[MSG_LEN];

void parseSerialMsg(void) {
    static unsigned char byte, status;

    if (DataRdyUSART()) {
        byte = getcUSART();

        if ( (!in_msg) && (byte != ST_CHAR) ) {
            status = 'n';
            in_msg = 0;

        } else if ( byte == ST_CHAR ) {

            if (escaped) {
                status = 'd';

                rxBuffer[rxCounter++] = byte;
                escaped = 0;
            } else {
                status = 's';

                rxCounter = 0;
                in_msg = 1;
                escaped = 0;
        } else if ( byte == ESC_CHAR ) {
            if (escaped) {
                status = 'd';

                rxBuffer[rxCounter++] = byte;
                escaped = 0;
            } else {
                status = 'e';

                escaped = 1;

        } else {
            status = 'd';

            rxBuffer[rxCounter++] = byte;
            escaped = 0;


        if ( rxCounter == MSG_LEN ) {
            status = 'k';

            rxCounter = 0;
            escaped = 0;
            in_msg = 0;


The paintLED function that is called from the message parser when a full message is found, controls the LED driver (RB0 is the data line, RB1 is the clock):

void paintLED(char* colors) {
    static unsigned char i,j;

    // Set colors of LED0
    for (i=0; i<6; i++) {
        ledDriverMsg[27-i] = colors[i];

    // Stream data
    PORTBbits.RB1 = 1;
    for(i=0; i<28; i++) {
        for(j=8; j>0; j--){
            PORTBbits.RB1 = 0;
            PORTBbits.RB0 = ( ledDriverMsg[i]>>(j-1) ) & 0x01;
            PORTBbits.RB1 = 1;
    PORTBbits.RB0 = 0;
    PORTBbits.RB1 = 0;

Please notice that the above function serves only the first LED (LED0), while the other are off. What is left, is just the interrupt routine called every time a byte is received in the UART RX buffer:

void highISR(void);

#pragma code HIGH_INTERRUPT_VECTOR = 0x8
/* High-priority service */
void high_ISR(void) {
        goto highISR
#pragma code

#pragma interrupt highISR
/* High-priority service */
void highISR(void) {

    if(PIR1bits.RCIF) {
        PIE1bits.RCIE = 0;

        // Real function to execute

        // Clear Interrupt Flag
        PIR1bits.RCIF = 0;
        PIE1bits.RCIE = 1;

The code for the MCU is easily portable to any other MCU! You can download it here [gview file=”http://tesladocet.com/wp-content/uploads/rgbled-pic-firmware.zip”]

Client side – Python

Controlling the Bluetooth from a PC is pretty trivial: in Python it requires a couple of lines to do that.

import serial
ser = Serial.serial('/dev/rfcomm0', 9600) # open the serial port
ser.write('\xFE\xFF\xFF\x00\x00\x00\x00') # turns the LED red

Please notice that you need to connect to the Bluetooth module from your PC and to open a serial port. Sometimes the operative system just establishes a link, but does not create the COM. For example in Arch Linux I use rfcomm to open the COM:

hcitool scan
# obtain some MACs
rfcomm connect bt_mac_here


The Android app is conceptually simple as it just reads the RGB values from 3 sliders or the values of predefined colors and then sends a message via Bluetooth. Anyway the Bluetooth management is not so simple, as you need to handle disconnections, re-connections etc…, but fortunately the Android documentation is rich and there is also a good sample app that implements a Bluetooth chat with a lot of examples. Finally I built the RGB control app modifying the original Bluetooth chat app.

RGB sliders and dropdown list. The application also shows the messages for debug purpose

The modified app just brings 3 sliders and a dropdown menu on the GUI side. Then I modified the send function adding the creation of a message with a start byte (0xFE), 6 bytes from the values of the sliders and eventually some escape characters. You can find the code of the Android application in my GitHub repository


In this video you can appreciate the crappy quality of my webcam and if you are able also the android app in action


Here the firmware for the PIC

[gview file=”http://tesladocet.com/wp-content/uploads/rgbled-pic-firmware.zip”]

Published by


Studying Electronic Engineering (M.Sc), home brewing, playing with electronics, computer science and guitars

2 thoughts on “RGB LEDs, TI TLC5971 driver and PIC18 controlled with Android and HC-05 Bluetooth”

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by sweet Captcha