Christmas fun for makers
Maker Christmas
Make your own Christmas music box with a microcontroller, servomotor, NeoPixel LED ring, and mini-MP3 player.
Music boxes and Christmas pyramids are among the top sellers in Germany during the Advent season. Expensive, hand-carved items can fetch four-figure sums, whereas cheap imitations sell for EUR20 (~$17). Christmas pyramids are often powered by the heat of candles, and the cheaper ones in particular have such an unfavorable candle-to-propeller ratio that the pyramid will not turn without mechanical help. Music boxes are driven by fragile springs, but the constant need to rewind them spoils the fun.
My idea of building a music box of my own design was born from these frustrations. In terms of the electronics, you do not need many components. The following sections are intended to give you some ideas for your own projects; the parts used in this example can easily be replaced by whatever delving into the depths of your lumber box reveals. The only important elements in this project are light, motion, and music.
Controls
To control the project, I used a Trinket M0 microcontroller [1] by Adafruit (Figure 1), which runs CircuitPython, a minimalist Python that supports a wide range of peripherals with its many libraries. The price of the controller is in the same range as a Pi Zero, including an SD card, but it is easier to put into operation because you do not need to install and configure an operating system.
Another advantage of a microcontroller is that you do not have to boot it, and you can simply switch it off without damaging the installation. The biggest advantage, though, is the CircuitPython support. The examples offered here illustrate how little code is needed to implement your ideas. Independently, the project could also be implemented with a Pi Zero, but some tweaks would be necessary at various points, such as the power supply.
Let There Be Light
In this project I use LEDs – more precisely, NeoPixel LEDs [2] – to create the illumination effects. NeoPixel LEDs are available in all shapes and sizes, from long strips through rings and arrays to individual LEDs. The components already contain the driver chips, which make them a bit more expensive than normal LEDs, but make controlling the LEDs far simpler.
NeoPixels normally require 5V. Three connections are all you need: supply voltage, ground, and data. If the cables are short and the number of pixels is low, 3.3V might be sufficient for the power supply and control. The maximum brightness is reduced a little, but because the LEDs are very bright anyway, the low voltage doesn't matter in practical terms. Regardless of the selected voltage, it is important to check that the NeoPixels do not overload the voltage source. The Raspberry Pi's 3.3V pins are not designed to deliver high currents. The Trinket's 3.3V rail has fewer problems: The built-in converter outputs 500mA.
I chose to use a 24-pixel RGBW LED ring (Figure 2). This device could easily be supplied with 3.3V from the Trinket M0 and controlled directly. The power consumption with the LEDs turned down is about 60mA maximum. The LED ring is the most expensive single component in the project ($17/EUR21). When soldering the cables, avoid creating a solder bridge to the adjacent LEDs.
In the minimal application program (Listing 1), the neopixel library for CircuitPython (imported in line 3) expects the code in the lib/
subdirectory. However, the library has a bug: When initializing the object (lines 8-12), you have to specify the pixel_order=neopixel.GRBW
argument, even though the value you are passing in is the default.
Listing 1
NeoPixel Control
01 import time 02 import board 03 import neopixel 04 05 pixel_pin = board.D2 06 num_pixels = 24 07 08 pixels = neopixel.NeoPixel(pixel_pin, 09 num_pixels, 10 pixel_order=neopixel.GRBW, 11 brightness=0.05, 12 auto_write=False) 13 14 def colorwheel(pos): 15 if pos < 0 or pos > 255: 16 return (0,0,0,0) 17 if pos < 85: 18 return (255 -- pos * 3,pos * 3,0,0) 19 if pos < 170: 20 pos -= 85 21 return (0,255 -- pos * 3,pos * 3,0) 22 pos -= 170 23 return (pos * 3,0,255 -- pos * 3,0) 24 25 def rainbow(wait): 26 for j in range(255): 27 for i in range(num_pixels): 28 rc_index = (i * 256 // num_pixels) + j 29 pixels[i] = colorwheel(rc_index & 255) 30 pixels.show() 31 time.sleep(wait) 32 33 while True: 34 rainbow(0.1)
The program generates rainbow colors across all pixels and rotates more or less slowly, depending on the wait
parameter (lines 25 and 31). CircuitPython does not support interrupts, so if you want to connect an additional button to the Trinket M0 (e.g., for an on/off switch or to switch between effects), you have to query the switch at a suitable point. In the example, this would be either inside the while
loop (lines 33-34) or before line 31.
In Motion
A continuous rotation servomotor rotates the music box. In contrast to stepper motors, which support precise positional control, only speed and direction can be specified for rotating servos. In this project, I use a Fitec FS90R [3] ($5, EUR6; Figure 3), which is available from various vendors.
Again, a few lines of code are all you need for control (Listing 2). First, a pulse-width modulation (PWM) object is created in line 6, along with a control object for the motor in line 7. The throttle
parameter controls the direction (sign) as well as the speed and expects values between -1
and 1
. Depending on the model and power supply, the motor does not stop for a value of
. In my lab, it stopped for values between 0.05
and 0.10
.
Listing 2
Servomotor Control
01 import time 02 import board 03 import pulseio 04 from adafruit_motor import servo 05 06 pwm = pulseio.PWMOut(board.D0, frequency=50) 07 my_servo = servo.ContinuousServo(pwm) 08 my_servo.throttle = 0.01 09 10 while True: 11 time.sleep(1)
Without a while
loop, the program ends and the microcontroller resets the hardware – hence lines 10 and 11. You can start, stop, or change the speed of the motor within this loop, but for this project, no further support after startup would be practical, because you will be manipulating the LEDs in the while
loop of the main program (Listing 1).
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
System76 Refreshes Meerkat Mini PC
If you're looking for a small form factor PC powered by Linux, System76 has exactly what you need in the Meerkat mini PC.
-
Gnome 48 Alpha Ready for Testing
The latest Gnome desktop alpha is now available with plenty of new features and improvements.
-
Wine 10 Includes Plenty to Excite Users
With its latest release, Wine has the usual crop of bug fixes and improvements, along with some exciting new features.
-
Linux Kernel 6.13 Offers Improvements for AMD/Apple Users
The latest Linux kernel is now available, and it includes plenty of improvements, especially for those who use AMD or Apple-based systems.
-
Gnome 48 Debuts New Audio Player
To date, the audio player found within the Gnome desktop has been meh at best, but with the upcoming release that all changes.
-
Plasma 6.3 Ready for Public Beta Testing
Plasma 6.3 will ship with KDE Gear 24.12.1 and KDE Frameworks 6.10, along with some new and exciting features.
-
Budgie 10.10 Scheduled for Q1 2025 with a Surprising Desktop Update
If Budgie is your desktop environment of choice, 2025 is going to be a great year for you.
-
Firefox 134 Offers Improvements for Linux Version
Fans of Linux and Firefox rejoice, as there's a new version available that includes some handy updates.
-
Serpent OS Arrives with a New Alpha Release
After months of silence, Ikey Doherty has released a new alpha for his Serpent OS.
-
HashiCorp Cofounder Unveils Ghostty, a Linux Terminal App
Ghostty is a new Linux terminal app that's fast, feature-rich, and offers a platform-native GUI while remaining cross-platform.