Stupid Pet Trick: Nodding Cockatoo

If you experience difficulties in figuring out the shape, stand three meters away, squint your eyes, tilt your head to the left, and make a 196° degree turn on your tiptoes. That should do it.

The prompt for this project, creating a “stupid pet trick” incorporating analog and digital input and output, made me think of a stupid pet trick from my own home. This is Sisi, my pet cockatoo named after Empress Elizabeth of Austria (fun fact), who imitates others’ nodding motions as shown in the following video:

Guests always find the interaction with Sisi amusing, more so if they whistle and she “replies” with a whistle of her own. Knowing that this is considered a fun trick, I decided to simulate Sisi’s nodding and sounds (which I wasn’t able to get to, unfortunately). For this purpose, I considered using a Neopixels grid on which I could alternate different images or frames of a cockatoo, each in a different position, such that when changing rapidly the Neopixels create the illusion of a moving bird. These changes would be triggered by a distance (IR rangefinder) analog sensor, to detect whether the user moves her/his head. The sensor would thus aid in determining which cockatoo frame to use at each moment.

I couldn’t incorporate sound by the deadline, but I had thought of placing a piezo buzzer behind the Neopixels grid and a microphone near the distance sensor. If the user were to speak near the cockatoo, the bird would “respond” with whistling sounds (the idea could be better implemented with actual recordings of my cockatoo, instead of the tones produced by the buzzer, as suggested by my professor).

The code is as follows, based on the Adafruit Neopixel “simple” example code by Shae Erisson:

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h>
#endif

const int PIN = 6;
//output for Neopixels strip

const int NUMPIXELS = 72;
//grid has dimensions 8 x 9 pixels (72 LEDs)

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

const int sensor = A5;
//analog input from IR rangefinder

//the following constants are three matrices, each representing one frame (the cockatoo at its highest, in the center or middle position, and at its lowest (the three positions together simulate the nodding motion) 
//each array within the larger array represents one LED, of which its color is defined by RGB values
const int birdUp[][3] = {
 //row 1
 {255, 255, 255},
 {255, 255, 255},
 {255, 127, 39},
 {255, 127, 39},
 {255, 127, 39},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 2
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},

//row 3
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 4
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},

//row 5
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 6
 {0, 0, 0},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 
 //row 7
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {0, 162, 232},
 {140, 39, 241},
 {0, 162, 232},
 {255, 127, 39},

//row 8
 {0, 0, 0},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {255, 255, 255},
 {255, 242, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 9
 {0, 0, 0},
 {255, 242, 0},
 {255, 242, 0},
 {255, 242, 0},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0}
 };

const int birdMiddle[][3] = {
 //row 1
 {255, 255, 255},
 {255, 255, 255},
 {255, 127, 39},
 {255, 127, 39},
 {255, 127, 39},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 2
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},

//row 3
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 4
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 5
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {0, 0, 0},
 
 //row 6
 {255, 127, 39},
 {0, 162, 232},
 {140, 39, 241},
 {0, 162, 232},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 7
 {0, 0, 0},
 {0, 0, 0},
 {255, 242, 0},
 {255, 255, 255},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {0, 0, 0},

//row 8
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 242, 0},
 {255, 242, 0},
 {255, 242, 0},
 {0, 0, 0},

//row 9
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0}
 };

const int birdDown[][3] = {
 //row 1
 {255, 255, 255},
 {255, 255, 255},
 {255, 127, 39},
 {255, 127, 39},
 {255, 127, 39},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 2
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},

//row 3
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 4
 {0, 0, 0},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 
 //row 5
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {255, 255, 255},
 {0, 162, 232},
 {140, 39, 241},
 {0, 162, 232},
 {255, 127, 39},

//row 6
 {0, 0, 0},
 {0, 162, 232},
 {0, 162, 232},
 {0, 162, 232},
 {255, 255, 255},
 {255, 242, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 7
 {0, 0, 0},
 {255, 242, 0},
 {255, 242, 0},
 {255, 242, 0},
 {255, 255, 255},
 {255, 255, 255},
 {0, 0, 0},
 {0, 0, 0},

//row 8
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},

//row 9
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0},
 {0, 0, 0}
 };

void setup() {
 pixels.begin();
}

void loop() {
 int distance = analogRead(sensor);
 int distanceNum = round(distance/100);
 //the sensor gives values ranging from around 250 to 650
 //divided by 100 and using the round() function, these values range from 3 to 7, which are more convenient for the program
 if(distanceNum > 4){
 for(int i=0;i<NUMPIXELS;i++){
 int red = birdUp[i][0];
 int green = birdUp[i][1];
 int blue = birdUp[i][2];
 pixels.setPixelColor(i, pixels.Color(red, green, blue));
 pixels.show();
 }
 }
 //if the sensor's value is 5, 6, or 7, the cockatoo appears at its highest (I wanted the range of the highest height to be the largest, given that it is easiest to make out the shape of the bird in this frame)
 else if(distanceNum == 4){
 for(int i=0;i<NUMPIXELS;i++){
 int red = birdMiddle[i][0];
 int green = birdMiddle[i][1];
 int blue = birdMiddle[i][2];
 pixels.setPixelColor(i, pixels.Color(red, green, blue));
 pixels.show();
 }
 }
 //if the sensor's value is 4, then the cockatoo appears at its middle height
 else{
 for(int i=0;i<NUMPIXELS;i++){
 int red = birdDown[i][0];
 int green = birdDown[i][1];
 int blue = birdDown[i][2];
 pixels.setPixelColor(i, pixels.Color(red, green, blue));
 pixels.show();
 }
 }
 //if the sensor's value is 3, then the cockatoo appears at its lowest height
 }

To create the grid, I connected nine strips of eight Neopixels each with soldered wires.

This is how the strips of Neopixels looked after I joined them, placed on a beautiful background that steals the show.

I found the image that I “translated” into the LEDs online. I edited it such that it was easy to pixelate, and altered the colors so that they didn’t appear to blend (similar color tones are difficult to distinguish for adjacent Neopixels). These four images demonstrate the process to reach the ideal pixelated cockatoo:

I placed the grid on a piece of wood, and created a pixelated tree trunk (made of colored craft wood sticks) under it, to give the illusion that the cockatoo is perched on a tree. The grid is covered with a somewhat translucent paper, to diffuse the lights and make the image’s recognition easier. The distance between the paper and the lights aids in the diffusion, and it would have been better to increase it just a bit more in this case. Nevertheless, the image turned out to be clear enough. The following picture shows the lit cockatoo (at its highest position, determined by one of the “frames”) over the tree. The distortion of the lights was caused by my phone’s camera; the cockatoo’s shape is actually clearer.

