Table of contents
Abstract
Every Christmas Tree has small LEDs and ornaments, so I thought, why not combine these 2 into one thing? In this project, I'll show you how I made a spinning Christmas Tree ornament that lights up and changes colors!
Project
Spinning Christmas Tree Ornament
1. Introduction
Hi, This will be my project for the Holiday Special 2023 Project14! In this blog, I will describe all aspects of my project, so I hope you enjoy it. When you look at a Christmas Tree chances are you will see 2 things, ornaments, and LED lights. Why not combine them into one? My idea for this project is to make a spinning Christmas Tree ornament as the title says, that would have LED-s on the outside, and when it spins up, it will give a cool effect. This can also be further improved by adding something like an IMU or an encoder to the ornament and making a spinning round display! Let's begin by looking at the mechanical design of the Ornament.
2. Mechanical Design
Now that you've gotten the idea for my project, let's first make a list of requirements for our mechanical design.
Requirement List
First of all, we need something to spin up the whole ornament, and for that, I will be using a small brushed DC motor, since I have a lot of them around, plus they are really easy to control using a single MOSFET. To enable tracking the position of the spinning ornament, I will add a small optical encoder and encoder wheel. Besides that, we need a battery for the power supply since we can't have any cable running to it, we need an RGB LED strip on the outer ring of the ornament and we need some electronics for controlling all of that. As the brain for the whole project, I will be using a Raspberry Pico W, since they are easy to use and quite a powerful little board with WiFi capabilities which will allow us to control the ornament wirelessly. Here is the list of the main things that we will need (you'll be able to find the full BOM at the bottom of this blog).
- Brushed DC motor
- 9V Battery
- Raspberry Pico W
- Encoder and Encoder Wheel
- RGB LED strip (addressable or standard)
- 3D printed parts
- Small hardware like screws, a bearing, and a 5mm metal rod
CAD Design
To package all of this into an ornament, I will design all of the necessary parts using Autodesk Fusion360, and print them out of PLA. You can find all of the CAD files both STEP and STL linked at the bottom of this blog. Here is the picture of the final CAD model of the ornament.
All of the purple parts in the picture are gonna be 3D printed, the gray cylinder in the middle of the ornament is the DC motor that spins the metal shaft with the wooden encoder disk glued onto it. They are connected using a small 3D-printed coupler. The red part in the picture is the 9V battery, since it's individually the heaviest part of the whole ornament, I made sure for the center of mass of the battery to align with the axis of rotation to minimize any unwanted vibrations when the ornament spins up. The LED strip is supposed to be glued with double-sided sticky tape on the purple outer ring. In the next picture, you can see the other side of the assembly.
On this side of the ornament, we can see the 2 green PCBs. The smaller one is the encoder module, while the bigger one is a piece of perfboard to which I will solder all of the necessary components. To save on space, all of these parts are put together using M3 screws (and a couple of M2.5), where I cut the threads using a tap.
3. Electronics Design
The electronics for this build are pretty simple with just a couple of smaller circuits that I will show you. The whole electronics can be broken down into power electronics, motor electronics, and PicoW connections.
Power Electronics
As the main power source for this project, as mentioned before, I will use a 9V battery. While 9V is okay for driving the motor, we need to step that voltage down if we want to power the Raspberry Pico W and the LED-s. We need to drop the voltage down from 9V to 5V. Since the 5V won't use that much power and I'm not aiming at any high efficiency, I will go with a linear regulator for this project, the 7805, that will drop the voltage down to 5V with just an addition of 2 capacitors to stabilize everything.
Motor Electronics
I want to leave the option of being able to control the speed of the motor, but for this purpose, we don't care about the direction of the spin of the motor. With that in mind, the easiest way to control a motor is by using a single N channel MOSFET as a low-side switch. By changing the duty cycle of the PWM on the pin that is connected to the MOSFET we can change the speed of the motor. If you're trying to replicate this, just make sure that the gate threshold voltage for the MOSFET you are using is under 3.3V. The MOSFET I will be using is the IRL540N. Besides the MOSFET I've added a 10k pulldown resistor as well as a 330R current limiting resistor. You can see the connections in the picture below.
Full Schematic
All of the other connections are just straight connections to the Raspberry Pico W. This includes 2 things that we need to connect, one is the encoder which just sends its signal to a single digital pin and the other connection is for the RGB LED. I've with an RGB LED strip that has addressable LEDs meaning that I can change the color and brightness of each of the LEDs completely through software which will enable me to program any sort of different effects besides solid colors. In the picture below you can see the full schematic.
Soldered Electronics
To solder everything I've taken a piece of perfboard and started adding components going by the schematics I've shown above. To easily disconnect everything, all of the connections with the perfboard go through crimp nylon connectors since they are easy to use and cheap. When printing a mount for a perfboard, make sure to make the distance between mounting holes to be a whole number multiplied by 2.54mm so you can just drill out a hole on the perfboard. In the picture below, you can see the finished soldered perfboard.
4. Final Assembly
The assembly process is straightforward, the easiest way to go about it is to follow the CAD model as you put things together. I've left STEP files so you can customize the motor mount to fit the DC motor that you might want to use for this project. Besides that, everything goes together with M3 and M2.5 screws. You can try cutting the thread with the screws themselves, but since the parts are rather small and prone to cracking, I like the cut the threads beforehand using a small tap. Two things to watch out for here are the battery and the shaft assembly. To keep the battery in place I just used a piece of double-sided sticky tape, as for the shaft assembly, I've secured the metal shaft to the coupler by drilling and tapping a hole through the metal shaft, but you can just use a bit of super glue. You can see pictures of the completed assembly below.
5. Software
Only one thing left before we can take the ornament for a spin (literally for a spin!) and that is the software. The software is split into 2 categories here, one is the software for the Raspberry Pico W, and the other is software for controlling the ornament wirelessly. To control the ornament wirelessly, I used NodeRed since I have a Raspberry Pi 3 set up as a server running Node-Red, so all controls go over MQTT. This was the fastest way for me to integrate wireless controls, you can also use things like Blynk and similar services which allow you to connect and control a device easily over WiFi. Let's first take a look at the Raspberry Pico W software.
Raspberry Pico W Code
Here I'll just summarize how the code on the Pico works. We first need to connect to the internet using the WiFi library and connect to the MQTT broker using the ArduinoMqttClient library. For controlling the addressable LEDs I used the FastLED library which is extremely easy to use and for the PWM I used the RP2040_PWM library.
The Pico first connects to the internet over WiFi and then to the MQTT broker, and sets up all of the pins. After that in the loop, it just listens and waits for new messages on the MQTT topics. There are 5 different topics:
- picow/red - LED Red channel topic
- picow/green - LED Green channel topic
- picow/blue - LED Blue channel topic
- picow/motor - Motor ON/OFF topic
- picow/mode - Change mode topic
Once it receives a message, it checks from which topic it is, and then based on the message it can adjust the RGB values for the LED strip or start/stop spinning the ornament, and so on. That would be how the whole code works, you can find the whole code below, just make sure to change the SSID, password, and MQTT parameters.
#include "RPi_Pico_TimerInterrupt.h" #include "RP2040_PWM.h" #include <FastLED.h> #include <WiFi.h> #include <ArduinoMqttClient.h> #define PIN_LED 14 #define PIN_ENC 4 #define PIN_MOT 10 #define NUM_LEDS 17 const char* ssid = "xxx"; const char* password = "xxx"; const char topic_red[] = "picow/red"; const char topic_green[] = "picow/green"; const char topic_blue[] = "picow/blue"; const char topic_motor[] = "picow/motor"; const char topic_mode[] = "picow/mode"; int red, green, blue, motor; RPI_PICO_Timer TimerMain(0); // Timer for the heartbeat LED - Frequency set in setup() - Default 10 Hz RP2040_PWM* PWM_Instance[1]; // PWM for the Pico CRGB leds[NUM_LEDS]; //WiFiMulti multi; WiFiClient multi; MqttClient mqttClient(multi); bool TimerHandler(struct repeating_timer *t) { (void) t; return true; } void setup() { // put your setup code here, to run once: Serial.begin(115200); pinMode(PIN_MOT, OUTPUT); // LED Timer - Frequency set in the Parameters section if (TimerMain.attachInterruptInterval(10000, TimerHandler)) { Serial.print("Starting LED Timer OK, millis() = "); Serial.println(String(millis())); } else Serial.println("Can't set LED Timer. Select another freq. or timer"); // Setting up PWM PWM_Instance[0] = new RP2040_PWM(PIN_MOT, 1000, 0.00f); PWM_Instance[0]->setPWM(); // Configuring the LED strip FastLED.addLeds<WS2813, PIN_LED, RGB>(leds, NUM_LEDS); Serial.print("Attempting to connect to WPA SSID: "); Serial.println(ssid); while (WiFi.begin(ssid, password) != WL_CONNECTED) { // failed, retry Serial.print("."); delay(5000); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); Serial.print("Attempting to connect to the MQTT broker: "); Serial.println("192.168.1.101"); if (!mqttClient.connect("192.168.1.101", 1883)) { Serial.print("MQTT connection failed! Error code = "); Serial.println(mqttClient.connectError()); while (1); } Serial.println("You're connected to the MQTT broker!"); Serial.println(); Serial.print("Subscribing to topics "); //Serial.println(topic); //Serial.println(); mqttClient.subscribe(topic_red); mqttClient.subscribe(topic_green); mqttClient.subscribe(topic_blue); mqttClient.subscribe(topic_motor); mqttClient.subscribe(topic_mode); //PWM_Instance[0]->setPWM(PIN_MOT, 1000, 75.00); } void loop() { int messageSize = mqttClient.parseMessage(); if (messageSize) { // we received a message, print out the topic and contents String msg_top = mqttClient.messageTopic(); Serial.print("Received a message with topic '"); Serial.print(msg_top); Serial.print("', length "); Serial.print(messageSize); Serial.println(" bytes:"); // use the Stream interface to print the contents String msg = ""; while (mqttClient.available()) { char c = (char)mqttClient.read(); Serial.print(c); msg = msg + c; } Serial.println(); Serial.println(); if (msg_top == "picow/red") { Serial.println("Change Red command."); red = msg.toInt(); for (int i = 0; i < NUM_LEDS; i++) { // g r b leds[i] = CRGB(green, red, blue); } FastLED.show(); } if (msg_top == "picow/green") { Serial.println("Change Green command."); green = msg.toInt(); for (int i = 0; i < NUM_LEDS; i++) { // g r b leds[i] = CRGB(green, red, blue); } FastLED.show(); } if (msg_top == "picow/blue") { Serial.println("Change Blue command."); blue = msg.toInt(); for (int i = 0; i < NUM_LEDS; i++) { // g r b leds[i] = CRGB(green, red, blue); } FastLED.show(); } if (msg_top == "picow/motor") { Serial.println("Motor Command"); if (msg == "true") { Serial.println("Turning motor ON"); PWM_Instance[0]->setPWM(PIN_MOT, 1000, 75.00); } else { Serial.println("Turning motor OFF"); PWM_Instance[0]->setPWM(PIN_MOT, 1000, 0.00); } } } }
Node-Red
I realize this is a solution that not many people will try to follow when it comes to wirelessly controlling the Pico but it was the easiest for me to integrate since I already have the Raspberry Pi running nonstop on my network. In the picture below, you can see all of the nodes that I've used and how they're all connected, it's a rather simple interface with 3 sliders and 2 switches.
Each of the dashboard controls is connected to send a message to a certain topic. In the 2 pictures below you can see how I've configured the nodes to work properly. The range for the sliders is from 0 to 255 while the switches just send 0/1 which on the Pico end is received as true/false.
That would be the whole interface code. When we launch it, we can see the sliders and switches as shown below.
One good thing about this approach is that besides having the controls on a computer, we also have controls on the phone. Here is what that looks like.
6. Testing
And we finally reached the fun part of the project, the testing! It's always an amazing feeling reaching this part of the project, let's begin by looking at the color changes!
Color Changing
Each of the RGB channels can be adjusted between 0 and 255 so we can get any color that we want.
Below is the video of going through the colors on the ornament.
Spinning Test
And in the end, we of course have the spinning test. I've discovered some problems here as you will see in the video itself, but the LED strip will only show the color red while it's spinning, I suppose it's a power issue since the motor is probably drawing a lot of current from the 9V battery, if you have any ideas why something like this would happen, please let me know! The effect is still pretty cool to look at!
The colors show up as soon as the motor gets turned off and it looks awesome, here are a few pictures that I managed to capture of the ornament spinning (or stopping to spin to get the other colors to pop up). First the one while spinning.
And here are a couple when the motor gets turned off and the colors come out!
7. Summary
This was a really fun project to work on and I'll have it in much better shape for Christmas next year. The main thing I need to figure out is why all of the colors go off when the motor starts spinning and then integrate the encoder properly into the whole project. I'll also need to revisit the whole mechanical assembly, balance it out a bit, and maybe go with an even faster motor, my goal for this is to work like a low-resolution spinning display next time. Besides all of that, I'm still extremely happy with how it turned out and how it looks. You can find all of the CAD files on the link below:
CAD files: https://www.printables.com/model/724048-spinning-christmas-ornament
Thanks for reading the blog, if you have any questions or suggestions, please leave them down below! Hope you enjoyed this project and that it will inspire you to make something by yourself!
Kind regards,
Milos Rasic
References
- https://github.com/256dpi/arduino-mqtt
- https://www.printables.com/model/724048-spinning-christmas-ornament