[pcomp week 4] The Lonely But Tender Ghost v.2, now with sound! (analog outputs)

IMG_0272

It was fun playing with speakers and servo motors this week. After doing the labs, I focused mostly on doing things with sound, but in the future I’d like to spend more time experimenting with servos…

In the tone lab, I had some fun with the pitch library, and found it pretty easy to change the song played to something else:

I wanted to continue building on my lonely ghost project from last week. When I last left it, I had hand-sewn an FSR that caused an RGB LED to change colors depending on how hard you squeezed it. It was supposed to express “feeling” with the colors — green was good, red was bad. At that point, using colors were the only output.

I added a speaker to my breadboard and worked on adding sound in addition to color as feedback for the toy’s feelings.

IMG_0270

The first thing I did was add the tone() function to the code so that when the variable “force” was 3 — that is, pushed the hardest, the speaker would make a noise in addition to having the LED turn red.

I thought the ghost could be made to be a bit needier. What if it got lonely if you didn’t pay attention to it for a period of time?

I used the millis() function to count the number of milliseconds that have passed whenever the ghost was squeezed. I then set a variable called lonelyTime, which was the amount of time it that could pass before the ghost got lonely. When the last time squeezed subtracted from the current millisecond count exceeded lonelyTime, I had the speakers make a tone. It would stop when you squeezed it again.

(I used the same method to make the LED blink when you weren’t squeezing the FSR, which I thought was a more natural neutral state than having the light just be white.)

This was nice, but all of the tones sounded pretty boring and static. That’s when I realized I could use the pitches library, like in the tone lab, to compose custom sounds for each state. I ended up making three:

in pain
in pain
happy
happy
Screenshot 2015-09-29 13.17.28
lonely

I was a bit surprised by how much more effective the custom sounds were at expressing feeling compared to the basic speaker tones.

Now, the ghost feels much more like a pet or a needy toy. When he’s lonely, the light will turn yellow and he’ll make the lonely sound until you squeeze him. If you squeeze him gently, the light turns green and he makes the happy sound. If you squeeze him too hard, he’ll make a distressing sound and the light will turn red. The blink effect makes it feel more alive as well.

Check out the video (with sound) here:

My Arduino code is below.

/* The Lonely But Tender Ghost likes it when you squeeze him gently but doesn't like it when you squeeze him too hard.
 * The light turns green and he makes a happy sound when you squeeze him just right.
 * If you squeeze too hard the light turns red and he makes a distressed sound.
 * Otherwise, the light blinks white.
 * If a period of time passes, he will get lonely. 
 * If he's lonely, the light will turn yellow and he'll make a lonely sound until you squeeze him again.*/

#include "pitches.h"

long lastSqueezeTime = 0;   //calculating when the last time the ghost was squeezed
long lonelyTime = 10000;    //the amount of time the ghost can go unsqueezed before making a sound
long blinkInterval = 1000;  //interval at which to blink
long blinkMillis = 0;
int redState = LOW;
int greenState = LOW;
int blueState = LOW;

//melody when the ghost is squeezed to force 2 and the light is green:
int greenMelody[] = {
  NOTE_C4, NOTE_E4, NOTE_G4, NOTE_C5
};
int greenNoteDurations[] = {
  16, 16, 16, 16,
};

//melody when the loneliness timer hits:
int lonelyMelody[] = {
  NOTE_G6, NOTE_FS6, NOTE_G6, NOTE_A6
};
int lonelyNoteDurations[] = {
  16, 16, 16, 16,
};

//melody when the ghost is squeezed to force 3 and the light is red:
int redMelody[] = {
  NOTE_GS7, NOTE_FS7, NOTE_F7, NOTE_A7
};
int redNoteDurations[] = {
  32, 32, 32, 32,
};


//setting the pins for each leg of the RGB LED
const int redPin = 11;
const int greenPin = 10;
const int bluePin = 9;

//creating a variable for the value of the force sensor
int forceSensorValue = 0;

void setup() {
  Serial.begin(9600);
  //setting the RGB LED as an output
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
}

void loop() {
  forceSensorValue = analogRead(A0); //reading the value of the FSR on A0
  int force = map(forceSensorValue, 800, 1000, 1, 3); //creating 3 different states of force: 1, 2, and 3

  long currentMillis = millis(); //setting currentMillis to the same as the millisecond counter

  //if you don't squeeze the FSR before the loneliness timer runs out...
  //...turn the light yellow (red + green)
  //...and play the lonelyMelody
  if (currentMillis - lastSqueezeTime > lonelyTime) {
    redState = HIGH;
    greenState = HIGH;
    blueState = LOW;
    for (int lonelyThisNote = 0; lonelyThisNote < 4; lonelyThisNote++) {
      int lonelyNoteDuration = 1000 / lonelyNoteDurations[lonelyThisNote];
      tone(8, lonelyMelody[lonelyThisNote], lonelyNoteDuration);
      delay(lonelyNoteDuration);
    }
  }

  //if you squeeze the FSR really hard to force 3...
  //...make the light red...
  //...and play the redMelody
  if (force == 3) {
    redState = HIGH;
    greenState = LOW;
    blueState = LOW;
    for (int redThisNote = 0; redThisNote < 4; redThisNote++) {
      int redNoteDuration = 1000 / redNoteDurations[redThisNote];
      tone(8, redMelody[redThisNote], redNoteDuration);
      delay(redNoteDuration);
    }
  }
  //if you squeeze the FSR just right to force 2...
  //...make the light green...
  //...play the greenMelody...
  //...and reset the loneliness timer
  else if (force == 2) {
    redState = LOW;
    greenState = HIGH;
    blueState = LOW;
    lastSqueezeTime = currentMillis;
    for (int greenThisNote = 0; greenThisNote < 4; greenThisNote++) {
      int greenNoteDuration = 1000 / greenNoteDurations[greenThisNote];
      tone(8, greenMelody[greenThisNote], greenNoteDuration);
      delay(greenNoteDuration);
    }

  }
  //if you don't push the FSR at all, or it's only pushed to force 1...
  //...turn the light off, but also make it blink
  else {
    digitalWrite(redPin, LOW);
    digitalWrite(greenPin, LOW);
    digitalWrite(bluePin, LOW);

    if (currentMillis - blinkMillis >= blinkInterval) {
      // save the last time you blinked the LED
      blinkMillis = currentMillis;
      // if the LED is off turn it on and vice-versa:
      if (redState == LOW && greenState == LOW && blueState == LOW) {
        redState = HIGH;
        greenState = HIGH;
        blueState = HIGH;
      }
      else {
        redState = LOW;
        greenState = LOW;
        blueState = LOW;
      }
    }

  }
  Serial.println(forceSensorValue);
  digitalWrite(redPin, redState);
  digitalWrite(greenPin, greenState);
  digitalWrite(bluePin, blueState);
}

 

One thought on “[pcomp week 4] The Lonely But Tender Ghost v.2, now with sound! (analog outputs)”

  1. This is a fabulous blog post. There’s plenty of clear written detail to explain what’s going on, and the tone is conversational and easy to follow. The images complement the writing well, to illustrate what’s going on. You do a good job of describing your goal at the beginning, and then illustrate all you learned with the video at the end. Keep this up, it’s a pleasure to read.

    Also, it really is amazing how much more evocative a behavior is when you use more than one sensory output, isn’t it? The lights and the sound together work nicely. Great work.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: