PComp | Week 5 | Theremin v.3
This is my 3rd week working on this (see previous week and the previous previous post).
Last week I managed to somewhat smoothen the value given by the IR sensor, and controlled the value using a pot and Volume 3 library.
This was my "note to self" for next iteration:
Consider using other sensors, maybe stretch sensor to get more stable output
Try using another value-smoothing algorithm
Have another sensor as vibrato control
Consider using mp3 shield to output less-annoying sounds
Here was my approach to improving the theremin* with what we learned in class.
Get new sensors: I bought short-distance IR sensor (8-30cm) for hopefully more stable values & photo cell meant for vibrato control.
Apply new sensors to existing build from last week, make sure the values are smooth and I can get glissando pitch output.
Use mp3 shield to play background music, so that the theremin* does not sound that annoying.
Theremin v3.1 (First attempt)
Function 1: Theremin simulator
New IR short distance sensor
Simple averaging/smoothing algorithm to avoid program getting stuck in a “for loop”
Photo cell as vibrato control
#include <TimerOne.h> #include <Volume3.h>
#define photoPin A1 #define IRPin A5 #define potPin A0 #define speakerPin 3
int photoRead = 0; int prevPhoto = 0; int diff = 0;
int IRRead = 0; int avgIR = 0;
int potVal = 0; int volume = 0;
int pitch = 0;
void setup() { Serial.begin(9600); }
void loop() {
photoRead = analogRead(photoPin); IRRead = analogRead(IRPin); potVal = analogRead(potPin);
volume = map(potVal, 0, 1023, 0, 1000); diff = photoRead - prevPhoto; //Serial.println(diff); prevPhoto = photoRead;
avgIR = 0.95 * avgIR + 0.05 * IRRead; //Serial.println(IRRead);
//diff = 0; pitch = avgIR + (diff*3); pitch = 800 - pitch;
Serial.println(IRRead);
//vol.tone(speakerPin, pitch, volume); tone(speakerPin, pitch); delay(5); }
But unfortunately when I ran pot and photocell together the photocell became very erratic. In the video it can be seen that the photocell fluctuates when I turn up the pot. I suspected that having so many things connected to the breadboard / arduino kind of messes with the current / voltage. I could not figure it out :(
I ended up factoring out the photocell values / vibrato.
Theremin v3.2 (Second Attempt)
Function 1: Theremin simulator
IR short distance sensor -> pitch control
pot -> volume control
photo cell -> vibrato control
Function 2: Background music player
DFPlayer Mini + speaker -> background music output
switch -> background music control (pause & play)
LED -> indicator of playing / not playing music
Building this was a nightmare....
There were so many inputs and outputs it was super hard to troubleshoot. My first speaker was busted (I think cos of the overclocking by Volume 3 library). My breadboard was so small often I got confused which pin connects to which. Even my button switch behaved erratically and I had to debounce it with a crude delay function.
And here's the source code:
#include <SoftwareSerial.h> #include <DFRobotDFPlayerMini.h> #include <TimerOne.h> #include <Volume3.h>
#define photoPin A1 #define IRPin A5 #define potPin A0 #define speakerPin 9 #define buttonPin 8 #define ledPin 7
#define C4 261 #define C5 523 #define C6 1046
SoftwareSerial mySerial(10, 11); //RX TX Pins DFRobotDFPlayerMini myDFPlayer;
int photoRead = 0; int prevPhoto = 0; int vibrato = 0;
int IRRead = 0; int avgIR = 0;
int potVal = 0; int avgPot = 0; int volume = 0;
int pitch = 0;
int switchState = 0; boolean isPlaying = false; boolean isFirstTime = true;
void setup() { mySerial.begin(9600); Serial.begin(9600);
Serial.println(); Serial.println(F("DFRobot DFPlayer Mini Demo")); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); while (true); } Serial.println(F("DFPlayer Mini online."));
pinMode(buttonPin, INPUT); pinMode(ledPin, OUTPUT);
myDFPlayer.enableLoop(); //enable loop }
void loop() {
//THEREMIN FUNCTIONS // 1. Read all the inputs photoRead = analogRead(photoPin); IRRead = analogRead(IRPin); potVal = analogRead(potPin);
// 2. Vibrato control value (photo cell) vibrato = photoRead - prevPhoto; if (vibrato > 10 || vibrato < -10) { vibrato = 0; } prevPhoto = photoRead; //Serial.println(vibrato);
// 3. Smoothing IR & Pot Values avgIR = 0.95 * avgIR + 0.05 * IRRead; avgIR = constrain(avgIR, 170, 540); //Serial.println(avgIR); avgPot = 0.99 * avgPot + 0.01 * potVal;
// 4. Theremin output logic (Volume & Pitch)
avgPot = constrain(avgPot, 100, 1000); volume = map(avgPot, 100, 1000, 0, 1000); //Serial.println(volume);
pitch = map(avgIR, 170, 540, 0, C6 - C5); pitch = C6 - pitch; //pitch += vibrato * 10;
vol.tone(speakerPin, pitch, volume); //tone(speakerPin, pitch); //Serial.println(pitch); delay(1);
//BG MUSIC PLAYER switchState = digitalRead(buttonPin); //Serial.println(switchState);
if (switchState && isFirstTime) { myDFPlayer.volume(20); //Set volume value. From 0 to 30 myDFPlayer.playMp3Folder(2); digitalWrite(ledPin, HIGH); Serial.println("HELLO"); isFirstTime = false; isPlaying = true; delay(2000); switchState = LOW; }
if (switchState && isPlaying && !isFirstTime) { myDFPlayer.pause(); digitalWrite(ledPin, LOW); isPlaying = false; Serial.println("PAUSING"); delay(2000); switchState = LOW; }
if (switchState && !isPlaying && !isFirstTime) { myDFPlayer.start(); digitalWrite(ledPin, HIGH); isPlaying = true; Serial.println("RESUMING"); delay(2000); switchState = LOW; } }