I have a 3 year old granddaughter who is now old enough to know she is missing out on something when her older siblings go to Grandad's house for "Robot Summer Camp" but not old enough to attend herself. So I decided to make something for her.
It should have at least some of the things that make a toy attractive to a 3 year old:
- Annoying to parents
Unfortunately due to multiple matters including procrastination I was only able to start this last week and so am limited in both time and materials. A friend recently sent me a link to a site that featured analog synthesizer projects to do with children and it sparked an idea. I could make an analog synthesizer violin with a bow that acted like a potentiometer! Or something like that. And here is a favorite book of hers which features animals making music:
So somehow the idea for the Rainbow Ukulele gradually formed
Where To Start?
Due to the shortage of time it was necessary to work with what was at hand. It was also time to relearn for the x thousandth time that taking shortcuts does not get things done quicker. Here is what there was to work with:
- Scrap plywood
- Adafruit Feather M4 Express
- Adafruit Prop-Maker Wing (has amplifier and speaker connections)
- Lots of buttons
- A ring with WS21812 "NeoPixels"
- Speaker removed from a junked printer
- Wire and stuff
That should be enough.
No time to do proper planning! Just start building! Cut out a pattern semi-freehand that fits the plywood:
This is the cheapest, crummiest plywood ever... Cut out the pieces and rasp them to shape:
That doesn't look too bad:
How to make the body? Could sandwich foamboard...
That is the crummiest material to cut to shape and work with ever... Glue and clamp:
I simultaneously blundered through the firmware (is CircuitPython firmware?) while doing the build above. The Adafruit Prop-Maker FeatherWing is a reasonably priced daughter board for the Feather series that provides support for NeoPixels and a Class D amplifier. Audio is only available with the M0 Express and M4 series Feathers and needs to be tested.
The first surprise is that according to Adafruit:
"Arduino does not have great audio playback support. So we don't recommend using Arduino if you need to play audio. If you'd like to use the Adafruit Prop-Maker FeatherWing's Audio capabilities, check out the CircuitPython code."
OK, I don't fully buy that, but I checked out the CircuitPython code. Unfortunately the examples and libraries seem to be blocking. Worst of all it is not possible to use interrupts and according to this pull request closed by ladyada there is no plan to have interrupts. There is an issue discussion here which seems to go around in circles with no resolution so I guess we are reduced to polling with blocking code. There are other issues with Python and microcontrollers but lack of interrupts is a non-starter for me. (MicroPython, from which Adafruit forked CircuitPython, does support some form of interrupts though).
To get something started I took a simple NeoPixel example that rotates through a color wheel and poll for button pushes inside the for loop to play a wav file. The "pass" feature that is supposed to keep the audio from blocking does not seem to work and the NeoPixels do not change color until the wav file is finished. In addition, I could not get the mixer or volume features to work properly (or at least the way I needed) even using the example code provided by Adafruit. The wav files are limited to 22 kHz or less and 16 bit wav format. Should I continue development in future I would use C/C++ on another microcontroller or possibly a Pi Zero.
To use the Python code below it is only necessary to place animal sounds (or whatever sounds are desired) on the Feather M4 Express which shows up as a drive when connected.
""" Plays files in WAV_FILE on Adafruit M4 Express with Prop-Maker FeatherWing when the PINs are pulled high Version 0.2 F Milburn September 2019 """ # import time import digitalio import board import audioio import neopixel NUM_PIXELS = 24 # NeoPixel strip length (in pixels) BRIGHTNESS = 0.2 # NeoPixel brightness enable = digitalio.DigitalInOut(board.D10) enable.direction = digitalio.Direction.OUTPUT enable.value = True strip = neopixel.NeoPixel(board.D5, NUM_PIXELS, brightness=BRIGHTNESS) def wheel(pos): # Input a value 0 to 255 to get a color value. # The colours are a transition r - g - b - back to r. if pos < 0 or pos > 255: return (0, 0, 0) if pos < 85: return (255 - pos * 3, pos * 3, 0) if pos < 170: pos -= 85 return (0, 255 - pos * 3, pos * 3) pos -= 170 return (pos * 3, 0, 255 - pos * 3) # define wave files WAV_FILE = ("Burp.wav", "Coo.wav", "frog.wav", "cow-moo3.wav", "cricket-1.wav", "goat.wav", "Snore.wav") # map switches to board pins, comment gives wire color PIN = (board.D4, # purple board.TX, # gray/black board.RX, # white/green board.SCK, # gray board.A4, # gray/orange board.A3, # white board.A2) # white/yellow # function to generate sounds from wav files def make_sound(sound_file): with audioio.AudioOut(board.A0) as audio: # Speaker connector wave_file = open(sound_file, "rb") wave = audioio.WaveFile(wave_file) audio.play(wave) while audio.playing: pass # Set input pins up SOUND0 = digitalio.DigitalInOut(PIN) SOUND0.pull = digitalio.Pull.UP SOUND1 = digitalio.DigitalInOut(PIN) SOUND1.pull = digitalio.Pull.UP SOUND2 = digitalio.DigitalInOut(PIN) SOUND2.pull = digitalio.Pull.UP SOUND3 = digitalio.DigitalInOut(PIN) SOUND3.pull = digitalio.Pull.UP SOUND4 = digitalio.DigitalInOut(PIN) SOUND4.pull = digitalio.Pull.UP SOUND5 = digitalio.DigitalInOut(PIN) SOUND5.pull = digitalio.Pull.UP SOUND6 = digitalio.DigitalInOut(PIN) SOUND6.pull = digitalio.Pull.UP while True: for i in range(255): strip.fill((wheel(i))) # Buttons that are pushed read as 0 or false if not SOUND0.value: make_sound(WAV_FILE) if not SOUND1.value: make_sound(WAV_FILE) if not SOUND2.value: make_sound(WAV_FILE) if not SOUND3.value: make_sound(WAV_FILE) if not SOUND4.value: make_sound(WAV_FILE) if not SOUND5.value: make_sound(WAV_FILE) if not SOUND6.value: make_sound(WAV_FILE)
Different sounds in wav format are available in many places on the internet.
The pins used are documented in the Python script above and the board connections for Neopixels and the speaker are given in the Adafruit Prop-Maker documentation (links at end of blog).
Putting It All Together
Soldered everything up and then hollowed out the interior with a box cutter and a kitchen knife. Affixed the microcontroller, button switches, and speaker with hot glue and super glue.
The Feather M4 Express has a built in 100 mA Lipo battery charger which worked well and was easy to use. There is room in the body for the battery and an on / off switch which is currently not used since I elected not to leave the battery in. The demonstration unit is shown below:
Unfortunately for you the element14 rules require that a video be posted. But I am going to spare you somewhat and not go through the whole barnyard menagerie.
Sorry for that... the 3 year old liked it :-)
This was my first attempt to add music to a project and was a lot of fun. I had a chance to show it to my granddaughter and she really enjoyed it.
One of the things she enjoys is picking out different sounds for it, adding them with my help, and then playing them back. That is one of the things that distinguishes it from some commercial products. Since this is more of a concept demonstration I did not leave it with her. But since she seemed to really enjoy it I plan to revisit the project and investigate the following improvements:
- Use a teensy or Raspberry Pi
- Improve the build of the ukulele body
- Change button position to better fit little hands
- Add a microphone to capture sounds
- Make it easier to download and add sounds from the internet
- Add a multipole switch to go from say wav files to different synthesized sounds
- Look at the violin and bow idea some more with a more analogish approach
- Allow a way for parents to more easily control volume
Thanks for reading! Comments and suggestions are always welcome.
15 Sep 2019: Documented Beta Testing