This is the complete setup, where the distance sensor is placed on the white box on the left, the Neopixels grid is on the right, and the RedBoard that connects both (as well as the laptop that powers it) is on the chair in the middle.


The sensor is placed in this arrangement because of two reasons. I chose to put the sensor inside the frame because it allows me to control the  values obtained from it. IR rangefinders work in such a way that after a certain point, the values it reads are reflected. For instance, if I place my hand over the sensor and gradually raise it, so as to increase the distance between the two, the values might read 250, …, 300, …, 400,… 500, …, 600, …, 500, … 400, …, 300, …, 250, … Thus, they are reflected around about 600, and the sensor cannot straightforwardly differentiate between the repeated values. The frame limits the sensor’s readings from about 250 to 650, thus eliminating the problem of the reflected values.

The sensor is also on top of a large box because I considered having the user move her/his head to trigger the cockatoo’s motion. Thus, I would need to have a structure that allowed this movement to occur comfortably. Just the frame wouldn’t have done it: placing it on a table, for example, would have required the user the bend in order to nod over the sensor. The tall box facilitated this aspect of the interaction. However, I decided to switch the motion from the head to the hands. Firstly, not all users’ heads will fit inside the frame, and even if they do, the movement is restricted. Secondly, it is easier to control the hand’s placement over the sensor’s beam, which is quite narrow, than the head’s.

This is the cockatoo as it nods, activated by a hand:

 

Ninja Panda (Stupid Pet Trick)

Once I knew the assignment was called “Stupid Pet Trick”, I knew that I wanted to use an actual toy pet for this assignment, instead of using boxes and cardboard, which I’ve been using for the previous assignments.

What can one do with a pet? There are many things that come to mind, but I remembered one thing I used to do to my cat (out of pure love, of course) when I was around 8 years old. Sometimes I would hug my cat so tight that he wouldn’t like it and make a sound that would make me let him go.

For this project, I’ve decided to modify the idea of hugging a little bit, since I can do more with a toy pet than I can with a real one. I wanted to put RGB LEDs behind the pet’s eyes, so that they change colour depending on how hard you squeeze the pet (indicating pet’s level of discomfort). For the squeezing part I’ve put a pressure sensor inside the pet. Also, I did not forget about the sound that my cat used to make, so if someone applies too much pressure on the pet, it would also make annoying sound that should make the person who is squeezing it to release the grip.

At first, I went to the store and bought this adorable seal, but then I found it too cute to dismember 

Shreya was kind enough to offer me her “ugly” panda, and I found it a better toy at least because it was bigger in size, and also not that cute, which made my butchering process less heartbreaking.

The hardest part was taking panda’s eyes out, because they were literally screwed into a piece of plastic (I assume so that kids cant pull them out easily), so it took me an hour at least, and two pair of pliers to take the eyes out without damaging the fabric around the eyes.

They were also coloured green on the inside, and I wanted them transparent, so I had to use acetone to get rid of the paint. Wiping it with a tissue did not get much of paint off, so I decided to let the eyes soak in the acetone. It did a great job getting rid of the paint, but it also melted the eyes…

So they looked really creepy:

Because of this, I ended up using googly eyes, which in my opinion fit the bear even better, and was easier to put LEDs behind them.

 

To put everything inside the bear, I had to take the stuffing out, put the pressure sensor inside his body, glue the LEDs behind his eyes, place the piezo buzzer in his head and gently put the stuffing back without pulling any wires. This is how it looks from behind with all the wires sticking out

My first thought was to saw it back together, but then I would not be able to open it in case I needed to fix or readjust something. I ended up using a velcro strip that I stitched to one side of the fabric, and there was even no need to stitch another strip to the other side, as velcro would perfectly stick to the fabric the bear was made out of.

This is how its back looks closed

After fitting everything in I just had to slightly adjust the placement of the LEDs, but that was no big of a deal. It ended up being more stable and pull-resistant than I thought it would be, and does not even look that terrible after I restuffed it .

Here is how it looks from the front, and how it reacts when someone puts a lot of pressure on its stomach. Still looks a little creepy 🙂

 

Stupid Pet Trick: Scaredy-“Cat”

My ultimate goal for the Stupid Pet Trick assignment was to create something that would have a personality of its own, not simply a systematic machine that seemed to just follow inputs and emit outputs. Thus, while brainstorming, I started considering projects that were related to making users “play around” with the creature, which is why I decided to make a creature that was scared of the light.

Description:

The creature would be surrounded by 4 LEDs (controlled by the user through knobs) and would avoid light by finding and rotating through a servo to a location that had no emitting light. Apart from its movements, this creature would also change color according to how “threatened” it felt, which would also add a lot to the personality aspect. Thus, in the end I decided to make an octopus (since they have the ability to camouflage and change colors) that would be, in a normal state blue, but as more and more LEDs turned on, it would change color until it would turn to a blinking red color when it couldn’t find a location without light. If all the LEDs were on, the octopus would move frantically and emits a blinking red light.

I placed the octopus in a ocean-like environment to follow up with the “underwater” theme. Initially, I wanted to make four predator figures (such as the shark head seen below) . And place LEDs inside them to add more to the “threatened” aspect of the project. However, due to time constraints and my struggles with paper art and origami, I decided to instead implement the lights into the environment to make it seem as if the creatures emitting the lights were hiding instead.

Final setup – the cables were extremely difficult to hide, so I simply placed them inside another box.
Underwater environment
Example of shark head I wanted to use to place LEDs in.

The interaction:

In this project, the user serves as the “controller” of the predators. Through four knobs, it can control with LEDs are on and their brightness. According to the changing LED states, the octopus shies away to locations where there are no emitted lights. Thus, in the end, it creates a fun interaction between the user and the creature in which the creature tries to escape the user’s lights. Below is a video of the final outcome. (Due to several complications explained later in this post, I was only able to record a small video sample, showing the final outcome where all LEDs turn on. The other videos attached in this post show the change in color and the octopus’ other movements)

The process:

Here are some of the initial sketches I made:

There were three ways I could elaborate this project:

  1. Utilizing solely photocells to find the location with the least light.
  2. Using solely input to give the impression that the creature was thinking (when it was actually just considering inputs)
  3. Combining both the photocells and input.

