Navigation

Saturday, 25 March 2017

Turn your BBC micro:bit into a Piano!


Components needed for this project are:
  1. Micro:bit
  2. zbit:toolbelt 
  3. breadboard
  4. push button switch
  5. 10kohm pull up resistor
  6. jumper wires
  7. audio jack
  8. speaker
The zbit:toolbelt or any other breakout board such as the kitronik edge connector breakout is required to assess individual pins on the micro:bit. For this project, we will be playing 3 notes. Since the micro:bit has only two switches, we wire a third switch as shown below:

The audio jack is required to connect the micro:bit to the speaker through which we hear the note being played. The audio jack is connected to PIN_P4 of the micro:bit. The wiring is shown:


We will be playing the B, A and G notes: the B note has a frequency of 987.8Hz; the A note has a frequency of 880.0Hz; and the G note has a frequency of 784.0Hz. PWM is used to generate the frequency of these notes. Since these notes are all sine waves, a sine wave has to be generated for each of these notes at their frequency. The figure below shows a sine wave:


As shown from the figure above, a sine wave has varying amplitude at each time step. So we define an array that represents the amplitude (in percentage pulse width units) at each time step. For simplification, I use an array with a length of 16 and load it with values that are approximations of the digital value of the sine wave. Note that if higher order array length is used, the digital value will be more precise and continuous. The code representing this is given:

float B_wave = 987.8;
float A_wave = 880.0;
float G_wave = 784.0;
float sine_wave[] = {0.53,0.72,0.84,0.91,0.94,0.86,0.78,0.63,0.47,0.28,0.16,0.09,0.06,0.13,0.22,0.38};

Since each note has different frequencies, I use the wait_us() function to define the time in microseconds at which each value in the sine_wave array is outputted. This value is specified by:

where f is the frequency and is therefore different for each note. The code is given by:

if(uBit.buttonA.isPressed()) {
            speaker.period(1/B_wave);     // 987.8Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(63);
            } 
        }          

        else if(uBit.buttonB.isPressed()) {
            speaker.period(1/A_wave);     // 880.0Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(71);
            } 
        } 

        else if(sw3 == 0) {         // negative logic
            speaker.period(1/G_wave);     // 784.0Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(80);
            } 
        }

The code above is in a while loop. Though it is better to put the code in an Interrupt Service Routine (ISR) at a specified frequency, the above tested code still works okay. The code is written in C++ as shown on the mbed platform. The complete code is given:

#include "MicroBit.h"
DigitalIn   sw3(P0_4);              //MICROBIT_PIN_P3
PwmOut      speaker(P0_5);         //MICROBIT_PIN_P4

float B_wave = 987.8;
float A_wave = 880.0;
float G_wave = 784.0;
float sine_wave[] = {0.53,0.72,0.84,0.91,0.94,0.86,0.78,0.63,0.47,0.28,0.16,0.09,0.06,0.13,0.22,0.38};
MicroBit uBit;

int main(void) {
    uBit.init();
    uBit.display.disable(); //disables the display
    while (1) {

        if(uBit.buttonA.isPressed()) {
            speaker.period(1/B_wave);     // 987.8Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(63);
            } 
        }     

        else if(uBit.buttonB.isPressed()) {
            speaker.period(1/A_wave);     // 880.0Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(71);
            } 
        } 

        else if(sw3 == 0) {         // negative logic
            speaker.period(1/G_wave);     // 784.0Hz 
            for (int i=0; i<16; i++) {   
                speaker.write(sine_wave[i]);  
                wait_us(80);
            } 
        }

        else {
            speaker.write(0);
        }
    }
}


A video demonstration is shown:

No comments:

Post a Comment