This winter holiday season celebrate in style with a IoT-enabled Christmas Tree! By using only a few Sparrow Wireless Sensor Nodes, you can create this awesome project in which you can control the lights on your Christmas tree. Modify colors and luminosity to create a fully-customizable Christmas experience! You won't need any pesky wires for this project, as the sensor nodes will act at the same time as lights and ornaments.
What You'll Need to Build the ProjectThis is a quick project, and you will need the following parts:
- Sparrow sensor nodes - I used 10, but you can use as many as you like.
- One Sparrow Nest programming board.
- Laptop PC with pre-installed Python and Arduino IDE.
- Sparrow plugin libraries for the Arduino IDE (install them from here).
- Any other Christmas decorations!
The nodes will run a short program which will control the RGB LED, making it blink. Also, each node will listen for incoming commands from a coordinator node. The commands will alter the LEDs color and turn on or off the blinking.
The coordinator node is connected to the laptop through its USB cable and receives commands over the serial-to-USB interface from a Python program. I have built a small GUI for the Python program to make it easier to configure each node's color.
On the Christmas tree, each node will run a simple code which will constantly blink its RGB LED. Each node has a predetermined address defined by NODEADDR in the code. Modify this to correspond to your own nodes. You can input any 8-bit address, except 255, which is used as a broadcast address.
Using this scheme, we can change the color of individual nodes in the tree, but also configure all nodes to blink to the same color.
The code listens for any incoming radio data from the coordinator node and periodically blinks the LED. For radio transmission, we have used the Sparrow Transfer library, detailed here.
Program each node with the code below. Start with address 0 and then increment until you get to the last node. It's important that nodes have consecutive addresses starting at address 0.
#include "SparrowTransfer.h"
#define NODEADDR 8 //this should be unique for each node (range 0-254)
SparrowTransfer ST;
struct RECEIVE_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
uint8_t address;
uint8_t red;
uint8_t green;
uint8_t blue;
};
//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;
// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
unsigned long currentMillis = 0;
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)
// pins for the LEDs:
const int redPin = 8;
const int greenPin = 11;
const int bluePin = 10;
int red = 0xFF; //variables to store the current color intensity for each channel
int green = 0xFF; //the LED works on reverse logic, so writing a 0x00 will completely turn on the respective LED
int blue = 0xFF; //we start with all LEDs off
uint8_t address = NODEADDR;
void setup() {
// initialize serial:
Serial.begin(9600);
// make the pins outputs:
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
//start the library, pass in the data details
ST.begin(details(mydata));
}
void ledColor(byte red, byte green, byte blue) //sets the color of the RGB led on the sparrow node
{
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
void BlinkLED() //makes the led blink witohut using the delay() function
{
currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
{
ledState = HIGH;
ledColor(0xFF, 0xFF, 0xFF); //turn the LED off
}
else
{
ledState = LOW;
ledColor(red, green, blue); // write the current color
}
}
}
void loop() {
if(address == NODEADDR || address == 0xFF) //we use 0xFF as a broadcast address
BlinkLED(); //all nodes will change their color when receiving address 0xFF
if(ST.receiveData()){ //if a new data packet arrived, alter the current color of the LED
if(mydata.address == NODEADDR || mydata.address == 0xFF)
{
address = mydata.address;
red = mydata.red;
green = mydata.green;
blue = mydata.blue;
}
}
}
The Coordinator NodeOn the coordinator node, we will run a short Arduino sketch that will parse incoming data from the Serial interface, format it properly and send it on the radio transceiver. Incoming data on the serial interface is in CSV format, a typical packed looks like this: (address, red, green, blue, '\n').
#include "SparrowTransfer.h"
//create object
SparrowTransfer ST;
int red, green, blue, address;
struct SEND_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
uint8_t address;
uint8_t red;
uint8_t green;
uint8_t blue;
};
//give a name to the group of data
SEND_DATA_STRUCTURE mydata;
void setup() {
// initialize serial:
Serial.begin(9600);
//start the library, pass in the data details
ST.begin(details(mydata));
}
void loop() {
while (Serial.available() > 0) { //data packets should arrive in csv format, ended by a '\n'
address = Serial.parseInt(); // first value should be the node address
red = Serial.parseInt(); // get the value for the red channel
green = Serial.parseInt(); // get the value for the green channel
blue = Serial.parseInt(); // last value should be the blue channel
// look for the newline. That's the end of a packet
if (Serial.read() == '\n') {
// constrain the values to 0 - 255 and invert because our LED is a common-cathode one
red = 255 - constrain(red, 0, 255);
green = 255 - constrain(green, 0, 255);
blue = 255 - constrain(blue, 0, 255);
mydata.address = address;
mydata.red = red;
mydata.green = green;
mydata.blue = blue;
ST.sendData(); //resend the packet a few times, just to make sure it gets through
delay(100);
ST.sendData();
delay(100);
ST.sendData();
delay(100);
ST.sendData();
delay(100);
ST.sendData();
delay(100);
// print the three numbers in one string as hexadecimal:
Serial.print(address, HEX);
Serial.print(red, HEX);
Serial.print(green, HEX);
Serial.println(blue, HEX);
}
}
}
The Python GUII have written a short Python program to control the network. Each sensor node corresponds to a button on the GUI. Clicking on the button will open a color picker, allowing you to select a color for the node. After the color has been selected, the code sends the new color data through the serial interface in the same CSV format specified above. There is also a global change color button which affects the color of all nodes.
from Tkinter import *
from tkColorChooser import askcolor # brings up the color dialog box
from functools import partial
import time
import serial
import thread
def setColor(button): # sets the color of an individual node (node address is given by the button variable)
(rgb, hexval) = askcolor()
if hexval:
ledVal = str(button) + ',' + str(rgb[0]) + ',' + str(rgb[1]) + ',' + str(rgb[2])
print ledVal # testing
ser.write(ledVal + '\n')
buttons[button].config(bg=hexval)
def setAll(): # sends a broadcast message for all nodes to change to the selected color
(rgb, hexval) = askcolor()
if hexval:
ledVal = "255," + str(rgb[0]) + ',' + str(rgb[1]) + ',' + str(rgb[2])
print ledVal # testing
ser.write(ledVal + '\n')
for y in range(0, 11):
buttons[y].config(bg=hexval)
def readSerial(ser, data):
while 1 :
out = ''
# let's wait one second before reading output (let's give device time to answer)
time.sleep(1)
while ser.inWaiting() > 0:
out += ser.read(1)
if out != '':
#print ">>" + out
log.insert('0.0', out)
# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial('COM128', 9600)
ser.isOpen()
thread.start_new_thread( readSerial, (ser, 1) ) #start a new thread for the serial interface
partials = [] # we're using partials to send data to the button click handlers
for x in range(0, 11):
# create the partial
f = partial(setColor, button = x)
# Add the partial to the list
partials.append(f)
#create the form
form1 = Tk()
# set the form's title
form1.title('Christmas Lights')
canvas_width = 493
canvas_height =493
canvas = Canvas(form1, width=canvas_width, height=canvas_height)
canvas.pack()
img = PhotoImage(file="img/tree.gif")
canvas.create_image(0,0, anchor=NW, image=img)
buttons = []
for y in range(0, 11):
# create a button
button = Button(form1, text='N'+str(y), command=partials[y])
buttons.append(button)
cycle = Button(form1, text='Cycle All', command=setAll)
b0_window = canvas.create_window(240, 120, anchor='nw', window=buttons[0])
b1_window = canvas.create_window(210, 200, anchor='nw', window=buttons[1])
b2_window = canvas.create_window(270, 200, anchor='nw', window=buttons[2])
b3_window = canvas.create_window(190, 280, anchor='nw', window=buttons[3])
b4_window = canvas.create_window(240, 280, anchor='nw', window=buttons[4])
b5_window = canvas.create_window(290, 280, anchor='nw', window=buttons[5])
b6_window = canvas.create_window(160, 360, anchor='nw', window=buttons[6])
b7_window = canvas.create_window(210, 360, anchor='nw', window=buttons[7])
b8_window = canvas.create_window(270, 360, anchor='nw', window=buttons[8])
b9_window = canvas.create_window(320, 360, anchor='nw', window=buttons[9])
b10_window = canvas.create_window(10, 10, anchor='nw', window=cycle)
# make a scrollbar
scrollbar = Scrollbar(form1)
scrollbar.pack(side=RIGHT, fill=Y)
# make a text box to put the serial output
log = Text ( form1, width=60, height=5, takefocus=0)
log.pack()
# attach text box to scrollbar
log.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=log.yview)
# run the event-loop/program ...
form1.mainloop()
Installing the Christmas TreeHere is a video taken while decorating my Christmas tree:
Comments