In the end, I decided to follow the third approach to make a more complete creature, since it would consider its environment through the photocells while also ensuring accuracy through the input information. However, I did not envision it would lead to so many complications in the process.

For instance, certain issues I had to consider was the amount of photocells I would use, their positions, how to limit the intruding light from the environment, whether the lights should be dimmed, and how to implement all this in the code.

My first trial was using two photocells that would be placed in either side of the creature. The code would then consider which photocell perceived the least light and would rotate to that position. The video below shows my first prototype for this method. At first, it seemed to be working correctly when only one LED was turned on. However, when two adjacent LEDs were on, the octopus would stay fidgeting between them and would not rotate anywhere else. Due to this issue, I decided to use the photocells for the creature to identify which LEDs were on and then move according to specific patterns using those inputs.

The final code had two states: The first: using the photocells to identify which LEDs were on. The second: “escaping mode” where it would move according to specific LED patterns (using an if-statement in the code).

After having the code and the initial prototype ready, it was necessary to actually make the model stable and functional, since the one I had before was composed of many small wires only connected through alligator clips. Thus, I started the whole process of soldering all my LED wires, the photocells, and the RGB LED. However, after this long process, when I attached the photocells and the RGBB LED onto the servo, the surface was too heavy and would fall out of place. I tried using super glue, sticky tack, and even the screws, but the surface that would hold the octopus kept falling out of place or would not follow the servo’s movements. This rose up the greatest complication during this process, since I realized that the cables I used for the soldering were too rigid, restricting the surface from rotating completely between the 0º and 180º positions. Thus, I had to discard of the previous solders I had done and instead soldered the smaller, more flexible wires together to allow for more movement. Nevertheless, even after fixing this aspect, the surface on the servo was still too heavy, and to complement this, for some still unknown reason, the photocells stopped working correctly. Thus, I decided to (painfully) discard of the photocells aspect in my project and instead focus only on input readings.

Sample of way the model worked with the photocells. As can be seen, the creature moves according to switching on of the LEDs.

Below is a video of the whole model actually working (without placing the creature on top yet):

Some aspects I took away from this project is how difficult (even more so than software) hardware can be, since even the smallest of complications in any part of the setup can lead to devastating results. I experienced this firsthand in this project when all my components and code functioned correctly at first but, once all were placed together, they were all too heavy for the octopus to rotate accurately without falling off. Thus, some improvements I would make would be to use another, bigger servo and ensure a method of attaching all the components more securely as to avoid this outcome. However, regardless of this, this project helped me learn more about the complications of “simulating” thought on the part of a robot, but was overall very fulfilling.

Final code:

#include <Servo.h>

Servo myservo; // create servo object to control a servo
int servoDegree; 

int photopin1 = A5; // analog pin used to connect potentiometer 1
int photopin2 = A4; // analog pin used to connect potentiometer 2

int photoReading1; //yellow (right) // variable to read the value from the analog pin
int photoReading2; // blue (left) // variable to read the value from the analog pin

int maxLight = 120;
int minLight = 7;

int knob4 = A2;
int knob2 = A3;
int knob3 = A0;
int knob1 = A1;

int yellowLed1 = 11;
int yellowLed2 = 6;
int yellowLed3 = 5;
int yellowLed4 = 13; //10

//RGB LED setup
const int RED_PIN = 4;
const int GREEN_PIN = 12;
const int BLUE_PIN = 2;

//variables for octopus' knowledge
bool led1 = false;
bool led2 = false;
bool led3 = false;
bool led4 = false;

bool start = true;
bool decideLight = true; 
bool countingState = true;
bool escapingState = false;
bool escapingState2 = false;
bool blueColor = true;

//counter for octopus' knowledge (know which LED is on)
int led1Counter = 0;
int led2Counter = 0;
int led3Counter = 0;
int led4Counter = 0;
int rightCounter = 0;
int leftCounter = 0;


void setup() {
 myservo.attach(9); // attaches the servo on pin 9 to the servo object
 pinMode(yellowLed1,OUTPUT);
 pinMode(yellowLed2,OUTPUT);
 pinMode(yellowLed3, OUTPUT);
 pinMode(yellowLed4, OUTPUT);
 pinMode(RED_PIN, OUTPUT);
 pinMode(GREEN_PIN, OUTPUT);
 pinMode(BLUE_PIN, OUTPUT);
 
 Serial.begin(9600);
}

