Digital Sandbox Arduino Companion

Pages
Contributors: jimblom
Favorited Favorite 5

Introduction

The Digital Sandbox is a learning platform that engages both the software and hardware worlds. It’s powered by a microcontroller that can interact with real-world inputs – like light or temperature sensors – while at the same time controlling LEDs, motors, and other outputs. The Digital Sandbox is equipped with everything, on board, that you will need to complete 13 experiments including controlling an LED, measuring how loud things are, detecting the temperature is, and more. Think of this as a SparkFun Inventor’s Kit all in one board!

DS graphic

In our original Digital Sandbox Experiment Guide, we show you how to program the Sandbox using a simple, graphical programming language called ArduBlock. This time around, we'll show you how to ditch the graphical editor in favor of actual Arduino code.

If you're looking to get into Arduino coding, this is a great place to begin. Grab a Sandbox and get started!

Experiment List (Table of Contents):

We'll explore the Sandbox through a series of progressive experiments. If you've already completed the Sandbox Experiment Guide, this list will look familiar. We've recreated the code from those experiments using Arduino code instead of ArduBlock.

After setting up your Sandbox, here's what's covered:

  1. Setup and Loop
  2. Exploring Blink
  3. Multi-Blink
  4. Dimming (the Hard Way)
  5. Dimming (the Easy Way)
  6. Color Mixing
  7. Number Storage with Variables
  8. If This Then That
  9. The Reaction Tester
  10. Serial Calculator
  11. Do the Analog Slide
  12. Automatic Night Light
  13. Thermal Alert!
  14. Sound Detecting
  15. Opto-Theremin (Addon)
  16. Serial Motoring (Addon)
  17. Servo Sweeper (Addon)

Click here to download a complete set of example code.

Need a little quick reference guide to Arduino? Download our Digital Sandbox / Arduino Quick Reference (Cheatsheet)

What is the Digital Sandbox?

The Digital Sandbox is a learning platform that engages both the software and hardware worlds. It's powered by a microcontroller that can interact with real-world inputs -- like light or temperature sensors -- while at the same time controlling LEDs, motors, and other outputs.

By interfacing the Sandbox to your PC or Mac via a USB cable, the Sandbox can be programmed using the popular Arduino programming environment.

The Anatomy of the Digital Sandbox

The Digital Sandbox includes a variety of on-board inputs and outputs that are commonly encountered in the world of electronics. Here is an overview of what's included on the board:

Annotated image of the Digital Sandbox

  1. USB Mini-B Connector -- Used to connect to a computer.
  2. JST Right-Angle Connector -- Used to supply power to the board.
  3. Slide Switch for Charging -- Used to charge a lithium polymer battery that is plugged into the two-pin JST connector, while the Digital Sandbox is connected to a computer and the slide switch is in the "ON" position.
  4. Reset Button -- This is a way to manually reset your Digital Sandbox, which will restart your code from the beginning.
  5. Slide Switch (Pin 2) -- On or off slide switch.
  6. LEDs (Pins 4-8) -- Use one or all of the LEDs (light-emitting diodes) to light up your project!
  7. LED (Pin 13) -- Incorporate this into your sketch to show whether your program is running properly.
  8. Temperature Sensor (Pin A0) -- Measures ambient temperature.
  9. Light Sensor (Pin A1) -- Measures the amount of light hitting the sensor.
  10. RGB LED (Pins 9-11) -- RGB (red/green/blue) LEDs have three different color-emitting diodes that can be combined to create many colors.
  11. Slide Potentiometer (Pin A3) -- Change the values by sliding it back and forth.
  12. Microphone (Pin A2) -- Measures how loud something is.
  13. Push Button (Pin 12) -- A button is a digital input. It can be either “on” or “off.”
  14. Add-on Header (Pin 3) -- Three-pin header for add-ons. Example add-ons are servos, motors and buzzers.

Digital Sandbox Baseplate Setup

Secure the Digital Sandbox board to the baseplate with the included Phillips-head screws.

baseplate assembly

Finger-tighten the screws for easy removal later.

Setup

If this is your first time using the Sandbox, we highly recommend checking out the parallel Digital Sandbox Guide for ArduBlock. That tutorial serves as a great introduction to programming -- it eases you into the programming world with a more simple, graphical language: ArduBlock. With ArduBlock you don't have to worry about misplacing semi-colons or making spelling mistakes, just drag and drop blocks to create your program.

If you're ready for the big show, though, follow along from here. On this page we'll get you set up with Arduino, from there it's on to the experiments.

Sandbox Setup & Driver Installation

If you haven't already, you'll need to install the driver for your Digital Sandbox. Begin by connecting the Digital Sandbox to your computer.

Sandbox connected to computer

Once the board is connected, you will need to install drivers. Please go to www.sparkfun.com/ftdi for instructions specific to your operating system.

Arduino Setup

Note: This tutorial assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE.

If you have not previously installed an Arduino library, please check out our installation guide.

After you've installed Arduino, when you get to the board selection step, set your Board type to Arduino Pro or Pro Mini 3.3V/8MHz.

alt text

And don't forget to set your Serial Port as well, under the "Tools > Serial Port" menu.

In addition, you may want to download all of the example sketches ahead of time. We have created a ZIP folder with every experiment sketch in this tutorial.

Unzip it and store it somewhere memorable. You can also access the most up-to-date code in the GitHub repository.

0. Setup and Loop

We'll start off as basic as possible. This experiment introduces the Arduino bare bones -- the least amount of code required to create any Arduino sketch. Every Arduino sketch is different, but they all include these few lines of code. Time to setup and loop!

Active Parts

Experiment 0 active parts

Code Components

In each experiment we'll introduce one or two new Arduino-language concepts, and provide a brief overview of how and why we use them. This time, we're introducing setup(), loop(), comments, and some syntax.

Setup: void setup() { }

This is a declaration for a function called "setup". This exact line is required in every Arduino sketch ever.

Any code that lives inside setup()'s curly brackets ({ and }) runs once at the very beginning of your program and then never again -- at least not until you reset the Arduino, or upload new code.

The setup() function is useful for ... setting up your Arduino for the rest of the program. You'll discover it's power in future experiments. Learn more about setup() in Arduino's reference guide.

Loop: void loop() { }

Like the setup line before it, this is another required Arduino-sketch function. While the setup() function sets your Arduino up, the loop() function...loops!

This is where the bulk of your Arduino sketch is executed. The program starts directly after the opening curly bracket (}), runs until it sees the closing curly bracket (}), and jumps back up to the first line in loop() and starts all over.

The loop() function will run over-and-over-and-over until the Arduino is reset.

You can learn more about loop in Arduino's reference guide.

Comments

You'll get used to these -- and tired of reading them -- by the time you're done experimenting. Comments are lines or blocks of text in your code that are there solely for you or anyone reading your code. Comments have no effect on the operation of your Arduino sketch, but they can be immensely helpful in deciphering what you or someone else was thinking when that line of code was written.

