# LilyPad ProtoSnap Plus Activity Guide

Pages
Contributors: MikeGrusin, Gella

## 9: Theremin Project

Now that you've explored all the boards on the LilyPad ProtoSnap Plus, it's time to start combining some of those skills into more interactive projects. The first project we'll explore is creating a musical 'instrument' inspired by a theremin. A theremin is an instrument played without needing physical contact, typically utilizing antennas. In this 'theremin', you'll use readings from the light sensor as a controller for frequency of tones produced by the buzzer. We can further categorize this project an opto-theremin because it is using light as a controller. In addition to the sensor and buzzer, we'll display the light readings on the bar graph and add the button as an on/off trigger.

### LilyPad Boards Used in This Activity

• LilyPad USB Plus's built-in LEDs

### New Concepts Introduced in This Activity

#### Using the Map() Function

The `map()` function is a handy function that translates one range of numbers into another range.

For example, let's say you wanted to use the light sensor (read by `analogRead()`) to control the brightness of a LED (written by `analogWrite()`), as we did back in activity 7. But the range of `analogRead()` (0 to 1023) doesn't match the range of `analogWrite()` (0 to 255).

This particular example is easy to solve in that you can simply divide the light sensor value by 4, as we did in activity 7. But it would take a bit more work to solve for arbitrary ranges that may not start at 0, as we'll be doing in this activity.

The `map()` function does this for you. The parameters it needs are:

`result = map(value,fromLow,fromHigh,toLow,toHigh);`

Where `fromLow` to `fromHigh` is the range of the original value, and `toLow` to `toHigh` is the range we want to translate that value into.

For example, here's how we'd set up `map()` for our earlier example:

`LEDbrightness = map(lightSensorValue,0,1023,0,255);`

It's worth mentioning that if `value` isn't within the original range, `map()` will still work; but it will return a result outside the target range. If your result must be within the target range, an additional function called `constrain()` can be used to ensure that:

`result = constrain(value,low,high);`

If `value` is already within the range of `low` to `high`, the `result` will be the same as `value`. But if `value` is outside that range, it will be set to `low` or `high`, whichever is closest. Our complete example would then look like:

`LEDbrightness = map(lightSensorValue,0,1023,0,255);`
`LEDbrightness = constrain(LEDbrightness,0,255);`

### Example Code

To open the code, go to:

File > Examples > LilyPadProtoSnapPlus > LPP_09_Theremin

You can also copy and paste the following code into the Arduino IDE. Hit upload, and see what happens!

``````language:c
/*
LilyPad ProtoSnap Plus Activity 9: Theremin
SparkFun Electronics
https://www.sparkfun.com/products/14346

A Theremin is an electronic musical instrument that is played by
moving your hands over it. In this activity we'll create a Theremin
using the light sensor and the buzzer.

******************************************************************************/

// Create variables for the pins we'll be using

int sensorPin = A2;
int buttonPin = A4;
int buzzer = A3;
int bargraphLED[6] = {15,16,17,18,19,20};

// Set the highest and lowest frequencies
// (Change these and see what happens)

int highestFrequency = 1047; // C6
int lowestFrequency = 523; // C5

void setup()
{
int x;

// Initialize the pins we'll be using

pinMode(sensorPin, INPUT);
pinMode(buzzer, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);

for (x = 0; x <= 5; x++)
{
pinMode(bargraphLED[x],OUTPUT);
}

// Initialize the serial monitor

Serial.begin(9600);
}

void loop()
{
int sensorValue;
int frequency;

// Read the sensor value (will be 0 to 255):

// Print out the sensor reading:

Serial.print("sensor value: ");
Serial.println(sensorValue);

// Display the sensor reading on the bar graph LEDs:

barGraph(sensorValue);

// Play a tone based on the light level:

// The light sensor will return a value from 0 to 1023,
// but we want to map this to a specific range of frequencies.
// We'll use a built-in fuction called "map" that transforms one range
// of values (0 to 1023) to another (lowestFrequency to highestFrequency):

frequency = map(sensorValue,0,1023,lowestFrequency,highestFrequency);

if (digitalRead(buttonPin) == LOW) // If the button is pressed:
{
tone(buzzer,frequency);
}
else
{
noTone(buzzer);
}
}

void barGraph(int value)
{
// Create a LED bargraph using value as an input.
// Value should be in the range 0 to 1023.

int x;

// Step through the bargraph LEDs,
// Turn them on or off depending on value.

// Value will be in the range 0 to 1023.
// There are 6 LEDs in the bargraph.
// 1023 divided by 6 is 170, so 170 will be our threshold
// between each LED (0,42,84, etc.)

for (x=0; x <= 5; x++)
{
if (value > (x*170) )
{
digitalWrite(bargraphLED[x], HIGH);
}
else
{
digitalWrite(bargraphLED[x], LOW);
}
}
}
``````

### What You Should See

To play the theremin, press and hold the button with one hand and cover the light sensor with the other. As the light sensor reads different light levels, the frequency on the buzzer will change and the bar graph LEDs will illuminate to show the different tones. Release the button to stop the sounds. You can also use a flashlight to shine more light on the sensor and get a large range of tones.

#### Program Overview

1. Create variables to store.
2. Read the value from the light sensor and store in `sensorValue`.
3. Print the light sensor reading to the serial monitor.
4. Display the sensor reading on the bar graph using a custom `barGraph()` function.
5. Map the values from the light sensor to the set range of frequencies (set in `lowestFrequency` and `highestFrequency`)
6. Check to see if the button is pressed. a. If it is, send a `tone()` command to the buzzer using the `frequency` value. b. If it is not, turn the buzzer off using `noTone()` command.
7. Repeat.

#### Code to Note

CodeDescription
`map(value,inMin, inMax, outMin, outMax)`
##### Mapping a Range of Values:
The `map()` function is a tool in Arduino used to take a value from one range and constrain it to another. This is especially helpful when you want to take readings from an analog input (which can range from 0-1023) and use them to display directly on an output (which ranges from 0-255). In previous examples, the code used division to make these two variables compatible. The map function is a little more sophisticated.

##### The map value has five parameters, listed below:

Value: `sensorValue`
The values/variable to map. In this code we are using the data we read from the light sensor stored in `sensorValue`.

inMin: 0
inMax: 1023
The minimum and maximum bounds of the value to be mapped. In this case, since `sensorValue` is from an analog sensor, these will be 0-1023.

outMin:`lowestFrequency`
outMax: `highestFrequency`
The minimum and maximum bounds you want to map the current value to. In this case, we've chosen a range of frequencies stored in the `lowestFrequency` and `highestFrequency` variables set at the beginning of the program.

Read more about the `map()` function on the Arduino Reference site.

### Coding Challenges

• Adjust `lowestFrequency` and `highestFrequency` to use different ranges for the tones the theremin creates.

• Try adding additional sound controls by adding another `if/else` statement using the switch. Suggestions: Turning a light pattern on/off that coordinates with the sound or switching between different frequency ranges.