void loop() {

 //placing servo in start position


 if (start == true){
 myservo.write(90);
 start = false;
 Serial.println("THIS WENT INTO START");
 delay(1500);
 }

 if (blueColor == true){
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 
 //adjusting values of potentiometer
 photoReading2 = analogRead(photopin2); 
 photoReading1 = analogRead(photopin1); // reads the value of the potentiometer (value between 0 and 1023)
 
 delay(1);
 photoReading1 = map(photoReading1, maxLight, minLight, 180, 0); // scale it to use it with the servo (value between 0 and 180)
 photoReading2 = map(photoReading2, maxLight, minLight, 180, 0); // scale it to use it with the servo (value between 0 and 180)

// 
 Serial.println("reading 1: ");
 Serial.println(photoReading1);
 Serial.println(photoReading1);
 Serial.println(photoReading1);
//// delay(500);
 Serial.println("reading 2:");
 Serial.println(photoReading2);
 Serial.println(photoReading2);
 Serial.println(photoReading2);
// delay(500);
 
// Serial.println("servo previous position");
// Serial.println(servoDegree);
 
// delay(600);

 //identifying knob state for leds 
 int knobValue1 = analogRead(knob1);
 delay(1);
 int knobValue2 = analogRead(knob2);
 delay(1);
 int knobValue3 = analogRead(knob3);
 delay(1);
 int knobValue4 = analogRead(knob4);
 delay(1);
 
 int mappedValue1 = map(knobValue1,0,1023,0,255);
 int mappedValue2 = map(knobValue2,0,1023,0,255);
 int mappedValue3 = map(knobValue3,0,1023,0,255);
 int mappedValue4 = map(knobValue4,0,1023,0,255);
 
 int constrainedValue1 = constrain(mappedValue1,0,255);
 int constrainedValue2 = constrain(mappedValue2,0,255);
 int constrainedValue3 = constrain(mappedValue3,0,255);
 int constrainedValue4 = constrain(mappedValue4,0,255);


 //setting up potentiometers for LEDs
 analogWrite(yellowLed1, constrainedValue1);
 analogWrite(yellowLed2, constrainedValue2);
 analogWrite(yellowLed3, constrainedValue3);
 analogWrite(yellowLed4, constrainedValue4);

 //checking for servo's current position
 int servoDegree = myservo.read();
// Serial.println(servoDegree);

 delay(1000);
 
 //using photocell: helping octopus perceive which light is on 
 
 if (decideLight == true){
 blueColor = false;
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 
 Serial.println("THIS WENT INTO THE LOOP");
 
 //adding the counters to see which side to go to
 if (countingState == true){
 if(photoReading1 < photoReading2){ //if right is brighter than left
 rightCounter += 1;
 Serial.print("COUNTER RIGHT");
 }
 else if(photoReading1 > photoReading2){ //if left is brighter than right
 leftCounter += 1;
 Serial.print("COUNTER LEFT");
 }
 }
 
 //moving servo once
 
 if (rightCounter > 1){
 myservo.write(50); //go to right side
 Serial.println("MOVE RIGHT");
 countingState = false;
 Serial.println("COUNTING STATE FALSE");
 delay(1500);


 if (photoReading1 > photoReading2){ //adding up counter for led2
 led2Counter += 1;
 Serial.print("COUNTER LED 2");
 }
 else if (photoReading1 < photoReading2){ //adding up counter for led1
 led1Counter += 1;
 Serial.print("COUNTER LED 1");
 }

 if (led2Counter ==2){ //if counter gets to 2
 led2 = true;
 decideLight = false;
 escapingState = true;
 Serial.println("LED2 TRUE");
 }
 else if (led1Counter == 2){ //if counter gets to 2
 led1 = true;
 decideLight = false;
 escapingState = true;
 Serial.println("LED1 TRUE");
 }
 }

 else if (leftCounter > 1){
 myservo.write(130);
 Serial.println("MOVE LEFT");
 countingState = false;
 Serial.println("COUNTING STATE FALSE");
 delay(1500);

 if (photoReading1 > photoReading2){ //adding up counter for LED 4
 led4Counter += 1;
 Serial.print("COUNTER LED 4");
 }
 else if (photoReading1 < photoReading2){ //adding up counter for LED 3
 led3Counter += 1;
 Serial.print("COUNTER LED 3");
 }

 if (led4Counter == 2){
 led4 = true;
 decideLight = false;
 escapingState = true;
 Serial.println("LED4 TRUE");
 }
 else if (led3Counter ==2){
 led3 = true;
 decideLight = false;
 escapingState = true;
 Serial.println("LED3 TRUE");
 }
 }
 
 }


Serial.println("KNOB 1 VALUE");
Serial.println(knobValue1);
Serial.println(knobValue1);
Serial.println(knobValue1);
Serial.println("KNOB 2 VALUE");
Serial.println(knobValue2);
Serial.println(knobValue2);
Serial.println(knobValue2);
Serial.println("KNOB 3 VALUE");
Serial.println(knobValue3);
Serial.println(knobValue3);
Serial.println(knobValue3);
Serial.println("KNOB 4 VALUE");
Serial.println(knobValue4);
Serial.println(knobValue4);
Serial.println(knobValue4);

 
 //later logic: using what the octopus has learned to move
 if (escapingState == true){
 Serial.println("ESCAPTING STATE: MOVING");
 if (led1 == true){
 myservo.write(150);
 escapingState = false;
 escapingState2 = true;
 }
 else if(led2 == true){
 myservo.write(150);
 escapingState = false;
 escapingState2 = true;
 }
 else if(led3 == true){
 myservo.write(10);
 escapingState = false;
 escapingState2 = true;
 }
 else if(led4 == true){
 myservo.write(10);
 escapingState = false;
 escapingState2 = true;
 }
 }

 if (escapingState2 == true){
 Serial.println("LOOP FOR PATTERNS");
 if (knobValue4 > 200 && knobValue3 < 200 && knobValue2 < 200 && knobValue1 < 200){
 myservo.write(10);
 Serial.println("GREEN");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, HIGH);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 > 200 && knobValue3 > 200 && knobValue2 < 200 && knobValue1 < 200){
 myservo.write(30);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 > 200 && knobValue3 > 200 && knobValue2 > 200 && knobValue1 < 200){
 myservo.write(10);
 Serial.println("RED");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 < 200 && knobValue3 > 200 && knobValue2 < 200 && knobValue1 < 200){
 myservo.write(30);
 Serial.println("GREEN");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, HIGH);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 < 200 && knobValue3 > 200 && knobValue2 > 200 && knobValue1 < 200){
 myservo.write(150);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 < 200 && knobValue3 > 200 && knobValue2 > 200 && knobValue1 > 200){
 myservo.write(150);
 Serial.println("RED");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 < 200 && knobValue3 < 200 && knobValue2 < 200 && knobValue1 > 200){
 myservo.write(130);
 Serial.println("GREEN");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, HIGH);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 < 200 && knobValue3 < 200 && knobValue2 > 200 && knobValue1 > 200){
 myservo.write(100);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 < 200 && knobValue3 > 200 && knobValue2 < 200 && knobValue1 < 200){
 myservo.write(100);
 Serial.println("GREEN");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, HIGH);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 > 200 && knobValue3 > 200 && knobValue2 < 200 && knobValue1 > 200){
 myservo.write(60);
 Serial.println("RED");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 > 200 && knobValue3 < 200 && knobValue2 > 200 && knobValue1 > 200){
 myservo.write(100);
 Serial.println("RED");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 > 200 && knobValue3 < 200 && knobValue2 < 200 && knobValue1 > 200){
 myservo.write(100);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 < 200 && knobValue3 > 200 && knobValue2 < 200 && knobValue1 > 200){
 myservo.write(150);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 > 200 && knobValue3 < 200 && knobValue2 > 200 && knobValue1 < 200){
 myservo.write(50);
 Serial.println("PURPLE");
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if (knobValue4 < 200 && knobValue3 < 200 && knobValue2 > 200 && knobValue1 < 200){
 myservo.write(150);
 Serial.println("GREEN");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, HIGH);
 digitalWrite(BLUE_PIN, LOW);
 }
 else if (knobValue4 < 200 && knobValue3 < 200 && knobValue2 < 200 && knobValue1 < 200){
 myservo.write(90);
 Serial.println("BLUE");
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, HIGH);
 }
 else if(knobValue4 > 200 && knobValue3 > 200 && knobValue2 > 200 && knobValue1 > 200){
 Serial.println("NO ESCAPE. ALL RED");
 myservo.write(60);
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 delay(500);
 myservo.write(120);
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 delay(300);
 myservo.write(60);
 digitalWrite(RED_PIN, HIGH);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 delay(300);
 digitalWrite(RED_PIN, LOW);
 digitalWrite(GREEN_PIN, LOW);
 digitalWrite(BLUE_PIN, LOW);
 myservo.write(120);
 delay(500);
 //CRAZY COLORS 0% blue
 }
 }
 
 
}

