Note: The products sold in this article have been updated and are not fully applicable to this tutorial.
With all the lights and decorations that people use at Christmas, power is left on all the time and electricity bills skyrocket. I made this motion detecting Christmas tree light setup that only turns on when people are nearby. It also plays music using one of our new products – the DFSpeaker v1.0!
The LED strip has different patterns appear at random and also includes a relay-fitted power outlet so that you can use your regular tree lights along with it.
The LED patterns are selected at random by the microcontroller. Each pattern is assigned a number, and the microcontroller uses a random number generator to select which pattern to use.
When the lights are triggered, a Christmas carol plays through the speaker.
We’ll start with the microcontroller board.
Take the I/O extension and mount it on top of the microcontroller.
This gives you expanded input and outputs so you can use more sensors and actuators.
Connect the PIR sensor
Next, add the PIR sensor. This sensor will detect motion and report the information to the microcontroller.
The PIR sensor connects to digital pin 2 on the board. The sensor’s wiring loom has 3 wires – red, black and blue/green.
The pin connectors on the expansion shield have corresponding colors. Red is vcc, black is ground (GND) and green is digital signal.
Always check that you match the corresponding colors when connecting things, otherwise the sensor wont work!
Connect the LED strip
The LED strip is attached to digital pin 6. In this case, we will only connect the digital signal. Why? Because the LED strip needs 3 amps of current to function properly which is more than this microcontroller can provide. To fix this we will use an external power supply to power the strip. The power supply we need is 5v@3a. If you have purchased an LED strip from DFRobot.com, it will have come provided.
If you’re clever you can wire it so that the microcontroller is powered from this power supply as well. The spare power and ground connections can plug in to the “PWR_IN” terminals on your expansion shield. Red is +, black is -, be very careful not to confuse this!
Unfortunately any other Christmas lights you use must use regular AC electricity.
Connect the MP3 module and Speaker
This part is a little more complicated, but don’t be too intimidated!
The mp3 module is connected to RX and TX on the microcontroller with 1k resistors in series. You will also need to provide power by connecting the module to 5v and GND on the microcontroller.
In order to put the resistors in series, I simply took two F-F jumper wires, cut them in half and then soldered a resistor in the middle of each. These resistors are pull up resistors and help with transmitting data.
If you’re not good with soldering, you could use some jumpers and a breadboard for the resistors.
In order to prepare your mp3 files, take a micro SD card, make a folder named “mp3” at the root directory of your card, and then rename each using the following structure:
“0001
“0002 I only used one track for mine, so it was one file in an mp3 subfolder titled: “0001.mp3”. The reason this is done is because the player needs this information to play the file. More information can be found on the wiki page for this item. The speaker itself is connected to the mp3 module for the signal connection. It also requires power, so connect it to 5v and GND connections on the microcontroller. For your reference, here are the pinouts of the MP3 module: VCC connects to 5v on the microcontroller RX connects to RX on the microcontroller (with a resistor in series) TX connects to TX on the microcontroller (with a resistor in series) SPK_1 connects to the signal wire of the speaker GND connects to GND on the microcontroller You can adjust the volume of the speaker by adjusting the potentiometer using a small phillips screwdriver. Connect the Relay The relay switch is attached to pin 7. As before, be careful that the power, ground and signal connections are the right way around. Relay Controller Power Strip
The following are steps to make a relay controlled power strip. This has many applications outside of this project and is worth doing, but you must remember to be careful when working with AC electricity – it is very dangerous, don’t be stupid! ALWAYS check, double check and triple check that power is disconnected before continuing. Find a power strip outlet with a built in power switch. Open up the power strip and disconnect/desolder the switch, leaving the switch wires intact. (Keep the switch, they can be useful for other projects!) Using the wires that were desoldered from the switch, connect one to the COM terminal of the relay switch and the other to the NO (normally open) terminal. With these connections, when the relay is activated via the microcontroller, the switch will close, the outlet will power on, and anything that is plugged in to it will have power (in this case, old Christmas lights). Here is an image of my finished relay controlled power strip. Code
#include
#include
#include
#define PIN 6 //led strip signal pin
Adafruit_NeoPixel strip = Adafruit_NeoPixel(180, PIN, NEO_GRB + NEO_KHZ800);
long randNumber = 0L;
const int relayPin = 7;
const int sensorPin = 2;
int sensorState = 0;
#define wipeSpeed 10
//PROTOTYPES=========================================================================
void choose();
void wipeClear();
void stateChange();
void wipeRed();
void wipeBlue();
void wipeGreen();
void wipeYellow();
void wipeWhite();
void theatreWhite();
void theatreRed();
void theatreBlue();
void theatreGreen();
//SETUP==============================================================================
void setup() {
pinMode(sensorPin, INPUT);
pinMode(relayPin, OUTPUT);
Serial.begin(9600);
mp3_set_serial (Serial); //set Serial for DFPlayer-mini mp3 module
mp3_set_volume (100); // value 0~30
//set all pixels to off
strip.begin();
strip.show(); // Initialize all pixels to 'off'
randomSeed(analogRead(0));
}
//LOOP===============================================================================
void loop() {
randNumber = random(1, 10);
sensorState = digitalRead(sensorPin);
if (sensorState == HIGH) {
// Serial.println("presence!");
digitalWrite(relayPin, HIGH);
mp3_play (1);
choose();
//delay (6000);
} else {
// Serial.println("no presence!");
wipeClear();
mp3_stop ();
digitalWrite(relayPin, LOW);
}
}
//LIGHT PATTERNS=============================================================================
//colorwipe
void colorWipe(uint32_t c, uint8_t wait) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void wipeRed() {
colorWipe(strip.Color(255, 0, 0), wipeSpeed);
}
void wipeGreen() {
colorWipe(strip.Color(0, 255, 0), wipeSpeed);
}
void wipeBlue() {
colorWipe(strip.Color(0, 0, 255), wipeSpeed);
}
void wipeYellow() {
colorWipe(strip.Color(255, 255, 0), wipeSpeed);
}
void wipeWhite() {
colorWipe(strip.Color(127, 127, 127), wipeSpeed);
}
void wipeClear() {
colorWipe(strip.Color(0, 0, 0), wipeSpeed);
}
//theatre chase
void theaterChase(uint32_t c, uint8_t wait) {
for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
for (int q = 0; q < 3; q++) {
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
}
void theatreWhite() {
theaterChase(strip.Color(127, 127, 127), 1000);
}
void theatreRed() {
theaterChase(strip.Color(255, 0, 0), 1000);
}
void theatreBlue() {
theaterChase(strip.Color(0, 0, 255), 1000);
}
void theatreGreen() {
theaterChase(strip.Color(255, 255, 0), 1000);
}
//CHOICE=============================================================================
void choose() {
if (randNumber == 1) {
wipeRed();
}
else if (randNumber == 2) {
wipeGreen();
}
else if (randNumber == 3) {
wipeBlue();
}
else if (randNumber == 4) {
wipeYellow();
}
else if (randNumber == 5) {
wipeWhite();
}
else if (randNumber == 6) {
theatreWhite();
}
else if (randNumber == 7) {
theatreRed();
}
else if (randNumber == 8) {
theatreGreen();
}
else if (randNumber == 9) {
theatreBlue();
}
//END================================================================================
Code Analysis
#include
#include
#include
#define PIN 6 //led strip signal pin
Adafruit_NeoPixel strip = Adafruit_NeoPixel(180, PIN, NEO_GRB + NEO_KHZ800);
First libraries are initialized and pins are declared.
The DFPlayer mini library is necessary if you want to use the mp3 module.
Software serial is also used for this module.
I have used Adafruit’s excellent Neopixel library to control the LED strip. This library can be found on the Adafruit website. It takes care of a lot of the more intimidating code so that you can focus on the more fun elements of the LED strip, i.e. lighting it up and making light sequences.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(180, PIN, NEO_GRB + NEO_KHZ800);
This line defines parts of the LED strip. 180 is the amount of LEDs in the strip. 800KHZ is the frequency of the strip.
long randNumber = 0L;
This line definies the random number datatype.
const int relayPin = 7;
This defines the relay as digital pin 7
const int sensorPin = 2;
This defines the sensor as digital pin 2
int sensorState = 0;
This sets the sensor to 0 every time the program runs
#define wipeSpeed 10
This part of the code relates to lines in the code later on. It is a quick way that I can use to change multiple values. Where “wipeSpeed” is declared later in the code in place of a value, it will substitute the value of 10 (or whatever value I decide to put there). This saves me having to change each value individually.
The random number function is declared also.
The initial sensor state is set as 0
The led strip parameters are declared – the strip I used had 180 leds in total so this is listed here.
I coded this so that you can alter the wipe speed of the LED lights by editing one value, rather than changing each line of code manually. I set it to 10. The smaller the number, the quicker the led strip will wipe.
//PROTOTYPES========================================================
void choose();
void wipeClear();
void stateChange();
void wipeRed();
void wipeBlue();
void wipeGreen();
void wipeYellow();
void wipeWhite();
void theatreWhite();
void theatreRed();
void theatreBlue();
void theatreGreen();
I used these to help me compile the code when it was in the process of being written. It includes every object created in the program and helps me to avoid annoying “out of scope” compilation errors.
//SETUP==============================================================================
void setup() {
pinMode(sensorPin, INPUT);
pinMode(relayPin, OUTPUT);
Serial.begin(9600);
mp3_set_serial (Serial); //set Serial for DFPlayer-mini mp3 module
mp3_set_volume (100); // value 0~30
//set all pixels to off
strip.begin();
strip.show(); // Initialize all pixels to 'off'
randomSeed(analogRead(0));
}
The setup function runs once when the program is initialized.
pinMode(sensorPin, INPUT);
pinMode(relayPin, OUTPUT);
Serial.begin(9600);
mp3_set_serial (Serial); //set Serial for DFPlayer-mini mp3 module
The pins modes are declared for the PIR sensor and the relay switch.
The PIR sensor (sensorPin) is an input, as it gives data to the microcontroller.
The relay switch (relayPin) is an output as it is taking information from the microcontroller and using it to perform a function.
Serial communication is initialized – this is useful for debugging the code as you can use the serial print function to output data to your pc.
The mp3 module also needs serial communication to operate.
mp3_set_volume (100); // value 0~30
//set all pixels to off
strip.begin();
strip.show(); // Initialize all pixels to 'off'
randomSeed(analogRead(0));
}
The mp3 module’s volume output is set. The maximum value you can have is 30. (I went crazy and tried to see if 100 would work, unfortunately 30 is the maximum value).
The strip.begin(); and strip.show(); functions
call functions from the Adafruit Neopixel library that turns off all the LEDs in the LED strip.
randomSeed starts the random number generator at a random point within the value range.
//LOOP===============================================================================
void loop() {
randNumber = random(1, 10);
sensorState = digitalRead(sensorPin);
if (sensorState == HIGH) {
// Serial.println("presence!");
digitalWrite(relayPin, HIGH);
mp3_play (1);
choose();
} else {
// Serial.println("no presence!");
wipeClear();
mp3_stop ();
digitalWrite(relayPin, LOW);
}
}
The loop part of the program runs forever (or until the microcontroller is powered off or a component fails).
randNumber = random(1, 10);
This line declares the random number generator function (randNumber). 1 is the lowest number, and 9 is the highest. For random numbers, it will never hit the upper bound, it will always remain 1 below it.
sensorState = digitalRead(sensorPin);
The sensor state polls the sensor.
if (sensorState == HIGH) {
// Serial.println("presence!");
digitalWrite(relayPin, HIGH);
mp3_play (1);
choose();
} else {
// Serial.println("no presence!");
wipeClear();
mp3_stop ();
digitalWrite(relayPin, LOW);
}
}
This starts a condition. If the sensor senses motion, a subroutine is activated which will activate the relay pin, play the first track on the mp3 module and then activate the random number chooser, choose a light pattern, turn on the led strip.
If no motion is detected, the led strip will wipe clear, the music will stop and the relay switch will turn off.
//LIGHT PATTERNS=============================================================================
//colorwipe
void colorWipe(uint32_t c, uint8_t wait) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void wipeRed() {
colorWipe(strip.Color(255, 0, 0), wipeSpeed);
}
void wipeGreen() {
colorWipe(strip.Color(0, 255, 0), wipeSpeed);
}
void wipeBlue() {
colorWipe(strip.Color(0, 0, 255), wipeSpeed);
}
void wipeYellow() {
colorWipe(strip.Color(255, 255, 0), wipeSpeed);
}
void wipeWhite() {
colorWipe(strip.Color(127, 127, 127), wipeSpeed);
}
void wipeClear() {
colorWipe(strip.Color(0, 0, 0), wipeSpeed);
}
//theatre chase
void theaterChase(uint32_t c, uint8_t wait) {
for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
for (int q = 0; q < 3; q++) {
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
}
void theatreWhite() {
theaterChase(strip.Color(127, 127, 127), 1000);
}
void theatreRed() {
theaterChase(strip.Color(255, 0, 0), 1000);
}
void theatreBlue() {
theaterChase(strip.Color(0, 0, 255), 1000);
}
void theatreGreen() {
theaterChase(strip.Color(255, 255, 0), 1000);
}
Here some code objects for the light patterns. It is easier to call a code object rather than type out long code instructions every time, so I did that here.
Let’s take a close look at one:
void wipeBlue() {
colorWipe(strip.Color(0, 0, 255), wipeSpeed);
}
wipeBlue is the name of the function. It is named this because it uses a wiping lighting sequence, and the color is blue. The three values at the end represent RGB values. The highest value you can use is 255 and the lowest is 0.
By changing these values, you can alter the colors of the lights. To make blue light, you use 0 red, 0 green and 255 blue.
If you remember, at the beginning of the code I had this line:
#define wipeSpeed = 10;
If you examine the different sequences I have, they all have this in place of a value. This will insert the number 10 as this value, saving me having to change each individually. Of course, you could also change these values to have different values, but I quite like consistency.
In this section of code I have used two main sequences for the lights: wiping (lighting up 1 led after another) and theatre chasing (lighting up LEDs in the style of theatre lights). If I had time, I could have added more.
//CHOICE=============================================================================
void choose() {
if (randNumber == 1) {
wipeRed();
}
else if (randNumber == 2) {
wipeGreen();
}
else if (randNumber == 3) {
wipeBlue();