Comments come in two flavors: single-line and multi-line. A single-line comment is any line in your code preceded by two forward slashes (//). For example:

language:c
// This is a single-line comment.
code_goes_here();

A multi-line comment can be used to comment out one or more lines. Multi-line comments are initialized with a slash-asterisk (/*) and they're completed with an asterisk-slash (*/). For example:

language:c
/* This
        is
            a  
                multi-line
                    comment */
code_goes_here();

Comments have no effect on your code's operation -- they're completely ignored by the Arduino itself -- but they are very useful to you and anyone else reading your code. Always comment your cod!

Syntax: Semicolons (;), Parenthesis (( )), Curly Brackets ({ }), Oh My.

The syntax of a programming language is analogous to the grammar rules that define our sentence structure. Just as every sentence you write requires a punctuation mark (., ?, !), (almost) every line of code your write in Arduino must end with a semicolon (;). We'll see those soon, don't worry!

More apparent in this example is the parenthesis and curly brackets. Whenever you open a parenthesis or bracket (( or {) you have to close it as well () and }).

You'll discover where parenthesis and brackets are required in your code as you gain more experience. Brackets can be used to contain the entirety of loops, functions, and conditional statements. Parenthesis can be used to define a conditional test, function parameter, or mathematical order of operations. That's probably all gobeldy-gook right now, but take comfort in knowing that you'll understand what all of that means by the time you've explored all of these experiments.

One last thing you'll have to get used to is case-sensitivity. Everything in Arduino is case sensitive: function, variable, library names, you name it. If the first letter is capitalized in the example code, make sure you capitalize it in your code! If you accidentally capitalize something the program editor will let you know with an error.

Fortunately, those errors are usually pretty easy to trace, just look at the line it's pointing you to.

The Sketch and Experiment

Here's the sketch for this experiment. It's about as simple as it gets, and introduces some of the most fundamental components of any Arduino sketch. You can copy the code below into your Arduino IDE, or alternatively, open the Sandbox_00_Setup_Loop.ino sketch from the ZIP file you downloaded previously.

language:c
    // Sandbox 00: Setup and Loop

/* This example serves as an introduction to the Digital Sandbox, Arduino code,
   and physical computing.

   This experiment introduces setup(), loop(), and some basic syntax.

   Oh, by the way, this is a comment (as is the very top line of this sketch).
   Any text that lives between a SLASH-ASTERISK and a ASTERISK-SLASH is a 
   multi-line comment. Comments are useful bits of text that are completely 
   ignored by the Arduino. They help you, or others, read your code, but they 
   have no effect on what the code does. There are no syntax rules or even 
   spell-cheking in comments.

   This is a multi-line comment -- anything within the slashes and asterisks
   will be commented out.
*/

// And this is a single-line comment. Any line preceded with TWO FORWARD SLASHES
// is commented out.


/* void setup() is required in every Arduino sketch. Code within the { and } of
   setup runs ONCE and ONLY ONCE at the very beginning of your program. This
   function is useful for one-time "setup" lines of code that prepare your 
   Arduino to run the rest of its program.
*/
void setup()    // Don't forget the ( and ) directly after the word setup.
{   // As you'll discover throughout your programming experience every {...


// (code would go here, if we were so inclined.)


}   // ...requires a mating }.


/* void loop() is also REQUIRED in every program. It contains the bulk of your 
   Arduino program.

*/
void loop() // Don't forget the parenthesis -- and the "void" before.
{
    // 1: The loop would start here...
    // 2: Then run this line of code...
    // 3: Then this one...
    // ...
    // ...
    // End of loop: Then this one, then jump back up to the first line of loop.
}

Copy the code above and paste it into your Arduino IDE. Alternatively, open the Sandbox_0_Setup_Loop_Blink.ino that you downloaded in the ZIP previously.

alt text

Then, select your Serial Port and Board, and click Upload (the right-facing arrow up top).

It turns out, this code doesn't do very much. After uploading it to your Sandbox, you won't actually see anything happen. That's OK, there's still a lot to learn!

Well, that last experiment was certainly boring. Let's make the Sandbox actually do something. When faced with a new platform, a coder’s first task is to write a “Hello, world” program. Usually a “Hello, world” program actually prints those comforting words on a screen. The Digital Sandbox doesn’t give us a screen to print words on, but we do have LEDs! Wonderful, blinky, bright, shiny LEDs. Instead of printing words, let’s blink an LED to say “Hello, world.”

Background Information

This experiment digs into the anatomy of the blinking an LED. We can customize an LED blink with a combination of two Arduino functions -- digitalWrite([pin], [HIGH/LOW]) and delay([milliseconds]).

This experiment introduces the concept of digital output. We call the Sandbox's LEDs "outputs," because it's an effect that the board produces.

The term "digital" means that the output can only take one of two states: ON or OFF. We may also refer to those two opposing states as HIGH/LOW or 1/0. When an output connected to an LED is HIGH, the LED turns on. When the output is LOW, the LED turns off.

Active Parts

alt text

Code Components

This time we introduce two functions required to turn an LED on and off.

Digital Output: digitalWrite([pin], [HIGH/LOW])

The digitalWrite() function is used to set the digital state of a pin to either HIGH or LOW.

We'll learn more about the first parameter -- pin -- in the next experiment. For now, we're simplying toying around with the second parameter -- the digital value of the pin.

When a pin goes HIGH, it outputs a voltage of about 5V. That's enough energy to push current through an LED and illuminate it. When a pin goes LOW, it outputs 0V. In this configuration, that doesn't allow any power to run through the LED, so it turns off.

Millisecond Delay: delay([ms])

The Digital Sandbox runs code so fast that sometimes we need to slow it down with a delay. This function will halt the Sandbox from doing anything for a specified number of milliseconds. There are 1000 milliseconds (ms) in a second, so delaying for 1000ms will stop for one second.

Sketch and Experiment

Here's the sketch. Check out the comments in the sketch to gain a more thorough understanding of what's happening. Alternatively, open up the Sandbox_01_Exploring_Blink.ino sketch.

language:c
    // Sandbox 01: Exploring Blink

/* When faced with a new platform, a coder’s first task is to write a “Hello, 
   world” program. Usually a “Hello, world” program actually prints those 
   comforting words on a screen. The Digital Sandbox doesn’t give us a screen to 
   print words on, but we do have LEDs! Wonderful, blinky, bright, shiny LEDs. 
   Instead of printing words, let’s blink an LED to say “Hello, world.”

   This experiment introduces some of the most basic functions in the Arduino
   language: digitalWrite([pin], [HIGH/LOW]) and delay([milliseconds]).
   Used together, these functions can be used to control an LED and time!
*/

// Nothing to do in the setup this time. But the function still has to be there!
void setup()
{
}

// Our loop function will blink the LED over-and-over-and-over.
void loop()
{
    digitalWrite(13, HIGH); // Set pin 13 HIGH (3.3-5V)
    delay(1000);            // Wait 1000 ms (1 second)
    digitalWrite(13, LOW);  // Set pin 13 LOW (0V)
    delay(1000);            // Wait 1000 ms (1 second)
}

Upload the sketch to your Sandbox, and take note of the small, yellow-ish LED labeled "D13". Is it blinking? How fast is it blinking?

Your Turn!

Now it's time to modify the code to create your own riff on the experiment.

Here are some changes we recommend trying this time around. Feel free to explore the code on your own. Make some errors and debug them using the error output window.

  • Try modifying the value in the delay() function. Make it lower than 1000. How short can you make the delays and still notice a blink? Ten milliseconds? One millisecond?

  • What happens if you take the delay() functions out of the sketch? You can quickly remove a function by commenting it out. Try this for your loop():

language:c
void loop()
    {
      digitalWrite(13, HIGH);
      //delay(1000);
      digitalWrite(13, LOW);
      //delay(1000);
    }

Comments are a great tool for deleting a line of code without actually deleting it.

Background Information

In this experiment we explore the subject of pins -- the manipulators of the Sandbox. Each LED (as well as the other inputs and outputs on the Digital Sandbox) is connected to a specific pin on the Sandbox's microcontroller.

Pins are all uniquely numbered, and each input or output component on the Sandbox is labeled with the pin number it's connected to -- that's the D2, D4, D11, A1, etc. lettering next to each LED, switch and sensor.

Every pin can be separately controlled; for instance pin 4 can be set HIGH at the same time pin 5 is set LOW. Some pins (as we'll later discover) have special powers, but every pin is at least able to accomplish digital input and output.

Active Parts

alt text

Code Components

Just one new function to introduce here. We kind of cheated last time by not using it, so we'll use it five times in this experiment.

Pin Mode: pinMode([pin], [INPUT/OUTPUT])

Every pin on an Arduino can be used as either an input or output, and one of your jobs is telling it which pin is which. To tell an Arduino what a pin's job is, you use the pinMode() function.

pinMode() takes two parameters inside its parenthesis. The first value is the pin that you want to set as an input or output. The second parameter is the mode, which can be either INPUT or OUTPUT. That's case-sensitive, those mode values need to be all caps!

Usually you'll stick your pinMode() declarations at the top of your sketch, in the setup() function. It only needs to run once, at the very start of your program, the pin will be set up that way for the rest of the sketch's run.

Sketch and Experiment

Here's our sketch, upload away! This is the Sandbox_02_MultiBlink.ino sketch.

language:c
// Sandbox 02: Multi-Blink

/* Large arrays of LEDs are often used to create massive outdoor signs and 
   animations because they're both bright and efficient. While we don't have the 
   millions of LED pixels that a display in Times Square might have, we can 
   still create some fun patterns with the Digital Sandbox.

   This experiment introduces the pinMode([pin], [INPUT/OUTPUT]) function. 

   It also explores the subject of PINs. Pins are all uniquely numbered, and 
   each input or output component on the Sandbox is labeled with the pin number 
   it's connected to -- that's the D2, D4, D11, A1, etc. lettering next to each 
   LED, switch and sensor.

   When we call a pinMode or digitalWrite function, we always have to declare
   which pin we want to control. That's usually done with the literal value
   (e.g. 4, 13, 8, etc) of the pin
*/

// The setup function will allow us to set the mode of each pin to OUTPUT.
void setup()
{
    pinMode(4, OUTPUT); // Set pin D4 as an OUTPUT
    pinMode(5, OUTPUT); // Set pin D5 as an OUTPUT
    pinMode(6, OUTPUT); // Set pin D6 as an OUTPUT
    pinMode(7, OUTPUT); // Set pin D7 as an OUTPUT
    pinMode(8, OUTPUT); // Set pin D8 as an OUTPUT
}

// The loop function will be used to blink each of our LEDs in succession.
void loop()
{
    digitalWrite(4, HIGH);  // Turn the D4 LED on (HIGH)
    delay(500);             // Wait half a second (500 ms)
    digitalWrite(4, LOW);   // Turn the D4 LED off (LOW)

    digitalWrite(5, HIGH);  // Turn the D5 LED on (HIGH)
    delay(500);             // Wait half a second (500 ms)
    digitalWrite(5, LOW);   // Turn the D5 LED off (LOW)

    digitalWrite(6, HIGH);  // Turn the D6 LED on (HIGH)
    delay(500);             // Wait half a second (500 ms)
    digitalWrite(6, LOW);   // Turn the D6 LED off (LOW)

    digitalWrite(7, HIGH);  // Turn the D7 LED on (HIGH)
    delay(500);             // Wait half a second (500 ms)
    digitalWrite(7, LOW);   // Turn the D7 LED off (LOW)

    digitalWrite(8, HIGH);  // Turn the D8 LED on (HIGH)
    delay(500);             // Wait half a second (500 ms)
    digitalWrite(8, LOW);   // Turn the D8 LED off (LOW)
}

Notice how each of our pinMode() declarations occur in the setup() function. Then the loop() is used to write each of those LEDs either HIGH or LOW.

Your Turn!

  • Try adding more blocks of code to create slicker patterns. Can you make a Larson scanner (ask an old person about Cylons or Knight Rider)? A chaser? Flip from odds to evens?
  • Try turning on more than one LED at a time. Turn them all on (and shield your eyes)!

3. Dimming (the Hard Way)

Yikes! Those white LEDs are blindingly bright! Is there any way to dim them? (Unless one of your hobbies is staring into the sun, we recommend putting a piece of paper over the LEDs in this experiment...or wear sunglasses.)

Background Information

Remember that the Digital Sandbox is fast. It can flick an LED on and off millions of times per second. What if we blinked the LED super fast, but also make it so the length of time the LED is off is more than the length of time it's on? This is called pulse-width modulation (PWM), a tool with a variety of applications, including dimming the LEDs.

In this experiment we'll explore PWM the hard way, by coding it in manually.

Active Parts

alt text

The Sketch and Experiment

Here's our sketch! Copy and paste into the Arduino IDE. Alternatively, open the Sandbox_3_Dimming_Hard.ino sketch. Upload away! Check out the comments for a line-by-line overview of what's going on.

language:c
    // Sandbox 03: Dimming (the Hard Way)

/* Yikes! Those white LEDs are blindingly bright! Is there any way to dim them? 
   (Unless one of your hobbies is staring into the sun, we recommend putting a 
   piece of paper over the LEDs in this experiment...or wear sunglasses.)

   This experiment explores the concept of pulse-width modulation (PWM). PWM
   is used to dim LEDs, vary motor speeds, and control all sorts of other
   components.

   In this experiment, we code in PWM the hard way -- manually.
*/

// In setup() we need to set our LED pins to OUTPUTs.
void setup()
{
    pinMode(5, OUTPUT); // Set pin 5 as an OUTPUT
    pinMode(6, OUTPUT); // Set pin 6 as an OUTPUT
}

// We run through loop() really fast! For 10% of the time the D5 LED is on
// and D6 is off. For the other 90% of the time, D5 is off and D6 is on.
void loop()
{
    // The 10%
    digitalWrite(5, HIGH);  // Turn D5 LED on
    digitalWrite(6, LOW);   // Turn D6 LED off
    delay(1);               // Wait 1ms

    // The 90%
    digitalWrite(5, LOW);   // Turn D5 LED off
    digitalWrite(6, HIGH);  // Turn D6 LED on
    delay(9);               // Wait 9ms
}

After uploading the sketch, keep an eye on the D5 and D6 LEDs. Do you notice a difference in their intensity? Which one is brighter?

Our sketch runs through the loop really fast -- it takes about 10ms to get through the whole thing, so it's running about 100 times per second. Nine times out of ten, the LED on pin 5 is off and the D6 LED is on. Because it's looping so fast you can't see any blinking, but you can sense a dimness in the D5 LED.

Your Turn!

  • How long can you make the delays before you start noticing a blink?
  • Try comparing both LEDs to a fully-on LED. Add pinMode(4, OUTPUT) and digitalWrite(4, HIGH) to the setup(), so the D4 LED turns on. Can you tell a difference between D4, D5, and D6?
  • What happens if you add something else to the loop section, like your animation from experiment two?

4. Dimming (the Easy Way)

Manual PWM is hard, and it doesn't leave room for anything else in the program. Why can't we offload that chore to the Digital Sandbox's microcontroller? It's smart enough for that...right?

Background Information

PWM is such a popular tool many microcontrollers implement special hardware so they can mindlessly toggle the pin while doing something else. We call this PWM-based output analog output.

Unlike digital outputs, which only have two possible values, analog outputs have a huge range of possible values. On the Sandbox we can analog-ly output 256 different values. If we set an analog output to zero, that's like setting a pin LOW, and 255 is like setting a pin HIGH, but all of the values in between produce an output that's neither HIGH or LOW -- it's somewhere in between.

Analog output seems great -- why wouldn't you use it all the time? Unfortunately, not all pins have special PWM powers. Only pins 3, 5, 6, 9, 10 and 11 are able to produce analog outputs.

Active Parts

alt text

Code Components

It's time to introduce digitalWrite()'s analog analogue: analogWrite().

Analog Output: analogWrite([pin], [0-255])

The analogWrite() function looks a lot like digitalWrite(). We still tell it which pin to control, but instead of a restrictive, digital output option, we get to choose any number between zero and 255 for the output.

There are two parameters to analogWrite(). The first is our pin, and the second is any value between 0 and 255. A 0 will output 0V, 255 will output 5V, 128 will do about 2.5V, and so on.

Unfortunately, not every pin is capable of analog output. You can only use analogWrite() on pins 3, 5, 6, 9, 10, and 11.

Sketch and Experiment

Here's our sketch, check out the code comments for help deciphering it. Open the Sandbox_04_Dimming_Easy.ino sketch or copy and paste from below. Upload away!

language:c
 // Sandbox 04: Dimming (the Easy Way)

/* Manual PWM is hard, and it doesn't leave room for anything else in the
   program. Why can't we offload that chore to the Digital Sandbox's 
   microcontroller? It's smart enough for that...right?

   This experiment introduces the analogWrite([pin], [0-255]) function.
   analogWrite is used to output an analog voltage on a pin. So not only can we
   output 0V and 5V, but we can also do 254 voltages in between. Need 2.5V 
   analogWrite to 128. Need 3.3V? Analog write 168.

   Unfortunately, not all pins can do analog output. Only pins 3, 5, 6, 9, 10, 
   and 11 have the special ability.
*/

// As usual, in setup we need to set our LED pin as an OUTPUT.
void setup()
{
    pinMode(5, OUTPUT); // Set pin 5 to an output
}

// In loop we'll cycle the pin 5 LED through five different brightness levels
// from fully off, to dim, to half-on, to full-on. Then we go back to off and
// do it all over again.
void loop()
{
    analogWrite(5, 0);  // Sets voltage to 0V (0/255 * 5V). LED is off.
    delay(1000);        // Wait a second

    analogWrite(5, 64); // Sets voltage to ~1.25V (64/255 * 5V). Pretty dim.
    delay(1000);        // Wait a second

    analogWrite(5, 128);// Set voltage to ~2.5V. Half-bright.
    delay(1000);        // Wait a second

    analogWrite(5, 192);// Set voltage to ~3.75V. Getting brighter!
    delay(1000);        // Wait a second

    analogWrite(5, 255);// Set voltage to 5V. Fully on (turn down for what!?).
    delay(1000);        // Wait a second
}

After uploading, keep an eye on the D5 LED. Can you identify 5 different levels of brightness (including off)? Don't stare at the LEDs for too long.

Your Turn!

  • What's the dimmest value you can set the LED and still see it on?
  • Can you add more brightness levels to the sketch to make it ramp up more smoothly? Get your copy/paste engines roaring!
  • Why do you think there are 256 possible analog output values? That doesn't seem like a very round number (hint: 28).

5. Color Mixing

Bleh...white. So boring. Let's add some color to this Sandbox. By combining analog output with an RGB LED, we can mix varying levels of red, green and blue to create a rainbow of colors!

Background Information

In art class you probably learned about primary colors and how you can mix them to produce any other color. While the artsy primary colors you might be familiar with are red, yellow and blue, in electronics (and programming in general) our primary colors are red, green and blue.

By selecting different analog levels for our primary colors, we can mix them to create any other color we want. Need yellow? Mix green and red. Purple? Red and blue. In this experiment we'll combine everything we've learned about analog output to add custom color to the Digital Sandbox.

Active Parts

alt text

Sketch and Experiment

Here's our sketch, nothing new to introduce this time. Check out the values in the three analogWrite() functions. What color do you expect will be made? Copy the code below, or open the Sandbox_05_Color_Mixing.ino sketch. Upload and find out.

language:c
    // Sandbox 05: Color Mixing

/* Bleh...white. So boring. Let's add some color to this Sandbox. By combining 
   analog output with an RGB LED, we can mix varying levels of red, green and 
   blue to create a rainbow of colors!

   In art class you probably learned about **primary colors** and how you can 
   mix them to produce any other color. While the artsy primary colors you 
   might be familiar with are red, yellow and blue, in electronics (and 
   programming in general) our primary colors are red, green and blue.

   By selecting different analog levels for our primary colors, we can mix them 
   to create any other color we want. Need yellow? Mix green and red. Purple? 
   Red and blue. In this experiment we'll combine everything we've learned about 
   analog output to add custom color to the Digital Sandbox.
*/

// The setup function, as usual, hoses our pinMode() declarations.
void setup()
{
    pinMode(9, OUTPUT); // Red LED is on pin 9
    pinMode(10, OUTPUT);// Green LED is on pin 10
    pinMode(11, OUTPUT);// Blue LED is on pin 11
}

// In loop() we can write our three LEDs to make a color:
void loop()
{
    analogWrite(9, 16);     // Set red to 16 (very dim)
    analogWrite(10, 128);   // Set green to 128 (half-bright)
    analogWrite(11, 64);    // Set blue to 64 (quarter-bright)

    // Does it look something like this?
    // http://www.colorpicker.com/108040
    // Try making your favorite color!
}

If it's hard to tell what the color is, put a piece of paper over the RGB LED.

Your Turn!

  • Play with the analog values to make your own colors. How about purple, or orange, or salmon? You can take it even further by adding delays, and blinking different colors to make animations. If you need help designing an RGB color code, try picking one out from this color picker website.
  • Make a stop light blink from green, to yellow, to red and repeat. You'll need some delay()'s and a handful of analogWrite() trio's.

6. Number Storage with Variables

The herky-jerky fading from experiment four accomplished the task, but just think of all the values we were missing! How do we make the LED fade smoothly? You can whip out 256 minutely different analogWrite() blocks, or you can reduce it to one, using variables.

Background Information

Variables are like storage containers for numbers. We can put any number in a variable, and either recall it and put it to use, or manipulate it to change the value it stores. Anywhere you stick a literal number (like "0" or "255") you can instead use a variable.

There are a few rules when it comes to creating a variable. They can be any word, but they must begin with a letter, and they can't have spaces (use "_" instead). They are case sensitive, so a variable named "fade" isn't the same variable as "Fade." Try to keep variables short, but use descriptive words to keep your code legible.

Active Parts

alt text

Code Components

We've been slacking off on introducing new functions and syntax, so it's time to throw some new stuff at you! As you'll discover, there's a lot that goes into creating and using a variable.

Declaring Variables

In order to use a variable, you first have to declare it. There are two parts to a variable declaration: a name and a type.

There are a few hard-coded rules to variable naming: a name can't start with a number and it can't contain spaces or special characters. Variables can only be composed of 0-9, a-z, A-Z, and _. When you're picking out a name for a variable try to make it descriptive, that'll help to make your code easier to read and write.

A variable type determines the minimum and maximum values a variable can be. There's no such thing as infinity in an Arduino -- there are limits to how big a number can be. In this example we'll be using one of the more fundamental variable types out there: int. An int variable can be anywhere from -32768 to 32767 -- plenty of room for us to play with. (Those seemingly random numbers come from the fact that an int is a 16-bit signed value, which means the range is from 215 to 215-1.)

Once you've picked out your variable type and name, you can declare it like this:

language:c
int example_variable;  // Declare a variable of type int, named example_variable

That's it! Now you can store numbers in the example_variable variable, and even manipulate it with some math.

Variable Scope

You can declare a variable anywhere in your code, but exactly where you declare it is actually really important, because every variable has scope. A variable's scope determines where it can be used and recalled. As a general rule of thumb, a variable can only be used within the curly bracket's ({ and }) that is was declared in -- that's its scope.

You can create a global variable by declaring your variable above setup(). Global variables can be used in both the setup() and loop() functions.

But, on the other hand, if you create a variable inside setup() that variable's scope is limited to the setup() function. So we can't create a variable in setup() and use it inside loop() -- it's out of scope!

Using Variables

To assign a value to a variable, you use the assignment operator -- =. The assignment operator takes the value on the right-side of the equals-sign, and stores it into the value on the left side.

So, for example, if we want to assign the value 5 to our example_variable variable, this line of code will do:

language:c
example_variable = 5;

But you can do so much more with variables than static value assignments. Variables are awesome because we can do math on them. You can add, subtract, multiply, or divide a variable with any one of these symbols:

Math OperationCode symbol
Add+
Subtract-
Multiply*
Divide/
Remainder%


You can use the Sandbox as a calculator, make it do some hard math with lines like this:

language:c
example_variable = (1024 / 8) * 52 - 163;

Parenthesis can be used, as they are in the above example, to define an order of operations. The stuff inside the parenthesis happens first, then multiplications and divisions, then additions and subtractions.

You're not limited to literal values on the right-hand side either! You can perform math on variables and store them into other variables. You can even perform math on a variable and store it in the same variable. For example, if I wanted to increase the value of example_variable by 1, this line of code would do:

language:c
example_variable = example_variable + 1;

In that case, if example_variable was 0, after running that line of code it would become 1.

Sketch and Experiment

Whew! That's a lot to read about variables. As you begin to use them, though, all of that information will start to become ingrained in your brain. Here's our sketch:

language:c
// Sandbox 06: Number Storage with Variables

/* The herky-jerky fading from experiment four accomplished the task, but just 
   think of all the values we were missing! How do we make the LED fade 
   smoothly? You can whip out 256 minutely different analogWrite() blocks, or 
   you can reduce it to one, using variables.

   Variables are like storage containers for numbers. We can put any number in a 
   variable, and either recall it and put it to use, or manipulate it to change
   the value it stores. Anywhere you stick a literal number (like "0" or "255") 
   you can instead use a variable.
*/

// To declare a variable you need two things: a type and a name. The type
// determines how much space in memory the variable takes up. The most standard
// variable type is int. int's can be either positive or negative. They take up
// 16-bits of space in your Arduino, so they can be anywhere from -32768 (2^15)
// to 32767 (2^15 - 1).
int fade;   // Decalare an int type of variable called "fade"

// Anything defined in the space _above_ setup() is GLOBAL. A global variable
// can be used in both the setup() and loop() functions. We'll want to use fade
// in both of those functions.

void setup()
{
    pinMode(5, OUTPUT); // Set pin 5 as an OUTPUT

    fade = 0;   // Set fade to 0, give it an initial value
}

void loop()
{
    // Set the LED on pin 5 to the value of "fade":
    analogWrite(5, fade);

    // Increment fade by 1. Code math takes the value on the right side of the
    // equation, and stores it into the variable on the left side.
    // This equation says take the original value of fade, add 1 to it, and
    // store that sum in the fade variable.
    fade = fade + 1;    // Increment fade by 1

    delay(500); // Wait half a second
}

Copy the code above, or open the Sandbox_06_Storage_w_Variables.ino sketch. Upload away and keep an eye on the D5 LED. Much smoother!

Your Turn!

  • Can you make the LED fade from HIGH to LOW? (Hint: You may need to change the setup value of fade, and change the + to a -.)
  • Can you make other LEDs fade? How about more than one fading at the same time? Can you do the math to make the D6 LED fade in the direction opposite of our D5 LED? (Hint: try adding an analogWrite(6, 255-fade) function call somewhere.)

7. If This Then That

Fading from the last experiment was working just fine until we got to the maximum brightness level of 255. What happens then is a mystery known only to the compiler (and you, once you learn a little something more about data types). What if we added "catch" to force that fade variable to reset when it hits a certain value?

Background Information

This experiment introduces the if statement, one of the most fundamental programming structures around. Not only are if statements important for computers, they also rule most of the decisions we make in our lives: If it's cloudy outside, then pack your umbrella. If you're hungry, then make a sandwich. Like us, computers use if statements to make choices.

An if statement requires two components to be complete: a condition and a consequence. A condition is a value or mathematical operation that evaluates to either true or false. If the condition evaluates to true, then the consequence is executed. The consequence can be a code block of any size - one block or hundreds of blocks.

If the condition in the if statement is false, then the consequence is skipped, and the program starts running the code following the if block.

Active Parts

alt text

Code Components

Time to introduce the if statement. Time to whip out some parenthesis and curly brackets.

If Statement: if ( [condition] ) { [consequence] };

An if statement requires two components: a condition and a consequence. The anatomy of an if always looks a little something like this:

language:c
if ( [condition] )
{
    [consequence]
}

Those parenthesis and curly-brackets are required (ignore the square brackets)!

The [condition] part of an if statement can be any statement that can evaluate to either true or false. To build those conditional statements, we usually use comparison operators. There are all sorts of comparison operators, including:

Comparison OperatorCode Symbol
Less than<
Greater than>
Less than or equal to<=
Greater than or equal to>=
Equal to==
Not equal to!=


You can use variables and/or literal numbers in conjunction with comparison operators. For example, if we want to test if example_varible is greater than or equal to 255, this statement will do:

language:c
if ( example_variable >= 255)
{
    // example_variable is greater than or equal to 255
    // do some stuff!
}

If the conditional statement turns out to be true, then the Sandbox executes each line in the if statement sequentially. If the statement is false, though, then the Sandbox jumps over the if statement's curly brackets and runs the first line of code after the }.

You're free to put any line of code inside the if statement's [consequence] section. You can even modify the variable that you tested in the condition. Building off the last example, if we wanted to set example_variable to 0 when it rises above 254, we can do something like this:

language:c
if (example_variable >= 255)
{
    example_variable = 0;   // Reset example_variable to 0
}

Something like that might look familiar in our sketch for this experiment.

Sketch and Experiment

Here's our sketch for this experiment. Just one if statement for now. Read through the comments to get an idea for what's going to happen. This is the Sandbox_07_If_Then.ino sketch.

language:c
    // Sandbox 07: If This Then That

/* Fading from the last experiment was working just fine until we got to the 
   maximum brightness level of 255. What happens then is a mystery known only to 
   the compiler (and you, once you learn a little something about data types). 
   What if we added “catch” to force that fade variable to reset when it hits a 
   certain value?

   This experiment introduces the if statement, one of the most fundamental 
   programming structures around. Not only are if statements important for 
   computers, they also rule most of the decisions we make in our lives: If it’s 
   cloudy outside, then pack your umbrella. If you’re hungry, then make a 
   sandwich. Like us, computers use if statements to make choices.
*/

int fade = 255; // Create a global variable, fade, set it to 255.

// setup() set's up our LED on pin 10
void setup()
{
    pinMode(10, OUTPUT);    // Set pin 10 as an OUTPUT
}

// loop() uses the fade variable to set the brightness of the LED. Each time
// through loop we decrease fade by 1. An if statement checks to make sure we
// don't set fade too low.
void loop()
{
    fade = fade - 1; // Decrease fade by 1

    if (fade < 0)   // If fade is less than (<) 0, execute the code inside
    {
        fade = 255; // Set fade back to 255 (top value)
    }

    analogWrite(10, fade); // Write the fade variable out to pin 10.

    delay(10);  // Wait 10 ms (1/100th of a second)
}

Run the code on your Sandbox and keep an eye on the blue LED. It should progressively go from super bright to off, and repeat that cycle endlessly.

We use a variable called fade to keep track of the analog output value. At the very beginning of each loop, we'll subtract 1 from the fade variable. Then, after subtracting from fade, we need to use an if statement to make sure it's not out of bounds. The if statement in this sketch states that if fade is less than 0 (that would mean it's a negative number), then set fade to 255.

Your Turn!

  • Can you make the fade work the other way? Start at 0, fade up to 255, and then go back to 0. (Hint: You'll need to flip the logical operator around.)
  • Make it even smoother! Can you make it fade smoothly up and smoothly down in the same sketch? From 0 to 255, then 255 to 0, then 0 to 255, then back again.

8. The Reaction Tester

Computers are great at doing math and automating boring tasks, but everyone knows that their true purpose is to play games. Let's create a game on the Digital Sandbox! But in order to control the game we need to add input.

Background Information

Up to this point, our Digital Sandbox experience has been very one-sided. Output to tiny yellow LEDs. Output to larger white LEDs. Output to RGB LEDs. Change the fade value of the output. Output, output, output. Let's flip the tables on the Sandbox, and send some input to the board!

Inputs are signals or values sent into a system. Some of the most common inputting components are buttons or switches. Buttons on a keyboard are an input to your computer because they send data into that system.

if statements are critical when assessing the status of an input and taking an action based on it - if button A is pressed, then print an "a." We can take the if statement a step further by adding an else condition, which allows us to control what happens if the if statement evaluates to false. So now we can say something like "if the egg floats, throw it away, otherwise (else) fry it and eat it!"

Active Parts

alt text

Code Components

This time we introduce a new function, plus a riff on the if statement.

Digital Input: digitalRead([pin])

The digitalRead([pin]) function is used to read the voltage of a pin. It can tell you if a pin is HIGH (~5V) or LOW (~0V).

There's just one parameter to the digitalRead() function -- a pin that you want to read. Make sure to set the pin as an INPUT, using the pinMode() function, before you read it!

This function is different from the others because it returns a value. Instead of just writing out the function call and skipping to the next line, we actually have to use the digitalRead() function in conjunction with other bits of code.

The digitalRead() function will come back with one of two values: HIGH or LOW. You can use that return value as part of a conditional test in an if statement, for example:

language:c
if (digitalRead(2) == HIGH)
{
    // Do something, because the button is pressed
}

Or, if you so pleased, you could store the value of the digitalRead() function inside a variable, like this:

language:c
example_variable = digitalRead(2);

If/Else: if( [condition] ){ [true consequence] } else { [false consequence] }

We'll use an extension of the if statement this time: the if...else statement. if/else allows us to take one action if the condition is true and a different action if it's false.

Here's the anatomy of an if/else:

language:c
if ( [consequence] )
{
    // Do something because the consequence is true
}
else
{
    // Do something because the consequence is false
}

if/else is really handy when you're dealing with digital outputs which can only be in one of two states. If a button is pressed, do something, otherwise do something else.

Equivalence Test

In this experiment we'll be using the equivalence test (==) in our if statement conditional. That's right, there are a pair of = signs. That's to differentiate the equivalence test from the assignment operator (=). While the single equal-sign assignment sets one value to another, the equivalence test is asking if two values are equal and producing either a true or false response.

If you're ever creating a conditional statement, make sure you use the equivalence operator if you're asking if two values are equal.

Sketch and Experiment

Here's the sketch. It's a big one! Check out the comments for a line-by-line explanation. This is the Sandbox_08_Reaction_Tester.ino sketch.

language:c
    // Sandbox 08: Reaction Tester

/* Computers are great at doing math and automating boring tasks, but everyone 
   knows that their _true_ purpose is to play games. Let's create a game on the 
   Digital Sandbox! In order to control the game we need to add **input**.

   Up to this point, our Digital Sandbox experience has been very one-sided. 
   _Output_ to tiny yellow LEDs. _Output_ to larger white LEDs. _Output_ to RGB 
   LEDs. Change the fade value of the _output_. Output, output, output. Let's 
   flip the tables on the Sandbox, and send some **input** to the board!

   This experiment introduces the digitalRead([pin]) function. This is the first
   time we'll use a function that RETURNS A VALUE. Instead of simply creating a
   function call and going to the next line, we need to do something with the
   function -- either assign it to a variable or use it in a conditional.
*/

// The led variable should run between 4 and 8. It's used to control the white
// leds (D4-D8), one at a time.
int led = 4;
// The speed variable controls how fast the LEDs cycle from one to the next.
// A larger value, means a slower cycle.
int speed = 100;

// setup()! Time to set up some OUTPUTs...and INPUTs!
void setup()
{
    // Set the button and switch as INPUTs:
    pinMode(2, INPUT);
    pinMode(12, INPUT);

    // Set all LEDs as outputs:
    pinMode(4, OUTPUT);
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
}

void loop()
{
    // The first if statement sets the SPEED of the game.
    // digitalRead() can either return HIGH or LOW. If the switch is in the "1"
    // position, the digitalRead() function will return HIGH.
    if (digitalRead(2) == HIGH) // If the switch is set to 1...
    {
        speed = 50;             // ...set speed to 50 (fast)
    }
    else                        // Else the switch will be set to 0...
    {
        speed = 150;            // ...so set the speed to 150 (slow)
    }

    // The button on pin 12 will be HIGH when pressed and LOW otherwise.
    if (digitalRead(12) == HIGH)    // If the button is pressed
    {
        digitalWrite(led, HIGH);    // Turn the active LED on, nothing else.
    }
    else                            // Otherwise, if the button is released
    {
        digitalWrite(led, LOW);     // Turn the active LED off.

        led = led + 1;              // Increment the led variable by 1
        if (led > 8)                // If led is out of our range (4-8)
        {
            led = 4;                // Set it back to the bottom (LED 4).
        }

        digitalWrite(led, HIGH);    // Turn on the new LED

        delay(speed);               // Delay for speed ms.
    }

    // The else statement above will take up the majority of the loop() if the
    // button is released. One LED will turn off, the next will turn on, and
    // a short delay will occur before we loop back.
}

There are two important if/else statements in this program, which each test the status of an input. The top if/else tests pin 2, which is connected to the switch. If the switch is set to one (e.g. HIGH), then we set a variable called speed to 50. If the switch is set to zero (LOW), then speed becomes 150.

The second if/else tests pin 12, which is tied to the small button. When the button is pressed, then the input is set to one (HIGH), and it's zero when released. This means that, when the button is being pressed, the code in the then will execute. When the button is not being pressed, the else code will run.

Can you guess what will happen in each of the pin 12 test cases? Upload the sketch to your board to find out!

This is a very simple game. Pick a number between four and eight, and try to make the LED stop on that number by pressing the button. To switch between easy and hard mode, move the switch from 0 to 1. Can you make it stop in the middle on hard mode?

Your Turn!

  • Trick your friend and swap which direction of the switch sets it to easy mode -- make zero hard and one easy.
  • Swap the function of the switch and the button, so you have to press the button to set the difficulty and flick the switch to stop the LEDs.

9. Serial Calculator

While you probably can't have a very stimulating conversation with the Digital Sandbox, it can send you some very interesting information. It's great at math, so let's have the Sandbox do some calculating for us! Trouble is, how do we get it to print numbers (without Morse code)? Enter serial communication!

Background Information

Serial communication is a form of data transmission where we can send a string of 1's and 0's between two devices and actually form a set of characters. So 01101000 01100101 01101100 01101100 01101111 00101100 00100000 01110111 01101111 01110010 01101100 01100100 becomes "Hello, world."

With serial, we can send actual text from the Sandbox and display it on our computer using the Serial Monitor.

Active Parts

alt text

Code Components

This sketch introduces a pair of functions related to Serial communication. Plus, a new data type!

Serial Communication

In order to use serial communication, you first have to initialize it by calling Serial.begin([speed]). The begin function requires one parameter -- the baud rate -- which controls how fast data is transmitted. The baud rate is usually selected among a set of common speeds: 4800, 9600, 19200, 38400, 57600, or 115200. For example, to initialize Serial at 9600 baud, this line of code will do:

language:c
Serial.begin(9600);

9600 is a good, default speed for most applications. We'll use it throughout our experiments. You'll usually want to stick your Serial.begin() statement in the setup() function. Call it once, set it, and forget it.

After Serial has been initialized, you can start printing messages using either Serial.print([message]) and Serial.println([message]). Both functions work to print a message, but the Serial.println() function adds a new line after the message.

You can print all sorts of with Serial. You can print static, pre-coded messages, by surrounding them with quotes ("), like this:

language:c
Serial.print("This is a long message, which you can print with Serial!");

Or you can print a variable, like this:

language:c
Serial.println(example_variable);   // Print the value of example_variable

As you continue to explore with Serial, you'll find it's a powerful debugging tool. It gives you a window into the Sandbox's mind -- you can print out the values of your variables.

Long Data Type

We'll be using a new variable data type in this experiment: long. A long variable stores 65536-times more values the the int! It can store numbers in the range of -2,147,483,648 to 2,147,483,647. Billions!

Why not always use long? Well it uses twice as much memory -- 32-bits. The Sandbox doesn't have unlimited memory, so it's best to conserve it when you can.

Sketch and Experiment

Here's our Sandbox_09_Serial_Calculator.ino sketch. Check out the comments for a line-by-line overview.

language:c
    // Sandbox 09: Serial Calculator

/* While you probably can't have a very stimulating conversation with the 
   Digital Sandbox, it can send you some very interesting information. It's 
   great at math, so let's have the Sandbox do some calculating for us! Trouble 
   is, how do we get it to print numbers (without Morse code)? Enter serial 
   communication!

   This experiment introduces Serial Communication. New functions include
   Serial.begin() and Serial.println(). Serial is a form of data transmission 
   where we can send a string of 1's and 0's between two devices and actually 
   form a set of characters. So 01101000 01100101 01101100 01101100 01101111 
   00101100 00100000 01110111 01101111 01110010 01101100 01100100 becomes 
   "Hello, world."
*/  

// Let's create a base value to do math on. This time, we'll use the "long"
// variable type -- a 32-bit variable, which can be anywhere from 
// -2,147,483,648 to 2,147,483,647.
long multiplier = 1;

void setup()
{
    // To initialize serial communication, we need to use the Serial.begin()
    // function. The number inside the function determines the baud rate -- the
    // communication speed. 9600 is a very standard, default speed. Good for
    // most debugging.
    Serial.begin(9600);

    // Serial.println() is used to print a message out to the serial monitor
    // you can print variables, or static text defined inside quotes ("").
    Serial.println("Powers of 2!");
}

void loop()
{
    if (digitalRead(12) == HIGH)    // If the button is pressed
    {
        // On top of printing static messages (embedded inside quotes), we can
        // also print the value of variables.
        Serial.println(multiplier);

        // Multiply the multiplier variable by 2. To multiply we use the 
        // asterisk (*) character.
        multiplier = multiplier * 2;

        delay(250); // Short delay for visibility on the serial monitor.
    }
}

After uploading the sketch, if you're waiting for the Sandbox to do something visual, you'll be waiting forever. Instead open the serial monitor. Click the Serial Monitor button up in the top right corner of the Arduino IDE.

After opening the Serial Monitor, press the D12 button. You should see multiples of 2 start to zoom by. Now you're calculating!

Your Turn!

  • Something funny happens when the power of 2 gets past 1073741824, and then turns to -2147483648, and then turns to zero. This is because our variable has reached its maximum value and, in a sense, has gotten confused. Can you add an if statement to catch an out-of-bounds multiplier variable and reset it?
  • Try some of the other mathematical operators. Add with +, subtract with -, or divide with /. Can you figure out what the % (modulo) operator does?

10. Do the Analog Slide

Digital inputs, like the button, only allow for two input values: HIGH or LOW. But what about the in-betweens? When you turn the volume up on your stereo, you're not forced to pick between mute and "OMG MY EARS." For volume control and other "finely-tuned" settings, we need analog inputs.

Background Information

Analog inputs are components that put data into a system with a range of more than two values. On the Digital Sandbox, analog inputs can produce a value anywhere between zero and 1023 (1024 total values). The value produced by an analog input is proportional to the voltage it produces. If an analog component reads a value of zero, the voltage is 0V. If the output value is 1023, then the voltage is 5V. An analog reading of 512 is about 2.5V, and so on.

A special component inside the Sandbox's microcontroller called an analog-to-digital converter (ADC) is able to convert that range of input voltages to a discrete number. This is a special circuit that most pins on the Sandbox don't have. It's so special that the ADC pins are labeled with a preceding 'A'. The analog sensors on the board are labeled as "A0", "A1", "A2" and "A3."

Many electronic components produce analog output, the most common of which is a potentiometer. "Pots" come in a variety of shapes and sizes. Rotary pots are very commonly used to adjust the volume on a stereo. Slide potentiometers, like that on the bottom of the Sandbox, are often seen adjusting sound levels on mixing boards.

Active Parts

alt text

Code Components

First there was digitalWrite() then analogWrite(). Then there was digitalRead()...can you guess what's next?

Analog Input: analogRead( [analog pin] )

To read the value of an analog input we use the analogRead( [analog pin] ) function. Like digitalRead(), this function requires just a single parameter -- a pin to read.

That's where the similarities stop though. Unlike digitalRead() the only pins you can analogRead() on are those with a preceding 'A': A0, A1, A2, and A3.

Also, instead of simply returning HIGH or LOW, analogRead() returns a number between 0 and 1023 -- 1024 possible analog values! An output of 0 equates to 0V, and 1023 means the pin reads 5V. If you get 512, you're somewhere in the middle ~ 2.5V.

You can store the return value of an analogRead() in a variable, and use it from there. For example, here's how we store the analog input of A0 into a variable:

language:c
example_variable = analogRead(A0);

Or you can use it directly, like in a comparison for example:

language:c
if (analogRead(A0) > 512)
{
    Serial.println("The analog voltage is above 2.5V");
}

Don't forget to set your analog pin as an INPUT some time before you read it!

Sketch and Experiment

Here's the Sandbox_10_Analog_Slide.ino sketch. Upload away!

language:c
    // Sandbox 10: Do the Analog Slide

/* Digital inputs, like the button, only allow for two input values: HIGH or 
   LOW. But what about the in-betweens? When you turn the volume up on your 
   stereo, you’re not forced to pick between mute and “OMG MY EARS.” For volume 
   control and other “finely-tuned” settings, we need analog inputs.

   This experiment introduces analog inputs using the analogRead([analog pin]) 
   function. You can perform an analogRead on any pin that has an 'A' in front
   of it.

   analogRead() returns a value between 0 and 1023. 0 is equivalent to 0V, 1023
   is 5V, 512 is 2.5V, and so on.

   After uploading this sketch, open the serial monitor and check the output
   of the slider potentiometer.
*/

// 
void setup()
{
    // Set up the slide potentiometer as an INPUT:
    pinMode(A3, INPUT);

    // Set up serial, set the baud rate to 9600:
    Serial.begin(9600);
}

void loop()
{
    // Print the value of analog pin 3:
    Serial.println(analogRead(analogRead(A3)));

    delay(100); // Delay for visibility.
}

After uploading the sketch, open up the Serial Monitor. Numbers should begin to stream by. Try sliding the potentiometer, do the values change?

Can you make the output 0? 1023? 512? Take note of which slide pot position relates to which value.

Your Turn!

  • Can you make the slider control the LEDs? You could slide the white LEDs back and forth, or try controlling the brightness of the RGB LEDs with the slider.
  • Why are there only 1024 output values? Why not an even 1000? (Hint: 210.)

11. Automatic Night Light

We now have all the programming tools we need to make some totally awesome, interactive projects. Let's incorporate the light sensor - another analog input component - to create an automatic night light that turns on when it's dark.

Background Information

You may not see them, but light sensors are incorporated into all sorts of modern electronic devices. There are light sensors in smartphones, which measure how bright your environment is and adjust the screen brightness automatically. There are light sensors in smoke detectors that detect particles in the air. Photogates use a light sensor to determine when an object passes a certain point -- critical for those photo finishes!

The light sensor on the Digital Sandbox is called a photo-transistor. It produces an analog voltage relative to the amount of light it sees. The lower the analog value, the darker the environment. If you cover the sensor completely, you might get the output all the way down to zero. Shine a flashlight on it and you might get a maximized reading of 1023.

Active Parts

alt text

Code Components

Nothing too major to introduce this time, but there is a new variable type!

Constant Variables & Initializing a Variable

The const keyword isn't really a variable type. It's a add-on to a variable type, which modifies how that variable works. const is short for constant. When you stick it in front of a variable declaration that variable becomes read-only.

It's good practice to assign const to any variable you never want to change. In our example, we have a constant darkness threshold that will never change, but may need to be adjusted between code uploads.

When you declare a constant variable, you're forced to assign it a variable right then-and-there. That means after the variable declaration, you have to add = something;. For example, here's how we create a const int variable and assign it the value 0:

language:c
const int example_variable = 0; // Create a constant variable, with the value 0

You can initialize any variable, not just constants. Do it when you can. It can save you a line of code, or sometimes your sanity!

Sketch and Experiment

Here's our Sandbox_11_Automatic_NightLight.ino sketch. Check through the comments, then go ahead and upload it.

language:c
    // Sandbox 11: Automatic Night Light

/* We now have all the programming tools we need to make some totally awesome, 
   interactive projects. Let’s incorporate the light sensor - another analog 
   input component - to create an automatic night light that turns on when it’s 
   dark.

   This experiment continues on with the analogRead() function. This time we're
   going to do something with the analog value! When the output from the light
   sensor gets too low, we'll automatically turn on some LEDs.

   If your light sensor isn't triggering correctly, check the output of the
   Serial Monitor to see if there's a better value for the dark variable than
   we've set.
*/

// A const variable is a variable that can't be changed once it's declared.
// The dark variable determines when we turn the LEDs on or off. If the light
// sensor reading is less then or equal to dark, turn the LEDs on. If it's
// greater than, then turn the LEDs off.
const int dark = 10;

void setup()
{
    // Set A1 as an INPUT
    pinMode(A1, INPUT);

    // Set our LED pins (4 and 8) as outputs:
    pinMode(4, OUTPUT);
    pinMode(8, OUTPUT);

    // Initialize Serial, set the baud rate to 9600 bps.
    Serial.begin(9600);
}

void loop()
{
    // Create a variable, and store the light sensor reading into it:
    int lightValue = analogRead(A1);

    // Print the reading from the light sensor:
    Serial.println(lightValue);

    // Compare the light sensor value to our dark constant variable.
    if (lightValue <= dark) // If the light sensor reading is less then "dark"
    {
        digitalWrite(4, HIGH);  // Turn on LED 4
        digitalWrite(8, HIGH);  // Turn on LED 8
    }
    else                    // Otherwise, if "lightValue" is greater than "dark"
    {
        digitalWrite(4, LOW);   // Turn LED 4 off
        digitalWrite(8, LOW);   // Turn LED 8 off
    }

    delay(100); // Delay for serial monitor visibility.
}

After uploading the sketch, try to cover the light sensor. Do the LEDs turn on? Do they turn back off once you uncover the sensor?

If the LEDs don't turn on as expected, check to see what value the photocell is reading by opening the Serial Monitor.

By default, we set the dark variable to 10. If the photocell is at or below that, the LEDs turn on, otherwise they'll stay off. Does that value need to go higher or lower for your night light to switch?

Your Turn!

  • If the brightness is right on the border of on/off, the LEDs can blink unpleasantly. Try adding another if statement to catch if the light sensor is right in the middle range; from there you can dim the LEDs based on what the sensor reads.
  • Try incorporating the RGB LED into this project. If it’s super-bright, make it light up yellow. Kind of dim? Green. Totally dark? Blue.

12. Thermal Alert!

"Is it hot in here, or is it just me?" Using a temperature sensor, which is able to precisely measure the room temperature, we can answer that question once and for all!

Background Information

Temperature sensors are a critical component in many circuits, whether you're controlling an A/C system or creating a safety mechanism for gas-powered appliances. Electronic temperature sensors come in many form-factors, from big thermocouples that can measure up to 1000 °C to that little black rectangle on the Digital Sandbox.

The temperature sensor on the Sandbox produces an analog voltage that represents the temperature around it. The voltage is actually linearly proportional to the Celsius temperature. If you know the output voltage of the sensor, you can calculate the temperature with this equation:

temperature = (voltage - 0.5) \times 100

We can have the microcontroller do all of that math for us as long as we find the right algorithm - an equation or set of instructions that accomplish a specified task.

Active Parts

alt text

Code Components

Time for another new variable type!

Variables with Decimals: float

Up to this point, every variable and value we've used has been an integer. Integers are whole numbers, which means they can't have decimal points. If you need the precision of decimal points, you have to call up the floating-point variable type.

To declare a floating-point variable, simply use the float keyword and create a variable like you normally would:

language:c
float myPi = 3.1415;

float variables can be huge anywhere from 3.4028235×1038 to -3.4028235×1038.

Floating point numbers may seem great, but they do have their assortment of problems: they take up lots of space, and they take a large chunk of time for the processor to work with. Try to avoid them if you can.

Sketch and Experiment

Here's our experiment. The sketch is titled Sandbox_12_ThermalAlert.ino. Take a peek at the comments and upload!

language:c
    // Sandbox 12: Thermal Alert!

/* “Is it hot in here, or is it just me?” Using a temperature sensor, which is
   able to precisely measure the room temperature, we can answer that question 
   once and for all!

   This experiment explores the analog input even further. This time we're 
   reading the input of an analog temperature sensor. Using some math we can
   convert that value to a voltage, and then to celsius and fahrenheit 
   temperatures.

   To do math with decimals, we need to introduce a new variable type: float.
   float variables allow us to escape the realm of the integer to calculate
   divisions to a very precise level.

   Also notice that we've defined some constant int variables for our LED
   and sensor pins. This is good practice -- it makes your code more readable
   and it allows you to quickly change the pin throughout your code if need be.
*/

// New variable type: float! float varaibles allow you to use decimal values.
// Other variables we've used -- int and long -- are integers. No decimal points
// allowed. floats are great, but only use them when you really need to. They
// require a lot more storage space and processor power.
// The thermalAlert variable is a constant. If the temperature reading is above
// this point, we'll turn the red LED on.
const float thermalAlert = 80.0;

// Lets also decloare some global variables for our LED and sensor pins. This
// makes swapping those pins much easier. And our code below will become much
// more readable.
const int temperaturePin = A0;  // Temperature sensor is connected to A0
const int redLEDPin = 9;        // Red LED is connected to pin 9
const int greenLEDPin = 11;     // Green LED is connected to pin 11

void setup()
{
    // Set the temperature sensor pin as an INPUT:
    pinMode(temperaturePin, INPUT);

    // Set the LED pins as OUTPUTs:
    pinMode(redLEDPin, OUTPUT);
    pinMode(greenLEDPin, OUTPUT);

    // Initialize Serial, set the baud rate to 9600.
    Serial.begin(9600);
}

void loop()
{
    // Read the raw 0-1023 value of temperature into a variable.
    long rawTemp = analogRead(temperaturePin);

    // Calculate the voltage, based on that value. To calculate voltage, we
    // multiply by the maximum voltage (5V), and divide by the maximum ADC
    // value (1023).
    float voltage = rawTemp * (5 / 1023.0);
    Serial.print("Voltage: ");
    Serial.print(voltage);
    Serial.println(" V");

    // Calculate the celsius temperature, based on that voltage. The celsius
    // temperature is calculated by subtracting 0.5V from the reading, then
    // multiplying that small value by 100.
    float celsius = (voltage - 0.5) * 100;
    Serial.print("Celsius: ");  // Print "Celsius: "
    Serial.print(celsius);      // Print the celsius temp
    Serial.println(" °C");      // Print " °C" and a new line

    // Use a common equation to convert celsius to fahrenheit. F = C*9/5 + 32.
    float fahrenheit = (celsius * 9.0 / 5.0) + 32.0;
    Serial.print("Fahrenheit: ");   // Print "Fahrenheit: "
    Serial.print(fahrenheit);       // print the fahrenheit temp
    Serial.println(" °F");          // Print " °F" and a new line
    Serial.println();               // Print a blank line

    // Now check the temperature, and turn on either the red or green LED 
    // depending on that value.
    if (fahrenheit >= thermalAlert)
    {
        digitalWrite(redLEDPin, HIGH);
        digitalWrite(greenLEDPin, LOW);
    }
    else
    {
        digitalWrite(redLEDPin, LOW);
        digitalWrite(greenLEDPin, HIGH);
    }

    delay(1000);    // Wait a second between readings
}

After uploading the sketch, open the Serial Monitor.

You'll see the temperature in Celsius and Fahrenheit start to flow by. You should also see either the green or red LED illuminate, depending on that temperature reading. If the temperature is above 80 °F, there'll be a red thermal alert!

Your Turn!

  • Can you add a third check to alert when it’s too cold, by turning on the blue LED? The real trick here is cooling the Sandbox off. One option is to power the board with a battery and stick it in the fridge.
  • Celsius and Fahrenheit are two of the most common temperature scales, but they’re not the only ones. Can you print the temperature in units of Kelvin or Rankine? You’ll need to find an algorithm to convert to them from Celsius.

13. Sound Detecting

\<Pitchman voice> Introducing the fabulously groundbreaking SOUND (Sandbox's Over/Under Nominal Decibels) System! Microphone check 1..2..1..2. With the SOUND you'll always have an adjustable sound level detector handy! \</Pitchman voice>

Background Information

In this experiment we'll use the Sandbox's on-board microphone to measure volume levels and display them on the LEDs. The microphone produces a sound wave, which is just another analog voltage that we can measure. The louder the sound, the higher the amplitude of the wave and the larger the voltage.

Without a lot of complicated math and filters, sound can be a difficult thing to measure and react to. Using the Sandbox for voice recognition isn't quite possible, but it can be programmed pick out high volumes as long as it can sample the microphone input fast enough. We can use the slide potentiometer to set the sensitivity of the display.

Active Parts

alt text

Code Components

You're probably getting used to this now: time for more variable types! This time it's not so much a new variable type as a common storage space for variables.

Arrays

Arrays are a group of variables all housed under a single variable name. Array syntax makes heavy use of the square brackets [ and ]. Here's an example of an array declaration:

language:c
int myArray[3]; // Create an array of 3 int's

You can also initialize your array, using curly brackets:

language:c
// Create and initialize an array containing 5 long variables
long myBigArray[5] = {0, 1000, -44321, 412355234, 42}; 

To reference a single variable in an array we use an index. Here's where the most confusing array-related thing comes into play: arrays are 0-indexed. That means, to access the first value in an array you put a 0 inside those square brackets. To access the last variable, you use an index of length_of_array - 1. For example:

language:c
int example_array[4] = {10, 20, 30, 40}; // Declare an array with four values
...
Serial.print(example_array[0]);  // Prints 10 (first value in array)
Serial.print(example_array[2]);  // Prints 30 (third value in array)
Serial.print(example_array[3]);  // Prints 40 (fourth value in array)
// Illegal: Serial.print(example_array[5]); // No good, there's no such thing.

You can create an array of two variables, or you can create an array of hundreds of variables. The size of the array is determined when you declare it. Your array can be of any type you wish.

for Loops

Time to introduce your first real loop: the for loop. for loops are used to repeat a block of code for a specified set of repetitions. Instead of looping indefinitely, like the loop() function, the for loop can be controlled to repeat once, twice, thrice, or thousands of times. It's up to you.

Here's the anatomy of a for loop:

language:c
for ( [variable declaration] ; [conditional]; [increment])
{
    // For loop statements
}

There are three important aspects to a for loop declaration:

  1. Variable declaration -- Each for loop allows you to declare and initialize a variable. That variable's scope is limited to the for loop and anything inside that loop.
  2. Conditional test -- This test determines when you break out of the for loop. As long as the result of the conditional is true, then the loop runs at least one more time. Once the result of the conditional test is false, we break out of the loop. This is kind of like an if statement inside the for loop. Traditionally you'll test the variable from the declaration section here.
  3. Variable increment -- The point of the for loop is to not loop indefinitely. Each time through, you want to get closer and closer to breaking that conditional statement. That's what the variable increment section is for. Traditionally, you'll increment the variable from the declaration section here.

Here's an example for loop that should run exactly five times:

language:c
for (int i=0; i<5; i = i + 1)
{
    // Loop through here 5 times.
    // i starts at 0
    // each time through i increases by 1
    // once i increases to 5, we break
    // out of this loop.
}

The order of operations of this loop looks a little something like this:

  1. Create a variable called i and set it to 0.
  2. Test i. If i < 5 we run through the for loop. If i >= 5 break out of the loop, we're done.
  3. After running through the loop, run the increment: add 1 to i.
  4. After the increment jump back up to step 2. And repeat until the condition becomes false (i >= 5).

In this case we'll exit the for loop after i increases to 5. We'll have run through it 5 times by then.

Arrays are the perfect partner for for loops. When you need to run through each variable in an array, you can place a variable in the index section, and for loop through each member of the array. We'll be doing just that in this experiment...

Sketch and Experiment

Here's the Sandbox_13_SoundDetecting.ino sketch. Read through the comments, they'll help re-inforce what you've just learned about array's and for loops.

language:c
    // Sandbox 13: Sound Detecting

/* Introducing the fabulously groundbreaking SOUND (Sandbox’s Over/Under Nominal 
   Decibels) System! Microphone check 1..2..1..2. With the SOUND you’ll always 
   have an adjustable sound level detector handy!

   In this experiment we’ll use the Sandbox’s on-board microphone to measure 
   volume levels and display them on the LEDs. The microphone produces a sound 
   wave, which is just another analog voltage that we can measure. The louder 
   the sound, the higher the amplitude of the wave and the larger the voltage.

   This sketch introduces the concept of arrays. Arrays are a collection of 
   variables, which can all be accessed and manipulated with a single variable
   name. To keep track of which variable within the array is being called, we
   use an index.

   We also use for loops for the first time in this experiment. For loops are a
   looping construct we use to run a block of code for a specified number of
   iterations. Instead of looping indefinitely, like we do with the loop()
   function, a for loop allows us to loop five, ten, twenty...however many times
   we need.
*/

const int slidePin = A3;    // A constant variable to store our slide pot pin
const int micPin = A2;      // A constant variable to store our microphone pin

// Now let's create an ARRAY of variables. An array is a collection of variables,
// all referenced with an array name and an INDEX. This is how we create an
// array containing 5 values:
const int ledPins[5] = {4, 5, 6, 7, 8};
// Our array is called "ledPins". Each value in the array can be referenced
// using a specific index, called within the square brackets directly following
// the array name. The index STARTS AT 0.
// For example, ledPins[0] is 4, ledPins[1] is 5, and ledPins[4] is 8 (the last
// value in the array).

void setup()
{
    // Set up our mic and slide pot pins as INPUTs
    pinMode(slidePin, INPUT);
    pinMode(micPin, INPUT);

    // Now let's use a for loop to set each ledPins as an input.
    // There are three parts to defining a for loop: (1) variable declaration,
    // (2) a test (like an if statment), (3) increment. As long as the test
    // returns true, we continuously run through the for loop. Each time through
    // the loop, the increment part happens. Eventually we want to exit the for
    // loop after everything we need to happen has happened.
    // In this example, we create a variable, i, and set it to 0. Each time 
    // through the loop we increase i by 1 (i++). At that point, if i is bigger
    // than 5, we exit the for loop.
    // We can also use the variable declared in the for loop. In this case we
    // use it to index the array. Arrays and for loops work hand-in-hand.
    for (int i=0; i<5; i++)
    {
        // First time through, set ledPins[0] (pin 4) to OUTPUT
        // 2nd time through, set ledPins[1] (pin 5) to OUTPUT
        // ...
        // 5th time through, set ledPins[4] (pin 8) to OUTPUT)
        // Then exit the for loop because i >= 5.
        pinMode(ledPins[i], OUTPUT);
    }
}

void loop()
{
    int maximum = analogRead(slidePin); // Read the slide pot sensor into a var
    int micReading = analogRead(micPin);    // Read the microphone value in

    // Again, we'll use a for loop to cycle through each LED in the ledPins
    // array.
    for (int i=0; i<5; i++)
    {
        if (micReading > ((maximum / 5) * (i+1)))
        {   // If the microphone reading is greater than a scaled value
            digitalWrite(ledPins[i], HIGH); // Turn on that LED
        }
        else
        {   // Otherwise, turn off that LED
            digitalWrite(ledPins[i], LOW);
        }
    }
}

Go ahead and upload the sketch, then make some noise at your Sandbox. Are the LEDs bouncing to your voice? If not, try tapping on the mic.

To adjust the sensitivity of the volume meter, move the slide pot up or down. With the slider set to the far right, it’ll take a really loud sound to make every LED turn on. But if you set the slider too low even the slightest noise will set the meter off.

Your Turn!

  • Can you rewrite the sketch to use the RGB LED instead of the white LEDs? Make it turn red when the volume is really loud, and blue and/or green otherwise. Bonus points for using analog outputs!

14. Opto-Theremin (Addon)

In this experiment we'll attach a speaker to the Sandbox, and turn it into a musical instrument! By using the light sensor to control our speaker's pitch, we can create a light-controlled theremin - a non-contact, electronic musical instrument.

Note: This experiment requires the Digital Sandbox Add-On Kit, purchased separately.

Background Information

By precisely modulating a pin, the Digital Sandbox can create electronic waves which, when routed through a loudspeaker, can produce a musical tone. We can program the Sandbox to control two characteristics of musical tone: pitch and duration.

A tone's pitch is what we perceive when we think of a note as being very high (screams, forks scratching plates, etc.) versus very low (like earth-rumbling bass). The pitch of a tone is very closely related to the frequency played through a speaker. If we toggle a pin from HIGH-to-LOW then LOW-to-HIGH 440 times per second, for example, it produces a 440 Hz (hertz) frequency - a "middle A" pitch. Humans can hear frequencies ranging from 20 (low-pitch, bass) to 20,000 Hz (high-pitch, "ow, my ears").

We can also program the duration of a tone - the length of time a pitch is played. In our program we'll use the delay function to set the duration. Playing a tone with the Sandbox is very easy. Just give it a pitch and it'll start toggling the output pin for you. Much like analog output, you can set it and forget it; the tone won't stop playing until you tell it to.

Active Parts

alt text

Code Components

This experiment introduces two functions related to sound-making, and another universally handy variable-altering function.

Playing (and Stopping) Tones

The tone function can be used to generate a sound wave and play it using an attached speaker or buzzer.

There are two parameters required to play a tone: a pin and a pitch. The pin should be set to whichever Arduino pin you have a buzzer connected to (don't forget to set it as an OUTPUT). The pitch should be set to the frequency of the tone you want to play.

Want to play an A440 out of pin 3? Here's the code:

language:c
tone(3, 440);   // Play a 440Hz wave on pin 3

As you'll quickly discover, the Sandbox isn't the most musical device out there. The tones it creates can be just a little-bit grating. If you ever need to halt a pin from playing a tone, use the noTone() function. You'll just need to specify which pin you want to noTone(), like this:

language:c
noTone(3);  // Stop playing "music" on pin 3

Map: map([value], [fromLow], [fromHigh], [toLow], [toHigh])

Arduino comes with all sorts of handy functions for manipulating variables, one of which is the map function. map translates a value from one range of values to another.

In our experiment, we want to map the 0-1023 input value of the light sensor, and map it down to a scales-worth (440-880) of tones for the speaker. Here's how we'll do it:

language:c
int lightSensorValue = analogRead(A1);
int frequency = map(lightSensorValue, 0, 1023, 440, 880);

That maps our frequency from the 0-1023 range to a proportional value in the 440-880 range.

Sketch and Experiment

Here is the 'Sandbox_14_OptoTheremin.ino` sketch for this experiment. Read through the comments and upload away!

language:c
    // Sandbox 14: Opto-Theremin (Add-on)

/* In this experiment we’ll attach a speaker to the Sandbox, and turn it into a 
   musical instrument! By using the light sensor to control our speaker’s pitch, 
   we can create a light-controlled theremin - a non-contact, electronic musical 
   instrument.

   By precisely modulating a pin, the Digital Sandbox can create electronic 
   waves which, when routed through a loudspeaker, can produce a musical tone. 
   We can program the Sandbox to control two characteristics of musical tone: 
   pitch and duration.

   This experiment introduces the tone() function, which can be used to play a
   specific tone out of a speaker attached to a pin.

   You'll also quickly discover the noTone() function, which you'll want to call
   before your ears start bleeding.
*/


// Let's create global variables to reference the pins of our in's and out's:
const int speakerPin = 3;   // The speaker is connected to 3
const int switchPin = 2;    // switch is connected to pin 2
const int sliderPin = A3;   // slider pot is connected to A3
const int lightPin = A1;    // light sensor is connected to A1
const int redPin = 9;       // Red LED is connected to pin 9
const int bluePin = 10;     // Blue LED is connected to pin 10
const int greenPin = 11;    // Green LED is connected to pin 11

void setup()
{
    // Set up our sensors (switch, slider, light) as INPUTs:
    pinMode(lightPin, INPUT);
    pinMode(switchPin, INPUT);
    pinMode(sliderPin, INPUT);

    // Set up our speaker and LEDs as OUTPUTs:
    pinMode(speakerPin, OUTPUT);
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);
}

void loop()
{
    if (digitalRead(switchPin)) // If the switch is in the "1" position:
    {
        digitalWrite(redPin, HIGH);     // Turn on the red LED
        digitalWrite(bluePin, HIGH);    // Turn on the blue LED
        digitalWrite(greenPin, HIGH);   // Turn on the green LED

        int light = analogRead(lightPin);   // Read in the light sensor
        // Convert the light sensor reading to a pitch frequency we can hear.
        // We can use the map function to convert a value from one range of
        // values to another, proportionally. This example maps light from
        // 0-60 to a proportional value between 440 and 880:
        int pitch = map(light, 0, 60, 440, 880);    

        // The tone function plays a pitch on a pin connected to a speaker.
        // In this case, we play the pitch we just calculated on the speakerPin
        // -defined pin.
        tone(speakerPin, pitch);
        // Once we call tone, that pitch will play until commanded to stop, or
        // a new pitch is defined. Let's delay for a short time, then run back
        // through and play another pitch.
        delay(5 + analogRead(sliderPin));
    }
    else    // Otherwise the switch is in the "0" poisition.
    {
        // noTone stops all sound coming out of the defined pin.
        noTone(speakerPin);         // Silence!
        digitalWrite(redPin, LOW);  // Turn red LED off
        digitalWrite(bluePin, LOW); // Turn blue LED off
        digitalWrite(greenPin, LOW);// Turn green LED off
    }
}

Then you'll need to attach the speaker to the expansion connector. Before you can do that, you will need to break off one group of three pins from the male breakaway headers. Now, you can connector the buzzer, with the top side facing down, to the expansion connector as shown in this image:

alt text

As fun as the opto-thermin may be, the sound is considered grating by some, so the code implements a simple ON/OFF function. Slide the switch over to the "1" position to turn the opto-theremin on.

Once the theremin is on, the speaker should start making noises. Try covering the light sensor; does the pitch change? We've turned the RGB LED white, so you can try to corral the light from that to control the light sensor.

You can adjust the duration of the tone by sliding the potentiometer. Slide the pot all the way to zero to get a really fast "zapper" sound, or slide to the right to create a soothing, slow sound.

Your Turn!

  • Try adding a "rest" function to your opto-theremin. Use the button to intermittently cut off the sound output.
  • Instead of using the Sandbox as a musical instrument, can you program it to play a written piece of music? Using a series of tones and delays, try reproducing the chorus of your favorite song!
  • Give yourself a hearing test! What's the highest frequency you can hear? Can you hear tones others can't? Can your pet hear pitches that you can't?

15. Serial Motoring (Addon)

Motors make the world go round. Well, not literally, but they make a lot things we use every day spin and actuate. There are tiny vibration motors in cell phones, speedy motors that spin CDs and Blu-Ray discs, and of course, massive engine motors that help propel our cars. In this experiment we'll explore one of the most fundamental motor types out there - DC motors - and we'll tell the Sandbox precisely how fast we want the motor to spin.

Note: This experiment requires the Digital Sandbox Add-On Kit, purchased separately.

Background Information

A DC motor turns electrical energy into a rotational, mechanical energy. DC motors are popular because they're very simple to control: Give them some voltage and they spin. You can control the speed of a motor much as you might control the intensity of an LED - with PWM - so in this experiment, we'll be using the analog output block to control the motor's speed.

This experiment also introduces serial input. Up to this point our conversations with the Sandbox have been very one-sided - the Sandbox has been outputting data to the serial monitor. Serial input allows us to send data to the Sandbox via the serial monitor.

Active Parts

alt text

Code Components

There are a couple new serial-related functions to discuss in this example. In addition to printing out to the Serial Monitor, we'll be reading from it. Two-way communication!

Reading In Serial Data

To begin, we need to know if any data has been sent from the Serial Monitor. We can use the Serial.available() function to find out how many, if any, characters are waiting for us. There's a good chance Serial.available() returns 0, which means there's no data to be read, but it could also return 1, 2, 3, or more. Here's how we usually use this function:

language:c
if (Serial.available() > 0)
{
    // do something with the Serial data, 
    // like Serial.read() or Serial.parseInt();
}

There are a few functions that can be used to read data in from the Serial Monitor. Serial.read() is one of the more common functions -- it reads in a single character at a time. In this experiment we'll be using Serial.parseInt(), which can be used to read in an integer value. This will be handy for our experiment because we want to read in values containing anywhere between 1 and 3 digits.

Here's an example using Serial.parseInt():

language:c
if (Serial.available() > 0)
{
    // Read a value in from Serial and store it in serial_in:
    int serial_in = Serial.parseInt();
}

When you do a Serial.parseInt() (or Serial.read()) the value of Serial.available() decreases by however many characters you read in. So you'll only be able to read a serially-sent value once.

Sketch and Experiment

Here's the Sandbox_15_SerialMotoring.ino sketch. You know the drill: read the comments and upload.

language:c
    // Sandbox 15: Serial Motoring

/* Motors make the world go round. Well, not literally, but they make a lot 
   things we use every day spin and actuate. There are tiny vibration motors in 
   cell phones, speedy motors that spin CDs and Blu-Ray discs, and of course, 
   massive engine motors that help propel our cars. In this experiment we’ll 
   explore one of the most fundamental motor types out there - DC motors - and 
   we’ll tell the Sandbox precisely how fast we want the motor to spin.

   In this experiment we'll use analog output to control a motor. We can PWM 
   them just like we do to dim LEDs.

   We'll also be exploring the subject of Serial input. In addition to Serial
   printing out to the serial monitor, we'll be reading data in from it too. To
   do that we'll use Serial.available() to check if any data is there, and
   Serial.parseInt() to read it in.
*/

const int slidePin = A3;    // Slide pot is connected to A3
const int motorPin = 3;     // DC motor is connected to pin 3
const int buttonPin = 12;   // Button is connected to pin 12

void setup()
{
    pinMode(slidePin, INPUT);   // Set the slide pot as an input
    pinMode(buttonPin, INPUT);  // Set the button as an input

    pinMode(motorPin, OUTPUT);      // Set the motor as an output
    digitalWrite(motorPin, LOW);    // Turn the motor off.

    Serial.begin(9600); // Initialize serial, set the baud rate to 9600
}

void loop()
{
    // Serial.available() will return 0 if nothing has been sent to the serial
    // monitor.
    // If one or more characters have been sent to the Serial Monitor, the
    // Serial.available() function will return that number (1 or greater).
    if (Serial.available()) // If data has been sent to the serial monitor
    {
        // Create a varaible, speed, and read the data from the serial monitor
        // into it. parseInt() reads in a number of characters and turns them
        // into a single integer value.
        int speed = Serial.parseInt();

        // Since we're using speed with analogWrite, we need to constrain its 
        // value between 0 and 255. The constrain function will do that for us!
        speed = constrain(speed, 0, 255);   // Constrain speed between 0 and 255

        // Print a message, tell us what we just told the Serial Monitor:
        Serial.print("Setting speed to: ");
        Serial.println(speed);

        // We can set the speed of the motor with the analogWrite function. It's
        // just like dimming an LED, but this time we're dimming the speed
        // of a motor.
        analogWrite(motorPin, speed);
    }

    // If the button is pressed, we'll halt the motor
    if (digitalRead(buttonPin))
    {
        // Set the motor output to LOW (off):
        digitalWrite(motorPin, LOW);

        Serial.println("Stopping!");    // Let us know through the serial monitor

        delay(250); // "Debounce" the switch, by delaying for a short moment
    }
}

After uploading, connect the motor’s black wire (GND) to the GND pin on the Sandbox’s add-on header. Then, connect the motor’s red wire to the OUT pin on the Sandbox’s add-on header. Your motor should now be connected to the Sandbox as shown in this picture:

alt text

Now open the Serial Monitor, type a number between zero and 255 into the box next to "Send," and then click that button. The Sandbox should respond with a message, and the motor should start spinning.

What happens when you send 255? How about zero? What happens when you send a number greater than 255 or less than zero (a negative)? Can you spot a block in the code that is restricting those values?

As a "safety mechanism," if you ever need to stop the motor from spinning, press the button to bring it to a halt.

Your Turn!

  • Try connecting something mechanical to the motor. Perhaps tape a narrow piece of paper to create a spinner and play Twister. Or add a few pieces of paper to create a fan. What else can you connect to the axle to take advantage of the rotational motion?
  • As a programming challenge, can you make the motor smoothly speed up or down when a new serial value is received?

16. Servo Sweeper (Addon)

DC motors are great for spinning an object at high speed with no regard for where it starts or stops. For many applications, though, it's important to precisely control the position of a motor. Wing flaps in a plane, steering mechanisms in RC cars, and robotic arm platforms are applications that benefit from motorized position control. For those applications we ditch the DC motor and whip out the servo!

Note: This experiment requires the Digital Sandbox Add-On Kit, purchased separately.

Background Information

A servo motor is like a DC motor with an internal controller and built-in sensors that help keep track of its shaft position. A servo motor knows, for example, if it's pointing at 15° or 115°.

Servos all have three wires that need connecting to: supply voltage, ground, and a signal. The voltage and ground connections supply the motor with power, and the control signal - a PWM output (surprise, surprise) - sets the position of the motor. Once the motor reaches the desired position, it stops until it is commanded to move to a new position.

Servo motors vary in their range of motion - the minimum and maximum angles they can point to. Only specialized, continuous rotation servos can rotate a full 360°; most have a stated range of motion between 90° and 180°. The servo we'll be using in this experiment has a 180° range of motion.

Active Parts

alt text

Code Components

In the final experiment, we're introducing you to one of the most powerful aspects of Arduino: importing libraries. Libraries are code written by Arduino or members of the Arduino community which go beyond the basic Arduino core to add unique features.

Using the Servo Library

In this case, we'll be using the Servo library, which includes all sorts of functions and data types to make controlling servo motors super-easy. To be able to use the Servo library, you first need to include it. That can be acheived with this statement, which should go at the very top of your code:

language:c
#include <Servo.h>  // Include the servo library.

Once you've included the library, the next step is to create a Servo variable -- an object you'll refer to throughout your sketch to control the servo. Here's how we create a Servo object named myServo:

language:c
Servo myServo;  // Create a servo object, which we'll use from here on out.

You'll usually want this line to go above the setup() function as well -- making it a global object. That way we can use it in both the setup() and loop() functions.

So far, so good. As with most everything, we need to initialize the servo. In this case, we need to attach a pin to it. Here's how we attach a servo to pin 3:

language:c
myServo.attach(3); // Attach myServo to pin 3

Notice how we're using the myServo object declared earlier. This line of code will usually go in the setup() function. Run it once, then it's attached for the rest of the program.

Finally, to set your servo to a specific position, you use the write() function. For example, to set your servo to a 55° angle, here's the code you'll need:

language:c
myServo.write(55);  // Set the servo to 55 degrees.

After executing that line, your Servo should immediately start spinning till it finds its position.

Sketch and Experiment

Here we go one more time! Upload the Sandbox_16_ServoSweeper.ino sketch and away you go.

language:c
    // Sandbox 16: Servo Sweeper

/* DC motors are great for spinning an object at high speed with no regard for 
   where it starts or stops. For many applications, though, it’s important to 
   precisely control the position of a motor. Wing flaps in a plane, steering 
   mechanisms in RC cars, and robotic arm platforms are applications that 
   benefit from motorized position control. For those applications we ditch the 
   DC motor and whip out the servo!

   This is the first experiment where you'll import an external library. In this
   case we'll be using the Servo library. Follow the comments as we show how the
   library is included and then used.
*/

// Servos are controlled using the Servo LIBRARY. To include a library, use the
// #include "library" statement.
#include <Servo.h>  // Include the servo library

// To use the servo library, we need to create an instance of the Servo class.
// This is like creating a variable of type "Servo". We can call our servo 
// anything -- in this case we'll call it "myServo"
Servo myServo;  // Create an instance of the servo library

// Create variables for our input and ouput components:
const int switchPin = 2;    // Switch is connected to pin 2
const int slidePin = A3;    // Slide pot is connected to A3
const int servoPin = 3;     // Servo is connected to pin 3

void setup()
{
    // Set the switch and slide pot pins as INPUTS.
    pinMode(switchPin, INPUT);
    pinMode(slidePin, INPUT);

    // To use the myServo object we created above, we need to attach it to a
    // pin. Use the attach function, and use servoPin as the pin value.
    myServo.attach(servoPin); // Attach the myServo object to our servoPin:
}

void loop()
{
    if (digitalRead(switchPin)) // If the switch is in the "1" position:
    {
        // Read in the value of the slide pot, and map it to our servo's
        // position range (0-180):
        int angle = map(analogRead(slidePin), 0, 1023, 0, 180);

        // Use the write function to set the angle of the servo:
        myServo.write(angle);
    }
}

After the code has been uploaded, connect the servo with the three pin male breakaway header to the Sandbox. Make sure the servo’s black wire (GND) is lined up to the GND pin on the Sandbox, as shown here:

alt text

This program allows you to control the position of the servo motor with the sliding potentiometer. Slide all the way to the right to set the servo to 180°, and all the way to the left sets it to 0°.

The servo will only move if the switch (connected to pin 2) is set to ON. If you leave the switch in the ON position, you can see how fast the motor responds to the servo block. If you move the switch to OFF, set the position of the slide pot, and set the switch to ON, you can see the speed and control mechanisms of the motor.

Your Turn!

  • What happens if you try to rotate the servo beyond 180° (change the last value in the map function)?
  • Think of something to attach to the servo! You could add a clock hand to make a weird, half-circle clock. Or add a papercraft hand, and have your Sandbox give you a high-five!

Resources and Going Further

Congratulations on completing the Digital Sandbox Experiment Guide! If you're looking for more resources devoted to the Digital Sandbox, Arduino, or ArduBlock, check out these links:

Now that you've introduced yourself to the world of electronics, hardware, programming, and Arduino, check out some of these tutorials and products to continue your learning.

  • What is An Arduino? -- If you're ready to step into a more open-ended development platform, we encourage you to continue working with Arduinos. This tutorial will help get you started.
  • SparkFun Inventor's Kit -- If you're looking for more hardware to continue your learning journey, the SIK is the next logical step.

SIK product shot

This kit includes an Arduino, breadboard, and tons of wires, buttons, LEDs, and more components. It also includes a guide with a series of experiments that introduce you to the Arduino programming language (no more blocks!) and electronics.