Introduction
Somehow or other I've ended up doing another music project (how did that happen?). My project14 this time is going to be the
'Microbit Quartet': four micro:bits playing music in time with each other. Here's the video of the group playing the traditional song
'Early One Morning'. The three yellow micro:bits appear by kind permission of Colchester Public Library (ie I've borrowed them
on my library card).
How it Works
To keep things simple, I used the existing sound capability but added an additional amplifier so that it could drive a miniature 8
ohm loudspeaker at a reasonable volume. Each micro:bit is monophonic and plays a single note at a time. Synchronisation between the
microbits keeps them in time (one micro:bit acts as the conductor).
For the amplifier, I'm using an NJM2073 (New Japan Radio Co. Ltd.). This is a small, dual amplifier that can be used either for
stereo or in a bridge configuration. I'm using it mono in a bridge because of the low supply voltage. The datasheet says it will
work down to 1.8V, so it will do fine on the 3V battery pack. The datasheet gives this recommended circuit for the bridge
arrangement but I'm a little unclear as to what I feed to it so I'm going to prototype it on a breadboard first so that I can
experiment with it and make sure it works how I think it does.
Here it is on a breadboard. I've included the Zobel network that they recommend (the resistor and capacitor across the speaker),
but I've increased the resistor value from 1R to 5.6R, in line with their suggestion to make it closer to the real resistance of
the speaker at low supply voltages.
Testing the Circuit
Here's the circuit I've ended up with but without the speaker and Zobel network which you can get from the circuit above. This will
look a little odd - I wanted to try it in the simulator but didn't have a model so I created the innards from two 'ideal' op amps
and two current-controlled current sources to replace the transistor mirrors. It won't be a particularly good model overall but it
does model the internal biasing arrangement shown in the datasheet which I was curious about. Anyhow, ignore all that - what's
important is the external circuitry [I've labelled the pins for my virtual amplifier component so you can see what's internal and
what's external]. Sorry if this is a bit confusing, say in the comments if you want a more straightforward version.
The logic signal at the input is attenuated with the potential divider and then ac coupled to the amplifier input with a capacitor.
I've lightly filtered the signal with the 5nF capacitor [odd value - really two 10nF in series] to round off the edges of the
digital waveform.
Here's how it behaves on the breadboard with a 1kHz 3V pk/pk squarewave at the input. Looks a bit noisy, but sounds ok. The bridge
is working nicely with each side roughly centred between the supply rails. [The scope traces show each side of the speaker relative
to GND and are ac coupled. The speaker sees the difference between the two.]
Here's what it looks like without the RC network on the output. I thought that I might get away without it but it is necessary to
stop it trying to oscillate.
Building the Amplifiers
Here are the interface boards after I finished soldering them together (they come as a kit of parts). I'm using these
because three of the Micro:bits are on loan from my local library so I need to be careful with them - everything will get
tested with my own Micro:bit (the one in the picture) and the loan ones only plugged in once I'm sure of the circuits and
power connections. Sorry about the picture - taken quickly under a desk lamp.
My aim at the moment is just to throw something together quickly for the video, so construction is on some tripad board.
Here they are as I built them but without the connecting wires (different desk lamp, just to confuse the camera). They're
meant to be identical yet all are subtly different (which pleases my natural sense of disorder and untidyness).
Here are the three performers that are appearing courtesy of Colchester Public Library. They each come in a neat little
plastic box holding the processor, the USB lead, and a pair of batteries with a battery box. I thought I'd have to mark my
own board but I've noticed that all the library ones are yellow so I'll immediately be able to tell my cyan one apart from
the rest. The piece of paper with a date on it is the return date. My little assistant looks nervous - I don't think he's
happy at the prospect of meeting all these super-clever thinking machines.
I've wired the amplifier boards to the interface boards [feels like a production line around here at the moment] and done
a quick test using my own micro:bit. They all play music.
My own battery pack hasn't had the batteries changed for a long time (if ever) and only reads 2.57V but they still work
quite happily. At the moment the only music I've tried is the music.BLUES tune that comes with the Python music library
but, if it works with that, it'll work with anything I throw at it. One thing that I discovered is that the output from
the micro:bit (on P0) isn't a square wave. It looks like they're doing it with PWM (I'd guess with the hardware, given how
awful the software-emulated PWM that can go on any pin is) with a fairly large mark/space ratio. Here's what the 'scope sees measured
across the speaker:
It's good because it actually makes for a more interesting sound than the square waves I was using from the generator.
Next job is to wire in the synchronisation lead between the boards and then apply myself to sorting out the music and
setting up the world premiere performance.
I'm too late to compete for the prizes now but that's ok, there'll be plenty more Project14 competitions - I'm doing it for the fun of it now.
Synchronisation
The synchronisation between the boards is very simple. I just wired all the P1 pins together (there's a ground wire between the boards too, to act as a return). My own micro:bit outputs on that pin and all the others input on the same pin. The line goes high at the start of the note and low at the end. The lead does the note timing and the others watch for the changes and react accordingly.
Code
At the start I was going to use Python but, unlike the blocks, it doesn't seem to have a suitable function for sounding a note without also giving it a duration. I tried using the blocks but got very confused about how on earth to do do an array of values. At that point, I tried changing to Javascript and discovered that it's basically the same thing as the blocks (to the extent that you can view what you've done as either by swapping back and forth). Once I could see an actual programming language it was all plain sailing (children now must be really, really clever to cope with the block stuff - had me totally confused). Mind you, I never thought I'd see the day when I'd be programming a microcontroller in Javascript.
Here's the code I ended up with for each processor. It was surprisingly simple in the end, though it took me quite a while with a large sheet of paper to sort out the music. In case you don't recognise it, the music is the traditional song 'Early One Morning'. My micro:bit (the cyan one) plays the melody and the others play a succession of three-note chords as an accompaniment. The code struggles a little at times - if you watch the video closely you may notice that the displays have difficulty keeping up and the music is a bit off-beat in a couple of places - but it works well enough for this, even if it isn't 'production quality'.
Player 1 (Conductor):
let musicTones = [ 0, 0, 0, 0, 0, 0, 392, 392, 392, 392, 466, 587, 587, 659, 523, 440, 392, 370, 440, 294, 294, 392, 392, 392, 392, 466, 587, 587, 659, 523, 440, 370, 392, 440, 466, 523, 587, 466, 392, 440, 466, 523, 587, 466, 392, 392, 466, 587, 784, 740, 659, 587, 523, 466, 440, 392, 370, 392] let musicDurations = [ 400, 400, 400, 400, 400, 400, 400, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 400, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 800, 400, 200, 200, 200, 200, 400, 400, 200, 200, 200, 200, 400, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 800] let musicCharacters = [ "1", "2", "1", "2", "3", "4", "G", "G", "G", "G", "B", "D", "D", "E", "C", "A", "G", "F", "A", "D", "D", "G", "G", "G", "G", "B", "D", "D", "E", "C", "A", "F", "G", "A", "B", "C", "D", "B", "G", "A", "B", "C", "D", "B", "G", "G", "B", "D", "G", "F", "E", "D", "C", "B", "A", "G", "F", "G" ] basic.forever(() => { for (let i = 0; i < musicTones.length; i++) { pins.digitalWritePin(DigitalPin.P1, 1) basic.showString(musicCharacters[i]) music.ringTone(musicTones[i]) basic.pause(musicDurations[i]) pins.digitalWritePin(DigitalPin.P1, 0) music.ringTone(0) basic.pause(20) } music.ringTone(0) basic.pause(1000) })
Player 2:
let musicTones = [ 0, 0, 0, 0, 0, 0, 196, 196, 196, 196, 196, 196, 196, 131, 131, 131, 131, 147, 147, 147, 147, 196, 196, 196, 196, 196, 196, 196, 131, 131, 147, 147, 196, 147, 147, 147, 196, 196, 196, 147, 147, 147, 196, 196, 196, 196, 196, 196, 196, 131, 131, 131, 131, 147, 147, 147, 147, 196] let musicCharacters = [ " ", " ", "1", "2", "3", "4", "G", "G", "G", "G", "G", "G", "G", "C", "C", "C", "C", "D", "D", "D", "D", "G", "G", "G", "G", "G", "G", "G", "C", "C", "D", "D", "G", "D", "D", "D", "G", "G", "G", "D", "D", "D", "G", "G", "G", "G", "G", "G", "G", "C", "C", "C", "C", "D", "D", "D", "D", "G" ] basic.forever(() => { for (let i = 0; i < musicTones.length; i++) { while (pins.digitalReadPin(DigitalPin.P1) == 0) { } basic.showString(musicCharacters[i]) music.ringTone(musicTones[i]) while (pins.digitalReadPin(DigitalPin.P1) == 1) { } basic.showString(" ") music.ringTone(0) // basic.pause(20) } basic.showString(" ") music.ringTone(0) basic.pause(1000) })
Player 3:
let musicTones = [ 0, 0, 0, 0, 0, 0, 247, 247, 247, 247, 247, 247, 247, 165, 165, 165, 165, 185, 185, 185, 185, 247, 247, 247, 247, 247, 247, 247, 165, 165, 185, 185, 247, 185, 185, 185, 247, 247, 247, 185, 185, 185, 247, 247, 247, 247, 247, 247, 247, 165, 165, 165, 165, 185, 185, 185, 185, 247] let musicCharacters = [ " ", " ", "1", "2", "3", "4", "B", "B", "B", "B", "B", "B", "B", "E", "E", "E", "E", "F", "F", "F", "F", "B", "B", "B", "B", "B", "B", "B", "E", "E", "F", "F", "B", "F", "F", "F", "B", "B", "B", "F", "F", "F", "B", "B", "B", "B", "B", "B", "B", "E", "E", "E", "E", "F", "F", "F", "F", "B" ] basic.forever(() => { for (let i = 0; i < musicTones.length; i++) { while (pins.digitalReadPin(DigitalPin.P1) == 0) { } basic.showString(musicCharacters[i]) music.ringTone(musicTones[i]) while (pins.digitalReadPin(DigitalPin.P1) == 1) { } basic.showString(" ") music.ringTone(0) // basic.pause(20) } basic.showString(" ") music.ringTone(0) basic.pause(1000) })
Player 4:
let musicTones = [ 0, 0, 0, 0, 0, 0, 294, 294, 294, 294, 294, 294, 294, 196, 196, 196, 196, 262, 262, 262, 262, 294, 294, 294, 294, 294, 294, 294, 196, 196, 262, 262, 294, 220, 220, 220, 294, 294, 294, 220, 220, 220, 294, 294, 294, 294, 294, 294, 294, 196, 196, 196, 196, 265, 265, 265, 265, 294] let musicCharacters = [ " ", " ", "1", "2", "3", "4", "D", "D", "D", "D", "D", "D", "D", "G", "G", "G", "G", "C", "C", "C", "C", "D", "D", "D", "D", "D", "D", "D", "G", "G", "C", "C", "D", "A", "A", "A", "D", "D", "D", "A", "A", "A", "D", "D", "D", "D", "D", "D", "D", "G", "G", "G", "G", "C", "C", "C", "C", "D" ] basic.forever(() => { for (let i = 0; i < musicTones.length; i++) { while (pins.digitalReadPin(DigitalPin.P1) == 0) { } basic.showString(musicCharacters[i]) music.ringTone(musicTones[i]) while (pins.digitalReadPin(DigitalPin.P1) == 1) { } basic.showString(" ") music.ringTone(0) // basic.pause(20) } basic.showString(" ") music.ringTone(0) basic.pause(1000) })
Here are links to the four pieces of code above on microbit.org:
Player 1: https://makecode.microbit.org/_HR0DDXC4MVUC
Player 2: https://makecode.microbit.org/_FCKDYcfi333d
Player 3: https://makecode.microbit.org/_3zm9gUMsE4z7
Player 4: https://makecode.microbit.org/_4hsYcsMqVEUT
Links to other blogs I've written can be found here: jc2048 Blog Index
Top Comments