Christmas fun for makers

Maker Christmas

© Lead Image © fabio bergamasco,

© Lead Image © fabio bergamasco,

Article from Issue 241/2020

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.


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.

Figure 1: The Trinket M0 costs less than $10. The advantage of a microcontroller is that it does not have to boot and can simply be switched off.

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.

Figure 2: The NeoPixel ring with 24 RGBW LEDs is far easier to control compared with single components.

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
05 pixel_pin = board.D2
06 num_pixels = 24
08 pixels = neopixel.NeoPixel(pixel_pin,
09                            num_pixels,
10                            pixel_order=neopixel.GRBW,
11                            brightness=0.05,
12                            auto_write=False)
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)
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)
31     time.sleep(wait)
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.

Figure 3: A continuous rotation servomotor like the Fitec FS90R allows you to control the direction and speed of rotation.

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
06 pwm = pulseio.PWMOut(board.D0, frequency=50)
07 my_servo = servo.ContinuousServo(pwm)
08 my_servo.throttle = 0.01
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

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Adafruit PyPortal

    Unlike other displays for the Raspberry Pi, Adafruit's PyPortal touchscreen provides an autonomous environment, including a microprocessor, sound output, and a WiFi connection.

  • Light Painting

    In the photographic method of light painting, you expose a subject over an extended period of time while moving the light sources. With a little technical support from a Raspberry Pi Pico, you can achieve sophisticated results.

  • Google Announces Its Own Pixel Phones

    Google finally enters the phone hardware business.

  • Halloween Vending Machine

    A Halloween vending machine frightens visitors, but the braver ones receive a chocolate reward.

  • TUXEDO InfinityBook Gen7 MK1

    The next-generation laptop from TUXEDO is faster and lighter than previous business models.

comments powered by Disqus
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.

Learn More