Assignment 5: D.O.G.E.

For my Stupid Pet Project, I chose to make an actual pet!

(Thanks to Arame for participating in the demonstration!)

The Dog-Owner Googly-eyed Experience (DOGE, for short), is a model dog that offers several interactions to its owner. Each one of the interactions has an effect on DOGE’s emotions, communicated with a front face panel with 64 LEDs.

(The DOGE’s face is my own work.)

The DOGE can experience three emotions – it can be Happy, Neutral, and Angry. They are communicated by lighting up different arrangements of LEDs.

In addition, when the DOGE is happy, it wags its tail happily, as it is attached to a servo that sways between 45 and 135 degrees. If the DOGE’s emotion changes to Neutral or Angry, the tail rests pointing downward, at what the servo understands as 90 degrees.

(Credit to Nikki Joaquin for drawing the dog’s tail for me!)

To signal to the owner that they did something to make the DOGE happy, the happy LEDs flash briefly and a higher-pitched tone plays. Conversely, when the owner does something that makes the DOGE angry, the angry LEDs flash alongside a lower-pitched tone.

The user can interact with the DOGE in three different ways: An FSR on the back of its head, a tilt sensor, and a series of switches on top of the DOGE’s back.

The owner has a choice of pressing the FSR gently or harshly. Pressing the sensor lightly will make the DOGE slightly happy (+1), while pressing it harshly will make the DOGE slightly angry (-1). In terms of the analog ranges of the sensor, there is a buffer zone between the two possibilities, so that the owner is less likely to make the DOGE angry when they intend to make it happy.

The tilt sensor actually acts as more of a vibration sensor – it contains a small ball that breaks a connection between two pins when the sensor is disturbed. This functionality is used by the DOGE to detect mean behavior by its owner. If the owner slaps the DOGE or kicks it, the tilt sensor will detect it and significantly reduce the DOGE’s happiness (-5). You will have to do a lot of nice things to your DOGE to make it happy again after one of these!

The tilt sensor is the small golden cylinder in the image below.

Finally, the DOGE’s hair switches. These are groups of wires on the DOGE’s back that detect petting motions by the owner.

When the wires are made to touch the strip of conductive fabric, the DOGE becomes happier. But it is not so easy – the owner has to pet the dog in the right direction (from head to tail) and has to start with the group of wires that is closest to the head. The owner has four seconds to complete the petting motion to achieve maximum happiness. As the owner moves the hand along the back of the DOGE, the happiness reward increases progeressively – +1 for the first group, +2 for the second… +4 for the last. This means +10 happiness for each completed petting motion! It pays to learn how to pet your DOGE!

This was easily the most complex project I made for the class. For starters, the LED panel has 64 LEDs in total, easily qualifying this project into the “LED Fetishism” category of IM projects. 64 resistors and more than 320 wires were required to make the whole thing work; by my count I used about half of all the available wires in the lab.

Second of all, this project is HUGE.

That’s me!

Working with it required hours of me leaning into a box that was big enough for a whole IKEA lamp to fit, and was heavy enough that it wanted to topple unless weighed down by a heavy wooden stool.

Third of all, the veritable jungle of wires required a lot of tidying up in order for me to even gain access to the insides of the box, and meticulous color-coding of wires to maintain my sanity when debugging hardware problems.

Fourth, my original wiring arrangement – which used a group of parallel resistors in series with a group of parallel LEDs proved suboptimal. Current would flow through different LEDs differently based on minuscule differences in their internal resistance, threatening to burn out the less-resistant LEDs, while leaving the others too dim (see this illustration for a visualization of the situation). To give each LED its own resistor, I had to dive into the jungle of wires and replace 64 of them with a resistor.

This was relatively easy to do because of a system that holds the DOGE’s head upright and its back sloped while allowing the box to be opened and its contents inspected.

The system that supports the DOGE’s back and head

This system also helps with routine fixes of non functional LED, caused by wire connections becoming loose:

Nonfunctional LEDs in the DOGE’s eyebrows

The code for the DOGE was more complicated than the violin, mainly because each one of the sensors and output devices required its own finite state machine and its own timer.

#include <Servo.h>

Servo myServo;

int const greenMouthPin = 3;
int const greenTonguePin = 4;
int const redMouthPin = 2;

int const servoPin = 5;

int const piezoPin = 6;

int const tiltPin = 7; // 1 normally, 0 when hit

int const firstHairPin = 8; // closest to head
int const secondHairPin = 9;
int const thirdHairPin = 10;
int const fourthHairPin = 11;

int const fsrPin = A0;

int currentHappiness = 0; // initial value
int previousHappiness = 0; // initial value

int const happyThreshold = 5; // the minimum happiness for dog to be happy
int const angryThreshold = -5; // the maximum happiness for dog to be angry

bool isHappy = false;
bool isNeutral = false;
bool isAngry = false;

long servoTimerCurrentMillis = 0;
long servoTimerPreviousMillis = 0;

int const servoSpeed = 10; // the speed of happy tail wagging
bool servoReversed = false;
int servoPos = 90;

long hairTimerCurrentMillis = 0;
long hairTimerPreviousMillis = 0;

int const hairLimit = 4000; // the time the user has to finish petting the dog
int currentHair = 0;
int const hairPositiveHappinessMultiplier = 1; // happiness from petting the dog

long fsrTimerCurrentMillis = 0;
long fsrTimerPreviousMillis = 0;

int const fsrThreshold = 200; // the time the FSR waits after touch before starting to sense (to give the user chance to choose pressure)
int const fsrLimit = 1000; // the time between two FSR readings after a reading has been made
int const fsrMinPositiveTouch = 200; // FSR threshold for gentle touch
int const fsrMaxPositiveTouch = 600; // FSR limit for gentle touch
int const fsrMinNegativeTouch = 700; // FSR threshold for mean touch
bool fsrWaiting = true;
bool fsrEnabled = false;
int const fsrPositiveHappiness = 1; // happiness from gentle touch
int const fsrNegativeHappiness = -1; // unhappiness from mean touch

