RGB LED wireless light

Here is a post about what you can do in an afternoon or two with a whole load of electronics stuff lying around.

I have had an red-green-blue (RGB) board from Big Clive or Phenoptix lying around for a while. I had developed an RGB LED controller for it, so I also had one of those attached to it.

It is designed to fit in to those 150W halogen light holders and I had managed to find one of those as well.

I wanted to do something fun with it so decided to make a wirelessly controlled LED lamp which can be used to change the room ambiance, or to signify some form of data (anything from room temperature to number of emails to read).

Demonstration

Here is the completed lamp and a short demo video:

Concept

The light contains an RGB LED board which is controlled using serial data from a microcontroller.

The microcontroller will read command sent to it via its serial lines and act on those accordingly.

The serial data is sent wirelessly using an XRF radio UART serial module. Any data sent to the device in the correct format will cause it to change colour.

Data will be sent using another XRF module from elsewhere (the XRF has a great range).

Parts required

If you would like to follow this build then you will need:

There are loads of different options for all of these parts, this was just what I had lying around.

Hardware

I wired two of the Minimus outputs (PD0 and PD1) to the Serial Data and Serial Clock lines of the RGB LED controller. The RGB controller was wired up to the 12V input and contains an on-board 5V regulator, which powers all the logic level equipment.

The XRF was wired to the Rx and Tx lines of the Minimus (PD2 and PD3). The wires were swapped over, so the XRF Tx connected to the Rx of the Minimus and vice versa. This is due to the flow of data from the XRF to the Minimus.

The XRF is a 3V3 device so I also needed a 3V3 converter board, the active version of this break out board.

A push switch was also wired to a digital input on the Minimus (on PD4). This has not bee used for anything yet, but the idea is that it will be used to locally switch the light output on and off.

Here are some photos of the guts of the it:

alt

All the parts before fitting into the halogen floodlight base.

alt

The minimus AVR.

alt

The RGB LED controller.

alt

The XRF and XBBO 3v3 breakout board.

alt

The RGB LED floodlight board.

alt

The empty halogen floodlight holder. This has holes for the 12V DC power, the push switch and the aerial of the XRF.

alt

alt

The RGB board was screwed into place with some 3mm machine screws and a couple of tapped holes I drilled.

alt

The finished, closed unit with glass front and power.

Software

The software was written using the Arduino IDE. It was uploaded to the Minimus AVR using the flow mentioned in this previous blog post. The code is available to download here or is written out here. Hopefully the comments help with the code flow. It uses the substring command which (as noted here) might be too memory intensive, which could cause an issue (but this code works as it is).

/********************************************************
/****** Colour Change using RGB shield with Minimus ****
/****** by Matt Little **********************************
/****** Date: 18/3/14 ***********************************
/****** info@re-innovation.co.uk ************************
/****** www.re-innovation.co.uk *************************
/********************************************************

This will read serial commands via a software serial.
It will update the colour of the LED board depending upon those commands.

Data is sent to the board serially in the format aXXXXXXX
Where XXXXXXX is an integer which relates to the hex FFFFFF
FF0000 hex = 16711680 int = RED ON
00FF00 hex = 65280 int = GREEN ON
0000FF hex = 255 int = BLUE ON

This can be created from the RGB (0-255) values with
R  x 65280
G x 255
B x 1

This will be read by an XRF or wireless device from a sending unit.

The minimus pin outputs are:
Minimus:    Arduino Equivalent:  Connection to:
PC2          20                   
PD0          0                    SDI
PD1          1                    CKI
PD2          2                    Rx
PD3          3                    Tx
PD4          4                    SWITCH 1
PD5          5
PD6          6
PD7          7
PB0          8  
PB1          9
GND

PB2          10
PB3          11
PB4          12                  LED 1
PB5          13                  LED 2
PB6          14                  LED 3
PB7          15
PC7          16                  
PC6          17
RESET    
PC5          18
PC4          19
Vcc



see www.re-innovation.co.uk for more details
Modified:
18/3/14  Initial code written - Matt Little

*/

#include <stdlib.h>
#include <string.h>

// This is for the serial shifted output data
const int SDI = 0; //PD0 of the Minimus is used as the SDI (Serial Data)
const int CKI = 1; //PD1 of the Minimus is used as the CKI (Data clock)

const int led = 6;      //LED of Minimus
const int led1 = 12;    //LED 
const int led2 = 13;    //LED
const int led3 = 14;    //LED
const int sw1 = 16;     // Input switch

// This line defines a "Uart" object to access the serial port
HardwareSerial Uart = HardwareSerial();

// Variables for the RGB shield
#define STRIP_LENGTH 1 //This is to use more than one RGB LED board. In this case only one board connected
long strip_colors[STRIP_LENGTH];

long fade = 0x000000;  // A long integer for the fade function
long fade_change = 0x000000;  // A long int to do bitwise shifting on the fade value

long colour = 0x000000; // This holds the colour to display

boolean dataAvailable=LOW;  // Flag to show data is available

