Musical Theremin
Description:
Create a simple smart theremin using mbed. Our approach includes the following features:
- Improve distance sensing sonar to play only designated pitches in a scale
- Add second sonar sensor to control volume
- Plot sine wave of pitch (frequency) vs amplitude (volume) in real time
- Print pitch (Hz) and volume (0-100%)
- Add Bluetooth to customize “smart” theremin
- Choose between major and minor keys
- Allows you to input your own name
Parts List:
- LPC1768 mbed: https://os.mbed.com/platforms/mbed-LPC1768/
- uLCD: https://os.mbed.com/users/4180_1/notebook/ulcd-144-g2-128-by-128-color-lcd/
- Potentiometer: https://os.mbed.com/components/Potentiometer/
- (2) HR-SR04 Sonar Sensors: https://os.mbed.com/users/4180_1/notebook/using-the-hc-sr04-sonar-sensor/
- Class D Audio Amp: https://os.mbed.com/users/4180_1/notebook/tpa2005d1-class-d-audio-amp/
- Speaker: https://os.mbed.com/users/4180_1/notebook/using-a-speaker-for-audio-output/
- Adafruit Bluefruit Bluetooth: https://os.mbed.com/users/4180_1/notebook/adafruit-bluefruit-le-uart-friend---bluetooth-low-/
- Simple breadboard
- External Power Supply
- 2 330uF capacitors
Schematic/Block Diagram:

Source Code:
musical
#include "mbed.h"
#include "ultrasonic.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#include <string>
// Define some colors:
#define Q 0x808000 //OLIVE
#define I 0x008000 //GREEN
#define S 0xC0C0C0 //SILVER
#define C 0x17202A //UFO GLASS
#define D 0x797D7F //DARK GREY
#define L 0x00FF00 //LIME
#define P 0xFF00FF //PINK
#define R 0xF1C40F //YELLOW
#define O 0xF39C12 //ORANGE
#define G 0xAAB7B8 //GREY
#define _ 0x000000 //BLACK
#define X 0xFFFFFF //WHITE
#define B 0x0000FF //BLUE
#define r 0xFF0000 //RED
PwmOut audio(p26); //output to speaker amp or audio jack
DigitalOut led(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
Serial pc(USBTX,USBRX);
uLCD_4DGL uLCD(p28, p27, p29);
Serial bluemod(p9,p10);
Mutex myMut;
volatile float half_cycle_time = 1;
volatile float secondhalf_cycle_time = 1;
//Buffers
volatile float half_cycle_time2;
volatile float half_cycle_time3;
volatile float half_cycle_time4;
volatile float half_cycle_time5;
volatile float half_cycle_time6;
volatile float half_cycle_time7;
volatile float half_cycle_time8;
volatile float half_cycle_time9;
volatile float half_cycle_time10;
volatile float secondhalf_cycle_time2;
volatile float secondhalf_cycle_time3;
volatile float secondhalf_cycle_time4;
volatile float secondhalf_cycle_time5;
volatile float secondhalf_cycle_time6;
volatile float secondhalf_cycle_time7;
volatile float secondhalf_cycle_time8;
volatile float secondhalf_cycle_time9;
volatile float secondhalf_cycle_time10;
// Averaging Values:
volatile float avghalf_cycle_time;
volatile float secavghalf_cycle_time;
volatile float pitch;
volatile int x = 1;
volatile int xpos = 1;
volatile int y = 120; // Offset to start printing sinusoid
volatile int color = WHITE;
volatile bool major = true; // play D Major = true
volatile bool cont = true; // continue with main thread once name inputted
volatile string name; // Holds person's name
volatile int scalar = 2;
string s;
//volatile
string key;
void Dmajor(void const *argument)
{
while(1) {
led = !led;
// Buffer for the key of D Major
if(avghalf_cycle_time>300/scalar&&avghalf_cycle_time<700/scalar) {
pitch = 1.0/294;
audio.period(pitch);//D
color = X;
} else if(avghalf_cycle_time>700/scalar&&avghalf_cycle_time<1100/scalar) {
pitch = 1.0/274;
audio.period(pitch);//c#
color = B; // blue
} else if(avghalf_cycle_time>1100/scalar&&avghalf_cycle_time<1500/scalar) {
if(major) {
pitch = 1.0/246;
audio.period(pitch); //B
color = I;
}
else if(!major) {
pitch = 1.0/232;
audio.period(pitch); //Bflat
color = I;
}
} else if(avghalf_cycle_time>1500/scalar&&avghalf_cycle_time<1900/scalar) {
pitch = 1.0/220;
audio.period(pitch);// A
color = R; //yellow
} else if(avghalf_cycle_time>1900/scalar&&avghalf_cycle_time<2300/scalar) {
pitch = 1.0/194;
audio.period(pitch); //G
color = O; // ORANGE
} else if(avghalf_cycle_time>2300/scalar&&avghalf_cycle_time<2700/scalar) {
if(major) {
pitch = 1.0/184;
audio.period(pitch); //f#
color = r; // red
}
else if(!major) {
pitch = 1.0/176;
audio.period(pitch); //fnat
color = r; // red
}
} else if(avghalf_cycle_time>2700/scalar&&avghalf_cycle_time<3100/scalar) {
pitch = 1.0/166;
audio.period(pitch); //E
color = G; // grey
} else if(avghalf_cycle_time>3100/scalar) {
pitch = 1.0/146;
audio.period(pitch); //D
color = Q; //OLIVE
}
// Buffer to control the volume
if(secavghalf_cycle_time>200&&secavghalf_cycle_time<300)audio = .001;
else if(secavghalf_cycle_time>200&&secavghalf_cycle_time<500) audio = .02;
else if(secavghalf_cycle_time>500&&secavghalf_cycle_time<800) audio = .03;
else if(secavghalf_cycle_time>800&&secavghalf_cycle_time<1100) audio = .05;
else if(secavghalf_cycle_time>1100&&secavghalf_cycle_time<1400) audio = .1;
else if(secavghalf_cycle_time>1400&&secavghalf_cycle_time<1700) audio = .2;
else if(secavghalf_cycle_time>1700&&secavghalf_cycle_time<2000) audio = .25;
else if(secavghalf_cycle_time>2000&&secavghalf_cycle_time<2300) audio = .4;
else if(secavghalf_cycle_time>2300&&secavghalf_cycle_time<2700) audio = .5;
Thread::wait(50);
}
}
void newdist(int distance)
{
//update frequency based on new sonar data
led2 = !led2;
half_cycle_time5 = half_cycle_time4;
half_cycle_time4 = half_cycle_time3;
half_cycle_time3 = half_cycle_time2;
half_cycle_time2 = half_cycle_time;
half_cycle_time = distance<<3;
avghalf_cycle_time = (half_cycle_time + half_cycle_time2 + half_cycle_time3 +half_cycle_time4 +half_cycle_time5)/5.0;
}
void secnewdist(int distance2)
{
led3 = !led3;
secondhalf_cycle_time5 = secondhalf_cycle_time4;
secondhalf_cycle_time4 = secondhalf_cycle_time3;
secondhalf_cycle_time3 = secondhalf_cycle_time2;
secondhalf_cycle_time2 = secondhalf_cycle_time;
secondhalf_cycle_time = distance2<<3;
secavghalf_cycle_time = (secondhalf_cycle_time + secondhalf_cycle_time2 + secondhalf_cycle_time3 +secondhalf_cycle_time4 +secondhalf_cycle_time5)/5.0;
}
//2 HC-SR04 Sonar modules
ultrasonic mu(p12, p13, .1, 1, &newdist); // Pitch
ultrasonic mu2(p14,p15,.1,1,&secnewdist); // Volume
void sonar1(void const *argument)
{
while(1) {
myMut.lock();
mu.checkDistance();
uLCD.locate(0,7);
myMut.unlock();
Thread::wait(1);
}
}
void sonar2(void const *argument)
{
while(1) {
myMut.lock();
mu2.checkDistance();
myMut.unlock();
Thread::wait(50);
}
}
void screen(void const *argument)
{
while(1) {
myMut.lock();
uLCD.text_height(2);
uLCD.locate(0,2);
uLCD.printf("Pitch(Hz): %3.1f",1/pitch);
uLCD.locate(0,4);
uLCD.printf("Volume(%%): %2.2f", ((float(audio))/.5)*100);
myMut.unlock();
Thread::wait(1000);
}
}
void sinusoid(void const *argument)
{
while(1) {
xpos = xpos+1;
x = xpos % (144);
y = 100 + 20*audio*sin(3.14*x/(pitch));
myMut.lock();
uLCD.filled_rectangle(x, 85,x+1 , 115, BLACK);
uLCD.pixel(x, y, color);
myMut.unlock();
Thread::wait(1);
}
}
int main()
{
audio = 0;
led = 0;
char Temp1;
char Temp2;
uLCD.baudrate(3000000);
uLCD.line(0,10,144,10,WHITE);
uLCD.line(0,14,144,14,WHITE);
uLCD.line(0,18,144,18,WHITE);
uLCD.line(0,22,144,22,WHITE);
uLCD.filled_circle(10,12,3,WHITE);
uLCD.filled_circle(20,10,3,WHITE);
uLCD.filled_circle(30,22,3,WHITE);
uLCD.filled_circle(40,20,3,WHITE);
uLCD.filled_circle(50,18,3,WHITE);
uLCD.line(0,110,144,110,WHITE);
uLCD.line(0,114,144,114,WHITE);
uLCD.line(0,118,144,118,WHITE);
uLCD.line(0,122,144,122,WHITE);
uLCD.filled_circle(60,120,3,WHITE);
uLCD.filled_circle(70,110,3,WHITE);
uLCD.filled_circle(80,115,3,WHITE);
uLCD.filled_circle(90,118,3,WHITE);
uLCD.filled_circle(100,121,3,WHITE);
uLCD.filled_circle(110,120,3,WHITE);
uLCD.locate(0,6);
uLCD.text_height(1);
uLCD.text_width(1);
uLCD.color(G);
uLCD.printf("Welcome to\n\nMBED Theremin!");
wait(5);
uLCD.cls();
uLCD.printf("\n\nPlease enter your name.\n\nPress:\n\n1) for minor \n\n2) for major\n\nPress the down key when finished.");
wait(3);
while(cont) {
char bnum=0;
char bhit=0;
if(bluemod.readable()) {
Temp1 = bluemod.getc();
if (Temp1 <= 'z' && Temp1 >= 'A') {
s.push_back(Temp1);
} else if (Temp1 == '!') {
Temp2 = bluemod.getc();
if (Temp2=='B') { //button data packet
bnum = bluemod.getc(); //button number
bhit = bluemod.getc(); //1=hit, 0=release
if (bluemod.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK?
switch (bnum) {
case '1': //button 1, number 1 (minor
if (bhit=='1') {
major = false;
key = "d minor";
}
break;
case '2': //button 2, number 2 (major
if (bhit=='1') {
major = true;
key = "D major";
}
break;
case '6': //button 6 down arrow
if (bhit=='1') {
uLCD.cls();
cont = false;
}
break;
} // end switch
}
}
} // End of button check
}//end if(readable)
}//end while
uLCD.locate(0,5);
uLCD.printf("You look great \ntoday, %s\n",s);
wait(5);
uLCD.locate(0,10);
uLCD.printf("Enjoy your\nnew theremin!");
wait(2);
uLCD.cls();
myMut.lock();
uLCD.locate(0,0);
uLCD.text_height(2);
uLCD.printf("Key: %s",key);
myMut.unlock();
mu.startUpdates();//start measuring the distance with the sonar
mu2.startUpdates();
Thread thread1(sonar1);
Thread thread2(sonar2);
Thread thread3(Dmajor);
Thread thread5(screen);
Thread thread6(sinusoid);
while(1) {
led4 = !led4;
Thread::yield();
}
}
Below is our project:
Import programFinalProjectRTOSVIII
Musical Theremin Using Bluetooth
Video Demo:
Images:

Please log in to post comments.