long tiltTimerCurrentMillis = 0;
long tiltTimerPreviousMillis = 0;

int const tiltLimit = 1000; // the time between two tilt sensor readings after a reading has been made
int const tiltNegativeHappiness = -5; // unhappiness from kicking or slapping

int const soundTime = 200; // the time sounds are played for, also determines emotion LED flashing
int const positiveSound = 1047; // the positive sound's frequency
int const negativeSound = 262; // the negative sound's frequency

void setup() {
  pinMode(greenMouthPin, OUTPUT);
  pinMode(greenTonguePin, OUTPUT);
  pinMode(redMouthPin, OUTPUT);
  
  myServo.attach(servoPin);
  
  pinMode(piezoPin, OUTPUT);
  
  pinMode(tiltPin, INPUT);
  
  pinMode(firstHairPin, INPUT);
  pinMode(secondHairPin, INPUT);
  pinMode(thirdHairPin, INPUT);
  pinMode(fourthHairPin, INPUT);
  
  pinMode(fsrPin, INPUT);
  
  //Serial.begin(9600);
}

void loop() { 
  // setup 
  servoTimerCurrentMillis = millis();
  hairTimerCurrentMillis = millis();
  fsrTimerCurrentMillis = millis();
  tiltTimerCurrentMillis = millis();
  
  // setting state
  isHappy = false;
  isNeutral = false;
  isAngry = false;
  
  if (currentHappiness >= happyThreshold) isHappy = true;
  if (currentHappiness > angryThreshold && currentHappiness < happyThreshold) isNeutral = true;
  if (currentHappiness <= angryThreshold) isAngry = true;
  
  // setting face
  digitalWrite(greenMouthPin, LOW);
  digitalWrite(greenTonguePin, LOW);
  digitalWrite(redMouthPin, LOW);
  
  if (isNeutral == true || isHappy == true) digitalWrite(greenMouthPin, HIGH);
  if (isHappy == true) digitalWrite(greenTonguePin, HIGH);
  if (isAngry == true) digitalWrite(redMouthPin, HIGH);
  
  // hair
  if (currentHair == 0 && digitalRead(firstHairPin) == HIGH) {
    // if the first hair was detected
    hairTimerPreviousMillis = hairTimerCurrentMillis;
    currentHair += 1;
    currentHappiness += currentHair*hairPositiveHappinessMultiplier;
  }
  if (currentHair > 0 && hairTimerCurrentMillis-hairTimerPreviousMillis > hairLimit) {
    // if the hair timer runs out
    currentHair = 0;
  }
  if (currentHair > 0 && currentHair <= 4) {
    if (digitalRead(firstHairPin+currentHair) == HIGH) {
      currentHair += 1;
      currentHappiness += currentHair*hairPositiveHappinessMultiplier;
    }
  }
  
  // servo
  if ((servoTimerCurrentMillis-servoTimerPreviousMillis) >= servoSpeed) {
    servoTimerPreviousMillis = servoTimerCurrentMillis;
 }
  if (isHappy == true) {
    if ((servoTimerCurrentMillis-servoTimerPreviousMillis) == 0) {
      if (servoReversed == false && servoPos < 135) servoPos += 1;
      if (servoPos >= 135) servoReversed = true;
      if (servoReversed == true && servoPos > 45) servoPos -= 1;
      if (servoPos <= 45) servoReversed = false;
    }
  }
  if (isHappy == false) {
    servoPos = 90;
  }
  myServo.write(servoPos);
  
  // fsr
  if (fsrWaiting == true && analogRead(fsrPin) > fsrMinPositiveTouch) {
    // touch detected
    fsrWaiting = false;
    fsrEnabled = true;
    fsrTimerPreviousMillis = fsrTimerCurrentMillis;
  }
  if (fsrTimerCurrentMillis-fsrTimerPreviousMillis > fsrLimit) {
    // the fsr timer runs out
    fsrEnabled = false;
    fsrWaiting = true;
  }
  if (fsrEnabled == true && fsrTimerCurrentMillis-fsrTimerPreviousMillis > fsrThreshold && fsrTimerCurrentMillis-fsrTimerPreviousMillis < fsrLimit && analogRead(fsrPin) > fsrMinPositiveTouch && analogRead(fsrPin) < fsrMaxPositiveTouch) {
    fsrEnabled = false;
    currentHappiness += fsrPositiveHappiness;
  }
  if (fsrEnabled == true && fsrTimerCurrentMillis-fsrTimerPreviousMillis > fsrThreshold && fsrTimerCurrentMillis-fsrTimerPreviousMillis < fsrLimit && analogRead(fsrPin) > fsrMinNegativeTouch) {
    fsrEnabled = false;
    currentHappiness += fsrNegativeHappiness;
  }
  
  // tilt
  if (digitalRead(tiltPin) == LOW && tiltTimerCurrentMillis-tiltTimerPreviousMillis > tiltLimit) {
    // only if the time is higher than the time limit
    tiltTimerPreviousMillis = tiltTimerCurrentMillis;
    currentHappiness += tiltNegativeHappiness;
  }
  
  // sound
  if (currentHappiness != previousHappiness) {
    digitalWrite(greenMouthPin, LOW);
    digitalWrite(greenTonguePin, LOW);
    digitalWrite(redMouthPin, LOW);
    delay(soundTime/4);
    
    if (currentHappiness > previousHappiness) {
      tone(piezoPin, positiveSound, soundTime);
      digitalWrite(greenMouthPin, HIGH);
      digitalWrite(greenTonguePin, HIGH);
      delay((3*soundTime)/4);
    }
    if (currentHappiness < previousHappiness) {
      tone(piezoPin, negativeSound, soundTime);
      digitalWrite(redMouthPin, HIGH);
      delay((3*soundTime)/4);
    }
  }
  
  previousHappiness = currentHappiness;
  
  //Serial.println(fsrWaiting);
  
  delay(1);
}

Finding Dory :)

For my “Stupid Pet Trick” I wanted to do something that was simply… adorable. I hoped to create something sweet, silly, and uncomplicated. For that reason, I decided to go ahead and make a project that involved one of the cutest Pixar characters of all time: baby Dory. I mean, look at her:

