The latest generation of addressable RGB LEDs opens a whole world of possibilities for projects. This project will guide you through creating an Arduino controlled mood light that will allow you switch colors, brightness, and even switch to a color craw effect reminiscent of the beloved lava-lamp of ages past.
In this tutorial I’ll show you how to build a three-tier LED lamp, use pin change interrupts on the the Arduino platform, and store power off persistent values in the Arduino's memory.
Gather the Components, Tools, and Libraries
- 12 LED Neopixel
- 16 LED Neopixel
- 24 LED Neopixel
- Official Arduino Nano (or clone if you are minding the budget)
- 7V-2A Power Supply (Neopixels can use a lot of juice and the Nano ~ 7V)
- 10K Ohm resistors (1/4 watt) Brown-Red-Orange (keep a set of resistors in your kit)
- Breadboard-friendly 2.1mm DC barrel jack
- Breadboard 1/4 size
- 22 Gauge Hookup Wire (multiple colors) (A whole box like this is handy for many projects)
- 20 Gauge Hookup Wire (black) for the riser posts
- Breadboard friendly on/off switch
- Breadboard friendly momentary switches (4 from the 20 pack)
- LRF Support (Little Rubber Feet)
- Soldering Iron
- Wire stripper that can strip 22 gauge wire
- Computer with Arduino IDE for programming Nano
- Arduino IDE (1.0.5 or later)
- USB A-Male to Mini-B Cable (for programming Nano)
Download the following Aduino libraries, start the Arduino IDE and install each with the Sketch > Import Library > Add Library menu.
- PinChangeInterrupt Library to detect pin change events
- Adafruit Neopixel Library to control the Neopixel rings
With this method you no longer need to manually find the Library directory and unzip. The IDE will take care of this step for you. After importing the libraries, exit the Arduino IDE.
The instructions below will show you how to assemble the lamp and circuit onto the circuit board and the programming of the Arduino. The function of the lamp is fairly simple: each button will trigger an interrupt in the Arduino which, in turn, will trigger a behavior change.
The buttons from right to left (because I'm difficult that way) are: on/off toggle, color preset mode, brightness preset level, effect mode. When the lamp is toggled off, the LED rings power off and the current settings are written into permanent memory. When the lamp powers on, it reads the settings and is ready to start back up where you left off.
Assembling the LED Tower
You will use the three LED rings to construct an LED tower that is the lamp part of the lamp.
- Cut nine 3-inch lengths of the black 20 gauge hookup wire
- Strip each wire 1/4" on each end
- Solder a black wire into the GND, Data Input, and PWR +5 holes on the 24-pixel ring from the bottom so the wires extend below the ring and the pixels aim up from the ring. Try to evenly space the wires as these will form the base riser for the lamp
- Solder a black wire into the open GND, Data Out, and PWR +5 holes on top of the ring, facing upwards in the same direction as the pixels. You should now have the 24-pixel ring with three wires pointing down from the bottom, and three pointing up in the same direction as the pixels
- Solder the three black wires from the 24-pixel ring to the bottom of the 16-pixel ring. The wires should connect the Data Out (from the 24-pixel ring) to the Data Input (on the 16-pixel ring), the GND (from the 24 ring) to the Ground (on the 16), and the PWR +5 (from the 24) to Power 5V DC (on the 16). You will now have a two-tiered pixel tower
- Solder three black wires onto the bottom of the 12-pixel ring in the Data Out, 5V DC Power, and GND holes so the wires stick out of the side away from the LED pixels
- Solder the Data Input wire from the bottom of the 12-pixel ring into the to the Data OUT hole on the top of the 16-pixel ring
- Tin the two remaining wires from the 12-pixel ring by adding a touch of solder to each one
- Solder the 5V DC Power wire from the 12-pixel ring to the top of the wire poking through the Power 5V DC hole on the 16-pixel ring. If you stripped a full 1/4" of insulation off the ends of the wires, there should be enough stripped wire poking through the top of the 16-pixel ring to solder connect the two wires
- Solder the remaining loose wire from the 12-pixel ring's GND hole to the top of the wire poking through the GND hole on the 16-pixel ring
At the end of this process double check that the ground wire runs from the very bottom to the top of the LED ring tower through all the ground-ish named holes and that the power does the same for the power-ish named holes.
There is a little variance in the silk screen naming convention on the rings, but you get the idea. Check that the data out wire from each ring goes into the data in hole from the next ring. This will create a singular strip of addressable lights as far as the the Neopixel library is concerned.
As hard as I tried to keep the wires even, I discovered that I had to bend them a little after soldering to level the rings on the tower. You may have to do the same. It should look something like the following image.
Assemble the Breadboard Circuit
Use the diagram image below to wire the circuitry. Each of the components should fit easily into the board and you may wish to custom cut the 22-gauge hookup wire for routing on the breadboard for a more finished fit. A few things to pay notice about the circuit.
- The on/off switch interrupts the unregulated 7V power from the power supply that feeds the LED rings and the Arduino. It is highly recommended that this switch be set to off before connecting or removing the power supply. This will protect the rings from power spikes from even high quality power supplies.
- The momentary push buttons connect a regulated 5V bus from the voltage regulator on the Arduino to the pins that will detect the button press. The 10K Ohm resistors keep the pins LOW until there button is pressed.
Once the board is assembled position the LED tower onto the board so the ground wire connects to a ground bus, the power wire to the unregulated power bus (the top red power bus in the diagram), and the signal wire to the D10 pin on the Arduino. Finally apply four little rubber feet to the bottom of the breadboard to prevent it from slipping around.
Program the Arduino
The hardware is now constructed and the next step is to program it. Download the zip packaged with this tutorial or see the github repository for the light.
- Open nightlight.ino in the Arduino IDE
- Connect the Arduino Nano to the computer with the USB-to-USB mini cable
- Ensure the serial port is set to the port the computer detected when you connected the Arduino using the Tools > Serial Port menu
- Ensure the board is set to Arduino Nano w/ ATmega328 using the Tools > Board menu
- Verify that the libraries are installed properly by clicking the Verify button and ensure there are no errors
- Use the Upload button to push the code to the Arduino
- Open the Serial Monitor window by pressing Control-Shift-M and make sure it is set to 19200 baud. You should see the Arduino initialize with output like the example below
RGB Mood light V1.0: Ready Reading from EEPROM: State: 1 Bright preset: 1 Color preset: 1 Effect Level: 9 State: 1 Setup complete. In off mode. slewdelay=20 climbdelay=200 In Function Initializing Effect with preset: 1
- Ensure the on/off switch is in the off position
- Attach the 7V power supply to the barrel connector
- Turn the switch on
- Try out the buttons and to see that they respond as described above, left to right: on/off toggle, color preset mode, brightness preset level, effect mode. The effect mode starts a slow climb of pixels from the bottom ring that slowly change color to the next preset. Once all of the 52 pixels have changed to the next color, the color change process will start again. Pressing the Effect button repeatedly will change the speed of the effect.
Notes About the Program
The Arduino has a certain amount of EEPROM that can be written to and read from which will retain values across power off of the Arduino. The lamp program includes the EEPROM.h file that exposes the functions EEPROM.read() and EEPROM.write() that read and write bytes into the EEPROM memory space.
As the programmer, you are responsible for knowing what address to read and write and the included code does this by using #define to specify the address a specific setting will hold on write and read.
#define STATEADDR 1 #define BRIGHTADDR 2 #define COLORADDR 3 #define EFFECTADDR 4
//read the variables from EPROM and setup system state=EEPROM.read(STATEADDR); currentBrightLevel=EEPROM.read(BRIGHTADDR); currentColorPreset=EEPROM.read(COLORADDR); currentEffectLevel=EEPROM.read(EFFECTADDR);
The EEPROM memory has a limit to how many times it can be written before it degrades, but that number is 10,000 range so there is little worry about wearing out your EEPROM with this application in the next few decades. Just note that it is not a good idea to be writing to the EEPROM every second or so in an application.
The program uses interrupts to detect when the buttons are pressed instead of polling for when the pin is high. In the following code from the setup() routing, the function fobHandler() is assigned to be called when any of the four buttons are pressed.
//setup the interrupt on each of the pins PCintPort::attachInterrupt(FOBPINA, &fobHandler, RISING); PCintPort::attachInterrupt(FOBPINB, &fobHandler, RISING); PCintPort::attachInterrupt(FOBPINC, &fobHandler, RISING); PCintPort::attachInterrupt(FOBPIND, &fobHandler, RISING);
The interrupt method provides a much more accurate detection of the button press. The Arduino has only two built in generic hardware interrupts but it also support a PinChangeInterrupt on each pin.
The Atmel chip can detect change, rising, or falling modes on each pin. When the interrupt is triggered the function specified earlier in the program fobHandler() interrupts the executing code and takes over. When the interrupt handler completes the execution returns to the program where it left off. To read more about Pin Change Interrupts see the project page on the subject.
The program uses an interrupt to detect which pin has been changed and the function fobHandler() sets the global variable latest_interrupted_pin to the number of the pin that changed. The chip is set to detect a RISING voltage change, meaning it will trigger as the button is pushed rather than when it is released.
The main loop of the program checks for a change of the latest_interrupted_pin variable and decides to what to do. The heavy lifting of what to do when the button is pressed is left to the main loop() because executing certain functions inside the interrupt function can cause things to get wonky such as Serial.print(), millis(), delay(), and anything requiring the additional use of interrupts.
In this program the interrupt handling routing simply changes a variable and lets the main loop do the complicated work to avoid wonkiness.
You have now constructed a novel mood lamp powered by an Arduino. In the basic configuration you are able to change the color, brightness, and effect of the lamp with the press of a button.
I’ve shown you how to chain Neopixel units together while creating a single addressable strip. During this build you also learned about using pin change interrupts to read hardware changes and how to store data in the permanent memory of your Arduino to create settings that persist across power outages.
There are a lot of places to take this project if you want to hack it to be something beyond the novelty bare wire electro-themed light sculpture it is today. Enhancements you might try include adding more color presets, a different effect when the lamp is in effect mode, or even a shade made of translucent velum.