04: Microcontroller Programming

This week, I wanted to start making progress towards my final project by exploring how we can make theremin-like systems using the Arduino. This happened over three iterations, ending with a basically functional theremin with volume and pitch control. Click the links below for details:

Misc : Soldering and 3D Printing

3D Printing

In lab, we were able to experiment with printing a tiny part (~5 min) on the 3D printers. I looked around Thingiverse for a bit for a cool-looking model, and eventually settled on this infinity cube by Eevee97. However, to make it print in 5 minutes, I just scaled it down to like 10% scale.

My first soldering job.

I thought that maybe there were 80/20 odds that scaling it down so much would really break something. Turns out, the odds were more like 100/0, and the result was basically a rectangle:

My first soldering job.

The moral of the story: you can't just rescale things whenever you want.

Soldering

I also ended up doing a little soldering. Initially I was trying to follow the Arduino theremin tutorial, and so I grabbed a part called a piezo that had two small wires at the end that wouldn't fit into the premade sockets. To fix this, I finally tried out soldering (I didn't get around to it last week), and was pleasently surprised that nothing went terribly wrong:

My first soldering job.

However, though the soldering was a success, I had still made a critical mistake---it turns out that the "piezo" that I had found was completely unrelated to the piezo part that I actually needed, which was more like a speaker. Bobby helped me figure this out pretty fast, but now I just have this component connected to two wires, and I don't even know what the component is. You win some, you lose some.

Arduino Tutorial : Light Theremin

This week, I wanted to focus on getting better at electronics and to start figuring out what making a theremin circuit could look like. As a jumping-off point, I saw a tutorial in the Arduino projects book for a "light theremin," which uses a photoresistor to control the pitch of a speaker.

The circuit is super simple. You have a speaker which is plugged into some PWM pin and to ground, and you have a photoresistor connected in the same way. The idea is that when the photoresistor sees different amounts of light, it should send a different signal, which we can map to pitch frequency.

My first soldering job.

The circuit and code didn't too tricky, but I still got pretty terrible results that sounded mostly like noise :

There are a few possible reasons for this. For one, I definitely could've messed something up that I didn't think of. Still, another reason for this that I noticed was basically that the photoresistor was super sensitive to light---like any light it saw would give the max signal, and then even completely covering it with a dark material would only lower its signal to like 80% it's max value. This made it pretty much impossible to use as a theremin analogue, so I decided to ask Bobby for some advice for the next iteration.

Ultrasensor Theremin V1

Bobby told me about this component called an Ultrasonic Sensor, which can calculate to reasonably high precision the distance from a sensor to the nearest solid object. It works by having two microphones---one sends out a super high-frequency signal, and the other listens to hear it reflected back, recording how long it takes to hear something along the way. This path basically gives you an equilateral triangle with a known base (the microphone distance) and a time signal proportional to the path travelled (two times the other side length). So we can do some basic algebra to get out the distance in whatever units we want.

Bobby's idea was to substitute the photoresistor from the Arduino tutorial with one of these ultrasensors and see what happens. After doing that, we had a much better theremin:

There were still a few small issues---most notably, there's a lot of noise in the distance measurements, so you don't get a consistent tone if you hold your hand in the same place. I think this could be pretty easily solved by averaging over multiple signals to decide the tone, but I didn't have time to implement it this week. More pressing was that this still isn't a full theremin---for that, I need to be able to control both pitch and volume.

Ultrasensor Theremin V2 : 2-Ther-e, 2-Min

The general idea behind getting the second theremin to work was pretty simple---pop in a second ultrasensor in a different place, and map its signal to volume. Luckily, unlike the standard tone function, there's a library called Volume3 by Connor Nishijima that lets you set both the pitch and volume of an output speaker. Here's what my hardware setup looked like:

My final theremin prototype.

I tried for a while to parallelize the process of sending and receiving the ultrasensor signals by putting them into a class and creating "phases" where they wait until a certain time has passed to perform the next operation, but this didn't really work. Eventually, I just tried to make a basic ultrasensor class and read each of there signals in the inner loop, and this worked totally fine. I guess that they're sent and received fast enough that it doesn't matter whether they're parallelized. Another lesson: always try the dumb solution first in case it just works out.

The only other notable change is that I was able to get a more smooth transition between pitches by changing the map function to work on floats. Ints basically truncated all the information between scales, so I could only map very specific distance measurements to pitches. Finally, I just found a box of clay in one of the bins and taped an ultrasensor to each side to get my final prototype:

A planet gear.
A gear sliding across a rack.
A gear sliding across a rack.

Here's my final code:

                
#include "Volume3.h" // Include the Volume library
#define speakerPin 9

// Ultrasensor class
class ultraSensor {
    int trigPin;
    int echoPin;
    // Tells where we should stop growing the signal (sensitivity parameter)
    float highBound;

    public:
    ultraSensor(int tPin, int ePin, float bound) {
        trigPin = tPin;
        echoPin = ePin;
        pinMode(trigPin, OUTPUT);
        pinMode(echoPin, INPUT);
        highBound = bound;
    }

    // Collect a distance reading from the ultrasensor, in cm (I think)
    float readTone() {
        digitalWrite(trigPin, LOW);
        delayMicroseconds(2);
        digitalWrite(trigPin, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);
        float duration = pulseIn(echoPin, HIGH);
        float distance = duration * .034  / 2;

        // Truncate the reading above a certain threshold
        if (distance > highBound) {
        distance = highBound;
        }

        return(float(distance));
    }
};

// This is just map, but it doesn't truncate floats
long float_map(float x, float in_min, float in_max, float out_min, float out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// Define one ultrasensor looking for pitch, and another looking for volume
float volBound = 10.0;
float pitchBound = 50.0;
ultraSensor pitchSensor(3,5, pitchBound);
ultraSensor volSensor(10, 11, volBound);

void setup() {
}

void loop() {
    // Grab the distance measurements from each sensor
    float pitch_raw = pitchSensor.readTone();
    float vol_raw = volSensor.readTone();

    // Map each distance to a valid pitch or volume
    float pitch = float_map(pitch_raw, 0, pitchBound, 200, 1500);
    float volume = float_map(vol_raw, 0, volBound, 0, 1023);

    vol.tone(speakerPin, pitch, volume);
    // vol.tone doesn't have a time parameter like the normal tone() function, so you have to add some delay for the sound to persist
    delay(2);
}
                
            

Theremin in Action

We now have a (mostly) functional theremin! Here's a video of me trying to play mary had a little lamb :

And here's Chris trying much more successfully to play the Blue Danube :

Anyways, that's it for this week. Overall I feel pretty satisfied with how it turned out! It's worth noting, though, that this is actually really unlike how a real theremin functions---I still have a lot left to learn about that actual cirucit. See you next week.

—Thomas