Physical Computing Final Project

Christian Camareno
5 min readMay 20, 2024

Christian Camareno

My final project idea was to create a neatly packaged device that lights up LEDs based on data from a gyroscope. I chose to do it because the gyroscope sensor, MPU-6050, seemed like a powerful component and I wanted to try it for myself.

Contraption Powered On

The premise of the device is that it is a handheld box that you can tilt to see LEDs change color in real time based on rotational data on all three axes; x, y, and z. Additionally, in the serial monitor in the Arduino IDE, you can see constant updates producing mapped data for each axis, as well as a message based on the rotation.

Results and Flavor Text

By default, the box lights a green light since it isn’t being titled. It feel stable. But as you turn the box in any direction, if the average of the three axes is above or below a certain parameter, a corresponding LED will light up. Since the final presentation, I have adjusted the code to make the LEDs blink if they power the yellow or red led. The red LED will flash faster than the yellow, to make a sense of urgency. Below is the updated code used to make the run, scroll to the side to view comments explaining what’s happening.

#include "Wire.h"       
#include "I2Cdev.h"
#include "MPU6050.h" //component that makes the device function

const int redLedPin = 2;
const int yellowLedPin = 5;
const int greenLedPin = 6;
bool doBlink = false; //how we will determine blinking

unsigned long previousMillis = 0; // Store the last time the LED was updated, unsigned longs are ideal for storing large values and handling overflow of time-based values
const long intervalYellow = 500; //basically delay between blinks, so less rapid on yellow
const long intervalRed = 250;

MPU6050 mpu; //declare component
int16_t ax, ay, az; //declare accelerometer axes
int16_t gx, gy, gz; //declare gyroscope axes, what we are using mostly this project

//this struct is essentially a class used to initialize variables
struct MyData {
byte X;
byte Y;
byte Z;
};
//this line establishes that the keyword data will refer to variables stored in the struct MyData
MyData data;

void setup()
{
Serial.begin(9600);
Wire.begin();
mpu.initialize();
pinMode(redLedPin, OUTPUT);
pinMode(yellowLedPin, OUTPUT);
pinMode(greenLedPin, OUTPUT);
}

void loop()
{
unsigned long currentMillis = millis(); //deal with large values equal to milliseconds currently in loop
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //get axes data for accelerometer and gyroscope
data.X = map(ax, -17000, 17000, 0, 255 ); // X axis data
data.Y = map(ay, -17000, 17000, 0, 255); // Y axis data
data.Z = map(az, -17000, 17000, 0, 255); //Z axis data
//Print Mapped Coordinates
delay(250);
Serial.print("Axis X = ");
Serial.print(data.X);
Serial.print(" ");
Serial.print("Axis Y = ");
Serial.println(data.Y);
Serial.print("Axis Z = ");
Serial.println(data.Z);
// Calculate average of x, y, and z axes
float average = (abs(data.X) + abs(data.Y) + abs(data.Z)) / 3.0;
// Print average to serial monitor for debugging
Serial.print(" Average: ");
Serial.println(average);
//Lighting Pins
//adjust led ranges, add processing sketch in real time
if (average > 160 && average <=255) {
digitalWrite(redLedPin, LOW);
digitalWrite(yellowLedPin, LOW);
digitalWrite(greenLedPin, HIGH);
Serial.print("That's all you got?");
}
else if (average > 120 && average <= 160) {
doBlink = check(); //doBlink bool's value determined by declared function check()
if (doBlink) {
// If the current time is greater than the interval time, toggle the LED
if (currentMillis - previousMillis >= intervalYellow) {
// Save the last time you blinked the LED
previousMillis = currentMillis;

// If the LED is off, turn it on and vice versa
if (digitalRead(yellowLedPin) == LOW) {
digitalWrite(yellowLedPin, HIGH);
} else {
digitalWrite(yellowLedPin, LOW);
}
}
} else {
// Ensure the LED is turned off if the condition is not met
digitalWrite(yellowLedPin, LOW);
}
digitalWrite(redLedPin, LOW);
//digitalWrite(yellowLedPin, HIGH);
digitalWrite(greenLedPin, LOW);
Serial.print("Okay dude, that's enough.");
}
else {
doBlink = check();
if (doBlink) {
// If the current time is greater than the interval time, toggle the LED
if (currentMillis - previousMillis >= intervalRed) {
// Save the last time you blinked the LED
previousMillis = currentMillis;

// If the LED is off, turn it on and vice versa
if (digitalRead(redLedPin) == LOW) {
digitalWrite(redLedPin, HIGH);
} else {
digitalWrite(redLedPin, LOW);
}
}
} else {
// Ensure the LED is turned off if the condition is not met
digitalWrite(redLedPin, LOW);
}
//digitalWrite(redLedPin, HIGH);
digitalWrite(yellowLedPin, LOW);
digitalWrite(greenLedPin, LOW);
Serial.print("STOP! PUT ME FLAT!");
}

}
//any number modulo (%) 2 is essentially asking if a number has a remainder if divided by a certain number
bool check() {
if (millis() / 5000 % 2 == 0) { //if the millisecond value / 5000 has a remainder of 0 if divided by 2 (if it is even)
return true;
} else {
return false;
}
}

As for enclosure, I decided to use lego because the idea of using garbage and old toys to make something new seemed too enticing. The interior of the lego structure has some pizza box cardboard as padding to prevent shifting of wires within the enclosure. The display window is an old ZipLoc bag that I cut up and secured by having lego bricks hold it in place. The enclosure was surprisingly tight and had little to no shifting of the wires inside. I also rearranged the wires from their original positions so the Arduino and breadboard would be positioned vertically stacked instead of horizontally to improve how practical it was to hold it.

Overall, I really enjoyed working with the MPU-6050. It can register so much information at once and can really elevate a project to the next level. A great step up from the 360 degree tilt sensors in the Arduino starter kits, which could only register tilt as booleans instead of large bits of numerical data.

--

--