I decided to make my project a little “game” (though really more like a simple task) in which the user helps bring Dory and her parents together (for those who remember, that was the main idea of the movie “Finding Dory”). The user has two photo cell sensors (one for each hand) and is able to use those to help Dory and her parents; the catch is that if the user only uses one hand at a time (i.e. only presses the sensor that is connected to the servo that has Dory on it, or only presses the sensor that is connected to the servo that has her parents on it), then the correlating party (Dory or her parents) will actually move farther away from each other. Only if the user uses both hands with the sensors at the same time will Dory and her parents move closer together, eventually leading them to be together and triggering the huge Neopixel heart behind them. (The idea for the concept comes from the movie: both Dory and her parents were constantly looking for each other – without both parties looking, they never would have found each other again.)

The project is shown in the video below (but I highly suggest watching it here, on Youtube, instead – since the video on the blog usually lags):

While creating this project I encountered a variety of obstacles, the most severe of which had to do with the servos:

  • First of all, using three servos on one Arduino (without an external power source) can cause issues, but fortunately I was able to tweak the code so that the servos were able to function well regardless.
  • Secondly, to make the servos all do exactly what I wanted required quite a lot of math/thinking deeply about the angles of each servo at all times. For example, I wanted the servos (with Dory and her parents) to move gradually rather than to make big jerks; moreover, because of positioning, the two servos have to move in opposite directions always. These considerations, combined with many others, led to a lot (and I mean a lot) of debugging and the need to continuously make small edits to the code.
  • Thirdly, keeping things attached well to the servos was difficult (as per usual). I used popsicle sticks to attach the characters to the servos, and used a lot of sticky tack to connect them. The major problem that I still have is that these popsicle sticks are still a bit too heavy for the servo, and sometimes lead to the head of the servo (the part that twirls) to pop off.
  • Additionally, the photo cell sensors are problematic in the same way that they always are: depending on the light in the room, they read very different numbers. To avoid this issue, I tested the project at the very spot where I sit in the class.
  • Lastly, because there are two photo cell sensors involved, it is likely that the user might accidentally cast a shadow on a sensor that he/she is not intending to affect.

Below I include copies of the code I used in this project. I used two different files (“FindingDory” and “OceanLights”) because I have two different red boards: at the top of my project is a string of Neopixels that are simply there for aesthetics. I did not want them to take power away from my servos/sensors, so I attached them to a different red board altogether that ensures that they are always on.

//FindingDory file

#include <Servo.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
   #include <avr/power.h>
#endif

Servo doryServo;
Servo parentsServo;
Servo togetherServo;

Adafruit_NeoPixel heartLights;

int doryDistanceSensor = A0;
int parentsDistanceSensor = A1;

//names of the sensor data
float dorySensorDistance, parentsSensorDistance;
//names of the servo angles
int doryCurrentAngle, parentsCurrentAngle, toMoveAngle, togetherCurrentAngle;
//min and max values for reading of photo cell sensors
float minThresh = 150.0f;
float maxThresh = 750.0f;

void setup() {
   doryServo.attach(9);
   parentsServo.attach(10);
   togetherServo.attach(11);

   heartLights = Adafruit_NeoPixel(27, 5, NEO_GRB +    NEO_KHZ800);

   pinMode(doryDistanceSensor, INPUT);
   pinMode(parentsDistanceSensor, INPUT);
 
   //initial angles for each servo
   doryCurrentAngle = 60;
   parentsCurrentAngle = 120;
   togetherCurrentAngle = 0;
 
   //write the initial angles to the servos
   doryServo.write(doryCurrentAngle);
   parentsServo.write(parentsCurrentAngle);
   togetherServo.write(togetherCurrentAngle);

   //"begin" makes sure all LEDs are off (at 0)
   heartLights.begin();
   //"show" is like writing the value (0) to the LEDs
   heartLights.show();

   Serial.begin(9600);
}

void loop() {
   //read sensors
   dorySensorDistance = analogRead(doryDistanceSensor);
   parentsSensorDistance =  analogRead(parentsDistanceSensor);

   //if Dory and parents are both detected
   if (dorySensorDistance <= maxThresh &&    parentsSensorDistance <= maxThresh) {
      toMoveAngle = dtoa2(dorySensorDistance);
      if (toMoveAngle > doryCurrentAngle) {
         doryServo.write(toMoveAngle);
         doryCurrentAngle = toMoveAngle;
      }
 
      toMoveAngle = dtoa1(parentsSensorDistance);
      if (toMoveAngle < parentsCurrentAngle) {
         parentsServo.write(toMoveAngle);
         parentsCurrentAngle = toMoveAngle;
      }
   }

   //if only Dory is detected
   else if (dorySensorDistance <= maxThresh) {
      toMoveAngle = dtoa1(dorySensorDistance);
      if (toMoveAngle < doryCurrentAngle) {
         doryServo.write(toMoveAngle);
         doryCurrentAngle = toMoveAngle;
      }
    }

    //if only parents are detected
    else if (parentsSensorDistance <= maxThresh) {
       toMoveAngle = dtoa2(parentsSensorDistance);
       if (toMoveAngle > parentsCurrentAngle) {
          parentsServo.write(toMoveAngle);
          parentsCurrentAngle = toMoveAngle;
       }
   }

   //what to do when dory and parents meet in the middle
   if ((doryCurrentAngle > 150) && (parentsCurrentAngle < 30)) {
      togetherCurrentAngle = 150; //family photo come up
      doryCurrentAngle = 180; //dory go down
      parentsCurrentAngle = 0; //parents go down
      doryServo.write(doryCurrentAngle);
      parentsServo.write(parentsCurrentAngle);
      togetherServo.write(togetherCurrentAngle);
 
      //setting the color of the lights
      for(int i=0; i<heartLights.numPixels(); i++) {
   heartLights.setPixelColor(i, heartLights.Color(150,0,0));
      }

      //turn lights on
      heartLights.show();
      //lights stay on for 5 seconds
      delay(5000);
      setup();
   }
}

//formula for distance to angle 1
int dtoa1(float photoDist) {
   return float((photoDist - minThresh) / (maxThresh-minThresh)) * 180;
}

//formula distange to angle 2
int dtoa2(float photoDist) {
   return float((maxThresh - photoDist) / (maxThresh-minThresh)) * 180;
}
//OceanLights file

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
   #include <avr/power.h>
#endif

Adafruit_NeoPixel oceanLights;

void setup() {
   oceanLights = Adafruit_NeoPixel(26, 6, NEO_GRB + NEO_KHZ800);
   oceanLights.begin();
   oceanLights.show(); // Initialize all pixels to 'off'
}

void loop() {
   rainbowCycle(20);
}

