The following information is a single lesson in a larger project. Find more great projects here.

Lesson Overview:

Now we'll write the code for the theremin!

Step 1: Introduction

In this lesson you will learn how to write the code that connects the photoresistor to the piezo buzzer. To change the pitch on the buzzer, you will use a new command called the tone() function, which changes its vibration frequency.

You will also write a calibration sequence in the setup() function that lets you adapt to different room lighting conditions.

Our version of the Light Theremin circuit is already in the Workplane. Let's get started!

1. Continue to the next step.

Step 2: Create Sensor Calibration Variables

In the header of the code, create a variable to hold the analogRead() value from the photoresistor.

Next you will create the sensor calibration variables. These store the highest and lowest values that the sensor can produce under current lighting conditions.

1. Copy the code below into the Code Editor. int sensorValue; int sensorLow = 1023; int sensorHigh = 0;
2. You’re going to set the initial value in the sensorLow variable to 1023, and set the value of the sensorHigh variable to 0.
3. When you first run the program, you’ll compare these numbers to the sensor’s readings to find the real maximum and minimum values (we'll go over this again in the Use it! step).
4. Continue to the next step.

Step 3: Define an LED Indicator

In the header, you will also create a constant named ledPin. You’ll use this as an indicator that your sensor has finished calibrating. In this project, use the on-board LED connected to pin 13.

1. Copy the code below into the Code Editor. const int ledPin = 13;

2. Continue to the next step.

Step 4: Start the Setup() Function

The entire setup() function will be used to calibrate your sensor. setup() will run for 5 seconds and during this time you will wave your hand over the light sensor to establish the minimum and maximum possible sensor values.

First, you will define ledPin (the on-board LED) as an OUTPUT and set it HIGH so the light is on when setup() starts running.

1. Copy the code below into the Code Editor. void setup(){ pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH);

2. Continue to the next step.

Step 5: Start Serial Communication

Since the piezo buzzer in the editor doesn't actually make any noise, you'll be watching the change in pitch in the serial monitor window! Initialize the serial communication with the Serial.begin() command.

1. Copy the code below into the Code Editor. Serial.begin(9600);

2. Continue to the next step.

Step 6: Use a Loop for Sensor Calibration

In order to create a 5 second calibration sequence, you’ll use a while() statement to run a loop for exactly 5 seconds. In your program, the while loop will start running when the program is uploaded, then continue running for 5000 milliseconds (5 seconds).

Since we only need to run the calibration sequence once, this while loop will go in our setup() function.

1. Copy the code below into the Code Editor. while(millis() < 5000) {
2. millis() function simply reports how long the Arduino has been running since it was last powered on or reset, in units of milliseconds.
3. while() loops run continuously until the condition in parentheses is met. In this case it will run while the total elapsed time is less than 5 seconds (or 5000 milliseconds), then move onto the next section of code.
4. Continue to the next step.
5. Stuck? HINT: The while() loop runs the code between the {curly brackets} for as long as the arguments in the while() loop are true.

Step 7: Calibrate the Sensor

In the while() loop, you’ll read the value of the sensor. In the Use it! section, we'll ask you to wave your hand in front of the sensor during this time to create a range of input values.

1. If the value is less than sensorLow (initially 1023), you’ll update the value of sensorLow to a lower value. If it is greater than sensorHigh (initially 0), sensorHigh gets updated to define a new maximum value.
2. Copy the code, including comments, into the Code Editor. sensorValue = analogRead(A0); if (sensorValue > sensorHigh){ sensorHigh = sensorValue; } //end if() if (sensorValue < sensorLow) { sensorLow = sensorValue; } //end of if() statement } //end while() loop
3. Continue to the next step.

Step 8: Indicate End of Calibration

The while() loop will end after 5 seconds have passed. Turn off the LED attached to pin 13.

1. Copy the code into the Code Editor. This is the end of the setup() function. digitalWrite(ledPin, LOW); //turn LED off } //end setup()

2. Continue to the next step.

Step 9: Read and Store Sensor Value

In this step you will start the the loop() function. Read the the photoresistor's input value on pin A0 and store it in sensorValue.

1. Copy the code below into the Code Editor. void loop() { sensorValue = analogRead(A0);

2. Continue to the next step.

Step 10: Map Sensor Value to Frequency

Create a variable named pitch. The value of pitch is going to be mapped from sensorValue, using sensorLow and sensorHigh as the bounds for the incoming values. To start out, use a pitch range of 50 to 4000 Hz.

The map function should look familiar, since it was used in theMood Cue project. More information is provided below.

1. Copy the code below into the Code Editor. int pitch = map(sensorValue, sensorLow, sensorHigh, 50, 4000);
2. Recall that sensorLow is the lowest possible input value you established during photoresistor calibration. sensorHigh is the highest possible value.
3. The map() function takes five arguments: the value being mapped (sensorVal), the input range of values (sensorLow and sensorHigh), and the output range of values (50 and 4000 Hz).
4. Continue to the next step.

Step 11: Play the Frequency

Next call the tone() function to play a sound.

In this project we will give tone() three arguments: what pin to play the sound on (in this case pin 8), what frequency to play (determined by the pitch variable), and how long to play the note (try 20 milliseconds to start).

1. Copy the code below into the code editor. tone(8, pitch, 20);

2. Continue to the next step.

Step 12: Display the Frequency

You will also use the Serial Monitor to watch the change in pitch as you interact with the photoresistor. Use the Serial.print() and Serial.println() functions. Finish off the program with a 10 millisecond delay to give the tone a little time to play.

1. Copy the code below into the Code Editor. Serial.print("pitch: "); Serial.println(pitch); delay(10); } //end loop()

2. The information in the serial monitor will look like this (for example): pitch: 50

3. Continue to the next step

Step 13: Use It! (simulator)

The buzzer does not make any noise in the simulator, so you will need to open up the Serial Monitor to observe the change in pitch as you slide the photoresistor icon back and forth.

1. Upload and run the code!
2. When you first run the program, there is a 5 second window for you to calibrate the sensor. To do this in the editor, click on the photoresistor to reveal the slider bar. Quickly slide this back and forth to set the minimum and maximum sensor levels.
3. If you miss the calibration period, that's ok! You can just Upload & Run the code again to reset the program.
4. When the calibration light turns off, move the slider on the photoresistor and watch the pitch values change in the Serial Monitor.

Step 14: Use It! (Arduino Kit)

Running the program on the Arduino kit is much more fun, since you get to make some noise with the piezo!

2. There is a 5 second window for you to calibrate the sensor. To do this, move your hand up and down over the photoresistor, changing the amount of light that reaches it. The closer you replicate the motions you expect to use while playing the instrument, the better the calibration will be!
3. After 5 seconds, the LED on the Arduino will turn off. When this happens, you should hear some noise coming from the piezo!
4. If you missed the calibration window, you can hold and release the reset button on the Arduino Uno, and try again.
5. Play your light theremin by moving your hand over the sensor!
6. Continue to the next step.

Step 15: Customize It

The range in the map() function that determines the pitch is pretty wide: 50 to 4000 Hz.

Try changing the frequencies to find ones that are right for your own musical style. Can you make something that sounds like a science fiction movie theme?

An example block of code with different sound frequencies is shown below.

1. Continue to the next step.

Step 16: Review

In this project, you learned how to use the tone() function to control the pitch frequency on a piezo buzzer. The tone() function operates very much like the PWM in analogWrite() but with a couple significant differences.

In analogWrite() the frequency is fixed; you change the ratio of on/off time during one pulse to vary the duty cycle.

With tone() you’re still sending pulses, but changing their frequency (or number of pulses per second). tone() always pulses at a 50% duty cycle: half the time the pin is high, the other half the time it is low.

Congratulations on creating your first musical instrument! Have fun creating spooky sounds with your project.

Check out other great projects here.