// ****** Serial Data Read***********
// Variables for the serial data read
char inByte;         // incoming serial char
String str_buffer = "";  // This is the holder for the string which we will display
#define MAX_STRING 20      // Sets the maximum length of string probably could be lower
char stringBuffer[MAX_STRING];  // A buffer to hold the string when pulled from program memory

void setup()
{ 
  Uart.begin(9600); // Set up a serial output for data display and changing parameters
  //set pins to output so you can control the shift register
  pinMode(SDI, OUTPUT);
  pinMode(CKI, OUTPUT);  
  pinMode(led1, OUTPUT); 
  pinMode(led2, OUTPUT); 
  pinMode(led3, OUTPUT); 
  pinMode(sw1, INPUT);       
  
  digitalWrite(led1, LOW);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW); 
 
  strip_colors[0] = 0xFF0000; //Set the data to Bright Red
  post_frame(); //Push the current color frame to the strip 
  delay(200); 
  strip_colors[0] = 0x00FF00; //Set the data to Bright Red
  post_frame(); //Push the current color frame to the strip 
  delay(200);
  strip_colors[0] = 0x0000FF; //Set the data to Bright Red
  post_frame(); //Push the current color frame to the strip 
  delay(200);
  strip_colors[0] = 0x000000; //Set the data to Bright Red
  post_frame(); //Push the current color frame to the strip  
}

void loop()
{
  
  getData();  // Check the serial port for data
  if(dataAvailable==HIGH)
  {
      strip_colors[0] = colour; //Set the data to Bright Red
      post_frame(); //Push the current color frame to the strip 
  }    
  delay(50);  // Just slow everything down
}

// **********************GET DATA SUBROUTINE*****************************************
// This sub-routine picks up and serial string sent to the device and sorts out a power string if there is one
// All values are global, hence nothing is sent/returned

void getData()
{
  // **********GET DATA*******************************************
  // We want to find the bit of interesting data in the serial data stream
  // As mentioned above, we are using LLAP for the data.
  // All the data arrives as serial commands via the serial interface.
  // All data is in format aXXDDDDDDDDD where XX is the device ID
  if (Uart.available() > 0) 
  {
    
    dataAvailable=HIGH;  // Tell the rest of the program there is data available
      
    inByte = Uart.read(); // Read whatever is happening on the serial port
  
    if(inByte=='a')    // If we see an 'a' then read in the next 11 chars into a buffer.
    {   
      str_buffer+=inByte;
      for(int i = 0; i<11;i++)  // Read in the next 11 chars - this is the data
      {
        inByte = Uart.read(); 
        str_buffer+=inByte;
      }
      //Uart.println(str_buffer);  // TEST - print the str_buffer data (if it has arrived)
      sortData();
      
      Uart.print("OK");  // Receive info back to say its updated
      
      str_buffer="";  // Reset the buffer to be filled again
    }
  }
  else
  {
    dataAvailable=LOW;  // Tell the rest of the program there is data available
  }
}

// **********************SORT DATA SUBROUTINE*****************************************
// This sub-routine takes the read-in data string (12 char, starting with a) and does what is required with it
// The str-buffer is global so we do not need to send it to the routine

void sortData()
{ 
  // We first want to check if the device ID matches.
  // If it does not then we disregard the command (as it was not meant for this device      
  if(str_buffer.substring(1,4)=="OFF")
  {
    colour = 0;
  }
  else
  {
    String colourString = str_buffer.substring(1,9);
    colour = colourString.toInt();
  }
}

//Takes the current strip color array and pushes it out
void post_frame (void) {
  //Each LED requires 24 bits of data
  //MSB: R7, R6, R5..., G7, G6..., B7, B6... B0 
  //Once the 24 bits have been delivered, the IC immediately relays these bits to its neighbor
  //Pulling the clock low for 500us or more causes the IC to post the data.

  for(int LED_number = 0 ; LED_number < STRIP_LENGTH ; LED_number++) {
    long this_led_color = strip_colors[LED_number]; //24 bits of color data

    for(byte color_bit = 23 ; color_bit != 255 ; color_bit--) {
      //Feed color bit 23 first (red data MSB)
      
      digitalWrite(CKI, LOW); //Only change data when clock is low
      
      long mask = 1L << color_bit;
      //The 1'L' forces the 1 to start as a 32 bit number, otherwise it defaults to 16-bit.
      
      if(this_led_color & mask) 
        digitalWrite(SDI, HIGH);
      else
        digitalWrite(SDI, LOW);
  
      digitalWrite(CKI, HIGH); //Data is latched when clock goes high
    }
  }

  //Pull clock low to put strip into reset/post mode
  digitalWrite(CKI, LOW);
  delayMicroseconds(500); //Wait for 500us to go into reset
}

Further work

  • Frosting for the glass to diffuse the LED output (Thanks for giving me an off-cut, Russell)
  • Get the push-switch to switch between on and off
  • Control via a Slice of Radio Raspberry Pi add-on board.

Leave a Reply

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