void rainbow(uint8_t wait) {
   uint16_t i, j;

   for(j=0; j<256; j++) {
      for(i=0; i<oceanLights.numPixels(); i++) {
         oceanLights.setPixelColor(i, WheelGB((i+j) & 255));
      }
      oceanLights.show();
      delay(wait);
   }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
   uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
     for(i=0; i< oceanLights.numPixels(); i++) {
        oceanLights.setPixelColor(i, WheelGB(((i * 256 / oceanLights.numPixels()) + j) & 255));
     }

     oceanLights.show();
     delay(wait);
   }
}

uint32_t WheelGB(byte WheelPos) {
   WheelPos = 255 - WheelPos;
   if(WheelPos < 85) {
      return oceanLights.Color(0, WheelPos * 3, WheelPos * 3);
 }
   if(WheelPos < 170) {
      WheelPos -= 85;
      return oceanLights.Color(0, WheelPos * 3, 255 - WheelPos * 3);
 }
   if(WheelPos < 255) {
      WheelPos -= 170;
      return oceanLights.Color(0, 255 - WheelPos * 3, 0);
   }
}

Enclosure References

Box designer: http://boxdesigner.connectionlab.org/

Hollow project enclosure: http://makezine.com/projects/hollow-2×4-project-enclosure/

Examples from Aaron:

Makezine examples: http://makezine.com/slideshow/15-fantastic-project-enclosures/

Response to Roberto Casati: The Definiton of Design ?

The word design can conjure up different images for different people. The way we perceive design is fluid and can change based on time and place. A contemporary ideal of design, for example, can conjure up images of sleek, metallic, and defined shapes with minimalistic features.
I didn’t particularly agree with creating a comparison between designers, artists, and engineers as exclusive categories. I believe that all of these categories give and take from each other. Moreover, each individual may see themselves and their work in a different way. One engineer’s approach to creating an end product may not be the same as another engineer’s approach. I felt that Mr. Casati was trying too hard not to create a definition of design, and in doing so inadvertently boxed design into its own set of categories of what it is and what it is not.
I left the talk feeling confused as to what his message was, and not really sure what he meant by many of his ideas. I did like the example of the kettle that he gave – which did not at first appear to be a kettle. It made me question what we perceive as design – and whether knowing the functionality of a product influences whether it is design or not design. I believe that design is a very broad category, and when you consider functionality of whatever it is you are designing .. you as an individual can come to a conclusion of whether it is ‘good design’ or ‘bad design’ based on your own user experience.
It’s also interesting to consider how design varies across different fields. A technologist is going to think of design differently than an architect for example. “Principles” of design are just ideas that are widely accepted among a majority of people, and interestingly I believe that creating so called principles of design is quite limiting for the field. However, design is an interesting thing to consider because when you are a designer, you are thinking with the end in mind for the user or audience of your product. To change “design norms” is to change the mentality or views of a large group of individuals. However, these shifts have certainly occurred considering that the way we perceive design is constantly shifting.

Star Spangled Banner: In Lights!

I was so interested in seeing the creative ways in which people could use a simple Piezo buzzer to produce the melodies of songs. Not being a musically inclined person myself, I found the combination of notes fascinating and even more beautiful once I was able to map them to different light patterns.

I was inspired by the Star Spangled Banner melody that I found on GitHub, considering that I call the US my home, and recent political events have certainly changed the way I perceive my home and the way that people might perceive me in my home. I wanted to play with the tempo of the melody with different buttons that are pressed on the arduino circuit.

I used red white and blue LED lights that alternate to the different notes in the Star Spangled Banner, and it creates a nice effect to look at along with listening to the tune that many people can recognize.

Unfortunately, my failure in this project was making the code to change the tempo of the song work with the buttons. I’ve later realized that it may have been due to the way the piezo speaker works, and that maybe the entire melody needs to play before I am able to press a new button that changes the tempo. In any case, I will continue to investigate this problem and figure out how to make it work.

I originally wanted to make an instrument that you can blow on to produce different sound frequencies, but the Electret Mic proved to be beyond my capabilities with Arduino so far. My next goal will be to work with this!

A better version of DJ station (Almost)

For the music instrument assignment I’ve decided to sort of expand on my last assignment, where LEDs would light up depending on the distance of the hand(head, whatever one puts) from the infrared light sensor. So for this project I am using the same infrared light sensor, which is connected to the piezo. Now, depending on the distance of the hand from the sensor, piezo will be making sound of different frequency.

Since I am using two piezo buzzers, one of them is programmed to output frequencies between 31Hz (the minimum that it can produce on the Red Board we are using) and 2000Hz, and the other one is set to 2001Hz to 5000Hz. I chose 5000Hz to be the highest frequency just because above that is makes REALLY annoying sound, however, the difference in the timber is not that noticeable any more.(Although initially I had one piezo have the range between 31Hz and 10000Hz, and the second one 10001Hz to 20000Hz, but it did not work that well.)

 

At first, I tried making two piezos buzz at the same time, while both of them were connected to one Red Board, however, once I had one piezo working the way I wanted to and then copy-pasted the code and changed it just a bit to fit the other piezo, the first one stopped working the way it should, and the second one never produced a single sound. As I figured later, it is really hard to make two piezos buzz simultaneously when connected to the same Red Board, unless I use some very fancy tone library that I had no idea how to use really, as the language used in the library was something I have not encountered before.

 

Thus, I ended up using two Red Boards, one for each piezo, with the pretty much the same code, except for the difference in the frequency setting for the piezo. I wish I have decided to use two Red Boards before spending 3 to 4 hours trying to figure out how to make two piezos buzz at the same time from the same Red Board. However, I think this was still a great experience trying to figure it out.

Another very useful thing that I was introduced to was mapping, as instead of writing hundreds of lines of code so that one value I was getting from analog corresponded to a certain frequency range, I just had to map the value with the frequency range, which is just a couple of lines of code.

Here is how it looks:

const int analog = A0;
const int buttonPin = 2;
const int piezoPin = 3;
int frequency;
int frequency1;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
// put your main code here, to run repeatedly:
int analogValue = analogRead(analog);

Serial.print("button: ");
Serial.print(digitalRead(buttonPin));
Serial.print(" sensor 1: ");
Serial.println(analogValue);


if(digitalRead(buttonPin) == LOW){
frequency=map(analogValue,180,600,31,2000);
tone(piezoPin, frequency);
delay(20);


}
}

To make it look less chaotic and hide all the wires that I’m using, I have decided to reuse the same box I used for the previous project. And so I stuffed both Red Boards and all the wires in the box.

This is how it ended up looking and how it works: