Arduino Theremin with fixed scale

An Arduino Theremin with Fixed Scale

In this project, you’ll learn how to build a simple Arduino Theremin using a piezo buzzer and the HC-SR04 ultrasonic sensor. But that’s not all – to help even inexperienced musicians impress their friends, this Theremin only plays notes within a scale that you define.

In the following example, we’ll use the popular A minor pentatonic scale. However, you can define and use any other scale as well. More on that later.

Building the Arduino Theremin

You only need to mount two components on your breadboard and connect them to your Arduino: the piezo buzzer and the ultrasonic sensor. Connect the buzzer to digital pin 3 and ground. For the ultrasonic sensor, first connect VCC to positive and GND to negative. Then connect the TRIG output to digital pin 5 and ECHO to digital pin 4 on your Arduino.

Schematic for the Arduino Theremin

How Your Theremin Works

This project works quite simply: The HC-SR04 ultrasonic sensor measures the distance to an object, e.g., your hand. This distance is then converted into pitch values that are output through the piezo buzzer.

Since all possible distances within a certain range are possible, there are also all possible tones – including those that don’t appear in classical scales. That’s why the following sketch always selects the next lowest note in the A minor pentatonic scale. This makes it easy to play appealing melodies!

The Code for Musicians

First, define some variables and constants for your Arduino Theremin that you’ll fill with life later:

//Arduino pins for the ultrasonic sensor
int trigger = 5;
int echo = 4;
int distance = 0;

//Pin of the piezo buzzer
const int piezo = 3;

//Variables for determining the range
int distance = 0;
int distanceHigh = 0;

//Variable for the number of notes in the scale
int lengthOfScale = 0;

//Variable for the note to be played
int note = 0;

Then follows an array containing the notes of the A minor pentatonic scale – specifically, which frequencies (in Hertz) you output with your piezo buzzer. These are repeatedly the notes A – C – D – E – G in different octaves.

int scale[] = {
  147, 165, 196, 220, 262, 294, 330, 392, 440,
  523, 587, 659, 784, 880, 1047, 1175, 1319, 1568,
  1760, 2093, 2349
};

On Github, you can find a more or less complete list of all notes and their frequencies.

The Setup Function

Here you first start the Serial Monitor and set the PinModes for the ultrasonic sensor pins. Then comes the first interesting part of the sketch: Your sensor has a theoretical range of 2cm to 4m – though the range in which you’ll play will probably be smaller.

That’s why you calibrate the sensor in the first five seconds after startup. This means determining how far your hand will be from the sensor. For this, slowly move your hand away from the sensor about 40-50cm. The greatest distance is then stored in the variable distanceHigh. You’ll need this again later.

void setup() {
  Serial.begin (9600);
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  while (millis() < 5000) {

    //The sensor measures the distance
    digitalWrite(trigger, HIGH);
    digitalWrite(trigger, LOW);
    distance = pulseIn(echo, HIGH);
    Serial.println(distance);

    //Store the greatest distance
    if (distance > distanceHigh) {
      distanceHigh = distance;
    }
  }
}

___STEADY_PAYWALL___

Note: To be precise, the HC-SR04 doesn’t directly measure distance, but rather how long an emitted ultrasound signal takes to return to the sensor after hitting an obstacle (your hand).

Now you still need to determine how many notes are in the scale, i.e., in the scale[] array. You do this as follows:

for (byte i = 0; i < (sizeof(scale) / sizeof(scale[0])); i++) {
  lengthOfScale += 1;
}

The Loop

This is where the music plays. In the loop, you have your ultrasonic sensor repeatedly measure the distance to your hand. Depending on how far it is from the sensor, a different tone sounds. The following function is central to this:

note = map(distance, 250, distanceHigh, scale[0], scale[lengthOfScale - 1]);

Using the map() function, you can assign a number from one range to another range. In this project, you map the current distance of your hand (distance) to a specific tone frequency.

This requires two ranges: The current distance of your hand falls within the range of minimum (set to 250) and maximum (distanceHigh) distance that you determined during calibration. The second range is bounded by the lowest note (position 0 in the scale[] array) and the highest note (determined by scale[lengthOfScale – 1]) of your pentatonic scale in the scale[] array.

The distance of your hand is thus, in our specific case, assigned to a number between 147 and 2349 and stored in the variable note.

Finding the Right Note

So far you only have a frequency range in which the note lies. But your Arduino Theremin shouldn’t play imprecise intermediate tones, but only notes of the A minor pentatonic scale. For this you need a loop and a few queries:

    for (byte j = 0; j < (lengthOfScale); j++) {

    if (note == scale[j]) {
      tone(piezo, note);
      break;
    }
    else if (note > scale[j] && note < scale[j + 1]) {
      note = scale[j];
      tone(piezo, note);
      break;
    }
  }

In the For-Loop, you check for each note in the scale[] array whether you’ve already placed your hand at the right distance and thus hit the note exactly. In this case, you play it through the tone() function with your piezo buzzer.

If not, you check in the first else if whether your played tone is between two notes. If so, you simply play the lower of the two.

Too complicated? An example: Your hand has a distance to the sensor that you’ve converted using the map() function to a frequency of 200 Hertz. But this isn’t a note of the A minor pentatonic scale, but lies somewhere between a G and a G sharp. According to your array, the next lower, correct note is a G at 196 Hertz. And that’s what your Theremin plays.

And that’s it! Turn on your Arduino Theremin, calibrate it, and let it rip. 🙂

Here’s the complete sketch:

/*
   Arduino Theremin with A minor pentatonic scale
   en.polluxlabs.net
*/

const int trigger = 5;
const int echo = 4;

const int piezo = 3;

int distance = 0;
int distanceHigh = 0;

int lengthOfScale = 0;

int note = 0;

//A Minor pentatonic scale
int scale[] = {
  147, 165, 196, 220, 262, 294, 330, 392, 440,
  523, 587, 659, 784, 880, 1047, 1175, 1319, 1568,
  1760, 2093, 2349
};

//C Major scale
//int scale[] = {
//  131, 147, 165, 175, 196, 220, 247, 262, 294,
//  330, 349, 392, 440, 494, 523, 587, 659, 698,
//  784, 880, 988, 1047
//};


void setup() {
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  while (millis() < 5000) {
    digitalWrite(trigger, HIGH);
    digitalWrite(trigger, LOW);
    distance = pulseIn(echo, HIGH);

    if (distance > distanceHigh) {
      distanceHigh = distance;
    }
  }

  for (byte i = 0; i < (sizeof(scale) / sizeof(scale[0])); i++) {
    lengthOfScale += 1;
  }
}

void loop() {
  digitalWrite(trigger, HIGH);
  digitalWrite(trigger, LOW);

  distance = pulseIn(echo, HIGH);

  note = map(distance, 250, distanceHigh, scale[0], scale[lengthOfScale - 1]);

  for (byte j = 0; j < (lengthOfScale); j++) {

    if (note == scale[j]) {
      tone(piezo, note);
      break;
    }
    else if (note > scale[j] && note < scale[j + 1]) {
      note = scale[j];
      tone(piezo, note);
      break;
    }
  }
  delay(30);
}
We don't track you. Enjoy your cookies while making awsome electronic projects!