Project 1 – Intro to Physical Computing: Student Work spring 2019 https://courses.ideate.cmu.edu/60-223/s2019/work Intro to Physical Computing: Student Work Wed, 15 Sep 2021 20:28:12 +0000 en-US hourly 1 https://wordpress.org/?v=5.0.21 Double Transducer: The Space Box https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-the-space-box/ Mon, 18 Feb 2019 14:29:16 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6065
The outside view of the completed Space Box
Inner components of the completed Space Box
Video of the Space Box in progress

Narrative: Appearing as a simple, unwired blue box with a planet carved into the top, The Space Box is designed to be placed upon a desk to create a visual representation of how appropriate the space between the user and the person sitting next to them is. When the ultrasonic sensor on the side of the box detects that an object is close by, it triggers the rotation of a motor inside of the box, which causes the color gradient going through an LED strip visible through the planet carving on the lid of the box to change much quicker, drawing the neighboring person’s eye and reminding them to move out of the user’s personal space.

Progress and Discussion:

Learning how to wire and program an LED strip to split the color wheel into a gradient across the four lights. It took us 4 libraries to finally be able to work with this particular strip, as it had four wires (power, ground, clock in, and data in), rather than three, as most libraries were designed.
Justin working on creating CAD models of gears to laser cut to attach to the motor and potentiometer inside of the box so that the digital output of the motor could be read as an analog input by the potentiometer as the gear on the motor rotated the position of the potentiometer.
The final CAD sketch of the above
The laser cut gear attached to the servo motor
All of the components ready to assemble, including the original LED lights, and the batteries that would be used to power the Arduino and LED strip inside of the box so it could have no external wires. The hole in the side of the box for the ultrasonic sensor to face out of is visible, as is the design on the top of the box through which the LEDs are visible
The next day, our LED strip’s colors seemed to become dramatically less vibrant, and began to appear solid white, though neither the code nor the wiring was changed. In this picture, we have removed the lights from the box and are trying to determine whether there are any wiring issues contributing to their dysfunction. There weren’t, so we simply substituted a different type of LED strip and altered our code accordingly.

From the moment this project was announced, we knew we wanted a chance to use the individually addressed LED strips to build something that could create a visual representation of another domain through a gradient along an LED strip. Initially we envisioned something that would use sound, through either pitch or volume, to change the rate at which colors would cycle through the strip, to create a physical version of an old Window’s Music Player type visual representation of a song, but when we realized that sound would be too difficult to use, we began brainstorming other attributes that could vary along a gradient, and came up with distance. By creating a visual representation of distance, we figured we could create a device that would be helpful in settings where personal space is not always respected, such as classrooms, by providing a gentle reminder to neighboring students when they are too close.

As the goal of the project was to create a double transducer, we sought next to think of an intermediate step that could be both produced and read by the Arduino. As we had used both servo motors and potentiometers in class, we were drawn to the idea of using motion, but knew we would then have to build a mechanical component for the motor to be able to rotate the potentiometer. Using CAD, we modeled gears that would fit around the dial of one of the potentiometers found in the Physical Computing room and could be attached to the propeller on the servo motor using screws. When assembled, the servo-motor and potentiometer system worked perfectly, but had a fairly long delay time due to the time needed for the motor to get to its final position. However, we decided that this worked well with our vision for the final device, as it was created to alert individuals that were spending extended periods of time invading the space of others, not to immediately respond to every shift in nearby movement. However, as we were directly mapping the distance recorded by the ultrasonic sensor to the motor position, any movement next to the box resulted in an audible twitch in the motor, which we thought would be distracting in a classroom setting, so we changed the code so that the motor would only shift at certain threshold levels of distance.

In order to create a functional prototype of The Space Box as an actual product, we wanted to keep all of the parts within one box, powered by internal batteries, and create an idea of the final aesthetics of it as an object that is meant to represent a measurement of distance through light in a way that is not only helpful, but beautiful. The time we had for this project limited us to using a cardboard box, but in its ideal form, The Space Box would be built out of a sturdier, more beautiful opaque material, that would have a hole for the ultrasonic sensor, and an engraving on the top representing a pun on the nature of the box as a detector of personal space through using a symbol of outer space through which the lighting gradient would ultimately be visible.

Schematic:

Code:

//The Space Box 
//Summary: Identifies whether certain threshholds of distance from an ultrasonic censor have been met, and changes the location of a servo motor in accordance to these thresholds, while mathematically dividing the color wheel into a gradient which is assigned to the LEDs on an LED strip with a cycle time determined by the position of the servo motor, as determined by the location of a potentiometer.
//inclusions required by libraries
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#include "Arduino.h"
#include "SPI.h"
#include <NewPing.h>
#include <Servo.h>

#define LEDPIN 2
#define TRIGGER_PIN  12  // sets trigger pin of unltrasonic sensor
#define ECHO_PIN     11  // sets echo pin of unltrasonic sensor
#define MAX_DISTANCE 200 // Maximum distance used for ultrasonic sensor

Adafruit_NeoPixel strip = Adafruit_NeoPixel(30, LEDPIN, NEO_GRB + NEO_KHZ800);  //setup of LED strip
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

Servo myservo;  // create servo object 

int val;    // variable to read the value from the analog pin
int nLEDs = 30;   //value for number of LEDs used
int POTPIN = A5;    //initialites pin for potentiometer
int potVal = 0;     // initializes variable for potenetiometer value
int waitTime;       //initializes delay time between sensor readings
int distance = 0;   //initializes variable for ultrasonic distance

void setup() {
  pinMode(POTPIN, INPUT); 
  strip.begin(); 
  strip.setBrightness(50);
  strip.show();
  myservo.attach(9);    //attaces servo
  Serial.begin(9600);
}

void loop() {
  Serial.println("main Loop");
  distance = sonar.ping_cm();
  val = map(distance, 0, 50, 0, 180);     // scale it to use it with the servo (value between 0 and 180)
  myservo.write(val);   //write value to servo
  delay(15);    //ensures servo is finished moving before reading potentiometer    
  strip.show();   // write all the pixels out
  potVal = analogRead(POTPIN);
  waitTime = potVal/100;
  Serial.println(val);
   
  int wait = 30;
  uint16_t i, j;
  //loop to write LEDs based on color from wheel 
  for(j=0; j<256; j++) {  
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
    Serial.println(wait);
  }
}

//code to cycle through different LED colors, inspired by the math in the code in the examples in the Arduino library LPD8806
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

 

]]>
Double Transducer: Picture Level https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-picture-level/ Mon, 18 Feb 2019 14:08:10 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5948 by Karan Gugle and Lillie Widmayer

Top view of the fully wired Picture Level, including the black paper cylinder used to block surrounding light from the photoresistor and LED inside.
1 of the 2 ultrasonic rangers used in the project, each of which provide distance measurements to calculate the angle of the painting to the ground.
Here’s Lillie using the Picture Level. As she change the angle between the palms of her hand, the speaker emits different pitched sounds, signifying how far the “painting” is from level.

Description: Device that indicates how crooked or level a picture is by dimming/brightening an LED depending on how crooked the picture is and playing notes of varying pitch based on the brightness of the LED.

Progress Images:

Initial breadboard + Arduino setup to get the speaker working. We had to test a few different resistors to empirically find the one that created the best sound.
First part of developing the level with 2 ultrasonic rangers. We thought we could place the speaker in the middle of the 2 rangers, but it turned out to be a wiring nightmare.
Side view of the KNEX contraption used to hold 2 small breadboards and our Arduino Uno R3 as well as to display the 360˚ rotating speaker. We built this housing before wiring any of our transducers.

Schematic:

//Double Transducer: Picture Level
//This code reads data from the ultrasonic sensors and controls LED brightness and pitch of sound coming from the speaker.

#include <NewPing.h>
#include <toneAC.h>

//Ultrasonic sensors and photoresistor need to be connected to analog pins
//LED needs to be connected to a PWM pin to vary brightness
#define TRIG1  12
#define ECHO1     11
#define MAX1 200
#define TRIG2  13
#define ECHO2     10
#define MAX2 200

 NewPing sensors[2] = {     // Sensor object array
  NewPing(TRIG1, ECHO1, MAX1),
  NewPing(TRIG2, ECHO2, MAX2)
 };

 const int SPEAKER = 8;
 const int PHOTORESISTOR = A0;
 const int LED = 5;
 const float BASE = 33.65; //distance between two sensors

 void setup() {
  pinMode(PHOTORESISTOR, INPUT);
  pinMode(LED, OUTPUT);
  pinMode(SPEAKER, OUTPUT);
  Serial.begin(9600);
 }

 void loop() {
  int dist1;
  int dist2;
  delay(200);
  for (uint8_t i = 0; i < 2; i++) { // Loop through each sensor and read data.
    delay(50); //time delay in order to read data from both sensors
    if( i == 0) {
      dist1 = sensors[i].ping_cm();
    }
  else {
    dist2 = sensors[i].ping_cm();
  }
 }

 /*
 print statements for debugging:
     Serial.print("dist 1 = "); 
     Serial.print(dist1);
     Serial.print(" dist2 = ");
     Serial.println(dist2);
 */

 int light = analogRead(PHOTORESISTOR);
   int diff = abs(dist1 - dist2);
   float angle = atan(diff/BASE)*(180/3.14); //angle calculated through trigonometry
   float brightness = log((angle + 9.0) / 90) * -15.0; //inverse relationship
   //between angle and brightness of LED
 
/*
More print statements for debugging: 
     Serial.print("angle = ");
     Serial.println(angle);
     Serial.print("light = ");
     Serial.println(light);
 */

   analogWrite(LED, brightness);
   if(light > 380) toneAC(4500); //varying tone pitch based on LED brightness
   else if(light > 300) toneAC(3500);
   else if(light > 200) toneAC(2500);
   else toneAC(2000);
 } 

 

We encountered challenging situations while building the Picture Level and learned a lot in the process. One of the major difficulties we had was with the ultrasonic sensors, which measured the distance to the nearest object. Both of us had only worked with ultrasonic rangers through the class exercise, so it was easy for us to get a single sensor working. We wired up the second sensor the same way we had done for the first, but ended up only being able to read a single ranger’s output at any given time. We ended solving the problem by creating a sensor array and reading the output of both sensors sequentially and learned a few things about the NewPing library in the process.

Another challenge we had was with the light and photoresistor relationship. We placed an LED directly adjacent to the photoresistor to capture how it dimmed and brightened according to the angle of the “painting”. However, the surrounding ambient light and the limited range of brightness the LED could produce led to a very small range of photoresistor values (300 – 350). This made it very hard for us to produce an accurate tone from the speaker, because we had such an inaccurate and limited range from the first transducer step. We decided to use the log() function to spread out the range of the photoresistor, so that it would be artificially more sensitive to changes in LED brightness.

Although these challenges were hard to overcome, we think it was smart to use sensors we had worked with before. Instead of fiddling around with the wiring / basics, we were able to concentrate on solving system-based challenges, which gave way to more thought and empirical testing. Our ultrasonic rangers encountered difficulties in accuracy due to the sequential reading of output and the delays we had set in the code, which is something we would adjust if we had to demo again.

Overall, we were able to create what we thought was an interesting project that required us to experiment and adjust as we worked. It brought together familiar sensors from class and applications of those sensors that were totally new to us. One aspect of the project both of us enjoyed was working with K’NEX to build housing for our breadboards, Arduino, and battery. Both of us see K’NEX as a good way to quickly prototype and hold together complex creations in the design phase. For the next project, we’d both like to focus more on brainstorming and planning. After seeing some of our other classmates creations, it would have beneficial if we could have created a more cohesive set of transducers.

 

]]>
Double Transducer: Proximity to Light to Angle https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-proximity-to-light-to-angle/ Mon, 18 Feb 2019 07:46:44 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6019 By Kaitlin McTigue, Nathan Naylor

What is a double transducer?

A “double transducer” is a device that reads some sort of input signal, converts it to a signal in a different domain, and then converts it a second time. The three domains we used for this simple double transducer are distance, light, and angle/direction.

Our project in a nutshell:

The first step in our project is a sensor that detects the distance of an object from it. When the user brings an object closer, the brightness of a light is raised and detected by a sensor that measures light. The data about the amount of light is then used to tell a little motor to point to a specific angle.

The power, data, and calculations are all handled by an Arduino microcontroller.

Parts

Inputs:
1 proximity sensor
1 photoreceptor
1 potentiometer

Outputs:
1 LED
1 servometer
Serial Monitor Feedback

Images

The IR Proximity Sensor.
Project in Motion. When the user moves their finger closer to the IR sensor, the end result is the servo moving it’s arm to 180 degrees.

]]> Double transducer: pressure -> IR -> movement https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-pressure-ir-movement/ Mon, 18 Feb 2019 06:57:52 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5804 Ian Shei and Yael Canaan

Description

This project transfers a pressure signal from a force-sensing resistor (FSR) pad to an infrared (IR) transmitter which is picked up by an IR receiver. The amount of IR light received is then used to move a servo motor, acting as a dial-like analog display, to a corresponding angle. Additionally, a digital OLED display shows the amount of pressure being applied to the pressure pad as a percentage.

ELI5: A pad is pressed and the force is displayed on a monitor. Two lights communicate with each other based on how hard you press and the lights tell the motor how much to move.

Progress images

Arranged from left to right: FSR pad, OLED display, servo motor. As more pressure is applied to the pad, the displayed percentage increases and the servo arm moves accordingly.

Schematic
Code
/*
 * Project 1 - double transducer
 *
 * Implements a double transducer: pressure input from an FSR is transduced 
* to IR output, the level of which * is read with an IR receiver and transduced to the movement of a servo
* motor serving as an analog display. * An OLED display also shows the relative percentage of pressure direct
* from the FSR pressure input. * * A basic smoothing algorithm is implemented twice to reduce jitter in OLED
* display output and servo motor * movement. * * Smoothing implementation adapted from David A. Mellis & Tom Igoe's * example at http://www.arduino.cc/en/Tutorial/Smoothing. * * I2C display implementation adapted from Limor Fried/Ladyada for * Adafruit Industries. */ #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #include <Servo.h> // pin assignments const int PRESSURE_PIN = A0; const int IR_TRANSMIT = 3; const int IR_RECEIVE = A1; const int MOTOR_PIN = 9; Servo motor1; int force; int ir_received; // constants for sensor inputs and outputs const int PRESSURE_MIN = 0; const int PRESSURE_MAX = 1023; const int LED_MIN = 0; const int LED_MAX = 255; const int MOTOR_MIN = 0; const int MOTOR_MAX = 180; const int IR_RECEIVE_MIN = 0; const int IR_RECEIVE_MAX = 1023; // setup for pressure display smoothing const int pressureNumReadings = 15; // number of pressure readings over which to average int pressureReadings[pressureNumReadings]; int pressureReadIndex = 0; int pressureTotal = 0; int pressureAverage = 0; // setup for motor smoothing const int motorNumReadings = 10; // number of motor inputs over which to average int motorReadings[motorNumReadings]; int motorReadIndex = 0; int motorTotal = 0; int motorAverage = 0; String percentage; void setup() { // put your setup code here, to run once: pinMode(IR_TRANSMIT, OUTPUT); pinMode(IR_RECEIVE, INPUT); motor1.attach(MOTOR_PIN); motor1.write(0); // for motor smoothing; array setup for (int thisMotorReading = 0; thisMotorReading < motorNumReadings; thisMotorReading++) { motorReadings[thisMotorReading] = 0; } // for pressure display smoothing; array setup for (int thisPressureReading = 0; thisPressureReading < pressureNumReadings; thisPressureReading++) { pressureReadings[thisPressureReading] = 0; } Serial.begin(9600); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } } void loop() { // put your main code here, to run repeatedly: force = analogRead(PRESSURE_PIN); // reading pressure level from FSR ir_received = analogRead(IR_RECEIVE); // IR LED transmits brightness according to pressure level on FSR analogWrite(IR_TRANSMIT, map(force, PRESSURE_MIN, PRESSURE_MAX, LED_MIN, LED_MAX)); ir_received = analogRead(IR_RECEIVE); // IR receiver receives IR from IR LED relative to transmit brightness // smoothing to decrease jerking of servo motor - constant running average over 10 readings motorTotal = motorTotal - motorReadings[motorReadIndex]; motorReadings[motorReadIndex] = map(ir_received, IR_RECEIVE_MIN, IR_RECEIVE_MAX, MOTOR_MIN, MOTOR_MAX); // mapping IR receiver range to motor movement range motorTotal = motorTotal + motorReadings[motorReadIndex]; motorReadIndex = motorReadIndex + 1; if (motorReadIndex >= motorNumReadings) { motorReadIndex = 0; } // smoothing for display of pressure percentage on OLED panel - constant running average over 15 readings pressureTotal = pressureTotal - pressureReadings[pressureReadIndex]; pressureReadings[pressureReadIndex] = map(force, PRESSURE_MIN, PRESSURE_MAX, 0, 100); // displaying pressure as percentage by mapping pressure readings pressureTotal = pressureTotal + pressureReadings[pressureReadIndex]; pressureReadIndex = pressureReadIndex + 1; if (pressureReadIndex >= pressureNumReadings) { pressureReadIndex = 0; } // calculating average values for motor and OLED display output motorAverage = motorTotal / motorNumReadings; pressureAverage = pressureTotal / pressureNumReadings; delay(15); motor1.write(motorAverage); percentage = String(pressureAverage); Serial.println(percentage); testdrawchar(); // calls display function to show relative pressure as percentage on OLED display } // display function to show relative pressure as precentage on OLED display void testdrawchar(void) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Pressure at: "); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0, 24); display.println(percentage + "%"); display.display(); }
Discussion

Yael~
I worked alone during the in class work period. At first I struggled a lot, especially due to being intimidated by how much I had to do on my own. I managed to get through the work session using Dan’s help as well as the internet. The use of many new parts at the same time was confusing and difficult for me, I ended up tackling it by integrating each new component separately. Some of the components were difficult because the rules for use contradicted previously learned material, such as the IF receiver – the longer pin had to be connected to ground. In the future I hope to expand my library of components and different tools that I will be able to use in more complex projects as well as my studio projects.

Ian:

Day 1 (4 hours)
When the project was handed off to me after the in-class work session, there were issues that had to be troubleshooted and corrected. First, while pressure readings from the FSR were being displayed in Serial output, since the IR receiver circuit lacked a resistor, values were all over the place and thus the servo motor (which derives it position from the amount of IR light received) was moving in an erratic, seemingly-random manner. Additionally, I altered the lines in code that accounted for the amount of IR light emitted from the LED to use analogWrite so that brightness would vary when plugged into a PWM pin on the Arduino. I also rewired the components on the board and to the Arduino so that the IR components would directly face each other. After some trial and error resulting in this setup, the next thing to tackle was constructing the code to be well-optimized for the application.

In theory, pressure values off the FSR can be directly applied to servo motor output using the map() function . However, given the requirement of a “double transducer,” the intermediate step of IR transmit and receive inevitably adds noise to the output – undesirable if one’s goal is to transduce one value to another. The first method applied to reduce noise was to shield the rearranged IR components with a cardboard tube, whose inside face I coated in black ink – the objective being to block as much interference from outside the system as possible. Secondly, I adapted Mellis and Igoe’s basic smoothing algorithm such that a running average of 10 IR receiver readings is written to change the servo’s position. Although this did not eliminate all jitter in the motor’s movement, it greatly reduced it to an acceptable (non-annoying) level. The number of values over which to average for was chosen as a balance between output stability and responsiveness (more values to average = more input lag).

Day 2 (1.5 hours)
As a “for-fun” bonus, a 128×64 OLED display was added to the project to display live pressure values as a percentage; the harder the FSR is pressed, the greater the percentage. Implementing an I2C digital display for the first time in a project presented a bit of a challenge since many implementations exist and are often specialized towards specific display components. Fortunately, I was able to ascertain and adapt the necessary function from the Adafruit I2C library in order to display text. Like the servo motor output, the displayed pressure percentage was also smoothed over a running average of 15 values (determined to be the best of both stability and responsiveness). Adding the display followed a sort of theme with the project as a whole – taking advantage of FOSS resources to wire all the components as well as integrate them in code.

Days 3 & 4 (2.5 hours)
After the project was completed and presented in class, I photographed it using a lighting cube in my design studio, post-processed the images in Lightroom, and made a wiring diagram in Illustrator describing the electronics. Given more time, possible refinements include soldering components together and creating a housing to yield a higher degree of refinement. An intriguing extension includes testing different, more advanced smoothing algorithms in order to remove any discernible jitter in the outputs.

]]>
Double Transducer: Ball in Cup Game https://courses.ideate.cmu.edu/60-223/s2019/work/ball-in-cup-game/ Mon, 18 Feb 2019 06:47:46 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5955 Our easy to play game is designed to help improve anyone’s ball in cup throwing skills. To start a game, a participant flips a switch which simultaneously starts a high pitched sound and a stopwatch. They then pick up a ball or another object and attempts to throw it in a cup. As soon as the shot is made, the game ends, the sound stops, an LED blinks three times, and the amount of time elapsed from the start of the game is displayed on a computer screen.

Detail view of sound emitter and sensor

Discussion

At the beginning of our project, we very quickly agreed that creating some sort of game which required human interaction to trigger our transducers would be interesting because it extended both the complexity and range over which we could design the product. Having further become very interested in incorporating IR, light, and sound into our project we set about merging our broader project interests with these components in what became the ball in cup game. The process was wrought with several challenging aspects, and yet other surprisingly simple features. Below we discuss highlights from each, broken down into two parts: hardware-wise, and code-wise.

In our initial brainstorming, integrating several components into two coupled transistors via circuits seemed like a daunting task: each transduction piece was unknown to us and required unique circuit logic to parse and reason over on the arduino. For instance, our sound detector consisted of five ports, including three for sound reading: ‘audio’, ‘envelop’, and ‘gate’. Each port captured a different element of a sound, while only a single one contained the information we needed. However, with help from online sources and material covered in lecture, we surprisingly found that wiring up each component was surprisingly straightforward.

Testing the IR Proximity Sensor
Testing the buzzer component
Testing the sound sensor with the buzzer

Furthermore, we realized that to integrate each component to form our transducers, it was sufficient to set them all in parallel with one another. This observation greatly simplified our final circuit architecture, and proved to be easier to debug than a more complicated circuit.

In conjunction with putting together our hardware project components, we faced several challenges with designing our code structure. Because our goal was to design a game based off of a double transducer, we had to not only tie in each transduction component together to form the transducers and dependencies, but also had to seamlessly integrate other game parts to work in concert with the transducers. One challenging aspect of this was incorporating game state design into our project. Our vision was to have three game states: start, active, and terminal, each which was responsible for different aspects of the game. The role of the start state was to initialize all required components for the game, the active state served to perform all actions required while the game is ongoing, and the terminal state was meant to perform game ending functions. In terms of our code, the start state should initialize the stopwatch, and flags which dictate whether the game was active. The active state in turn should play the sound infinitely and detect if the IR sensor had been tripped, turning off the sound if true (first transducer). Meanwhile, the terminal state should be triggered by the absence of sound which causes the LED blinking series (second transducer) and terminates the game. This required a complete restructuring of our code because up until this point, both transducers were captured in a single if statement which detected whether the IR sensor had been detected. Thus, meeting our vision required breaking apart the transducers into the active and terminal game states, as well adding a new start state as well.

One aspect of our project we might have changed if we had more time was the type of sound emanating from an active game. Instead of playing a single monotone sound continuously, we think it would be interesting to instead change the sound based on elapsed time. This change would provide a unique form of participant feedback as it would give an indication of how long the game has been going on for.

Detail View of Proximity Sensor in Cup
Detail view of electrical components

Video of Ball in Cup Game in action:

Schematic for ball in cup game

Code

 

/* Ball In Cup Game
 *  
 *  This code integrates several components (an IR sensor, speaker,
 *  sound detector, LED, switch, and display) into a double transducer
 *  ball in cup game. Specifically, the double transducer is created in
 *  our code according to the following; IR sensor --> sound (first 
 *  transducer), sound detector --> light (second transducer). The switch 
 *  and display are then used to start the game, and show a participant's 
 *  end game score respectively. 
 *  
 *  Inputs: an IR sensor, switch, and sound detector
 *  Outputs: a speaker, LED, and text display (via serial port)
 *  
 */


// Initialize and Assign Global Variables
const int BUZZER_PIN = 4;    // sound pin 
const int SWITCH_PIN = 3;    // switch pin
const int SOUND_PIN = A0;    // sound detector pin
const int LED_PIN = 5;       // LED pin
const int IR_PIN = A1;       // IR sensor pin
// initialize flags
bool DID_PRINT = false;      // Has current game time elapsed been printed
bool IS_END_GAME = false;    // Dictate game state
int OLD_SWITCH_STATE;        // initialize switch state variable
unsigned long time;          // initialize time variable

void setup(){
  // Set up arduino inputs & outputs
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(SWITCH_PIN, INPUT);
  pinMode(SOUND_PIN, INPUT);
  pinMode(IR_PIN, INPUT);
  // read initial state of switch
  OLD_SWITCH_STATE = digitalRead(SWITCH_PIN);
  // set serial port
  Serial.begin(9600);
}

void loop(){
  // initialize function variables
  int start_time;            // game start timestamp
  int end_time;              // game end timestamp
  int ir_threshold = 180;    // Used to detect when IR sensor is tripped
  int sound_threshold = 40;  // Used to detect absence of sound
  int current_switch_state = digitalRead(SWITCH_PIN);
  // switch state is different from past switch state
  // trigger game start
  if (current_switch_state != OLD_SWITCH_STATE) {
    // assign active game flags
    IS_END_GAME = false;
    DID_PRINT = false;
    // start sound
    tone(BUZZER_PIN, 1000);
    // begin stopwatch (record current timestamp)
    start_time = millis();
    }
  // game is active
  if (IS_END_GAME == false) {
    // ball has landed in cup
    if (analogRead(IR_PIN) > ir_threshold) {
      // terminate game
      IS_END_GAME = true;
      // IR sensor --> sound transducer
      noTone(BUZZER_PIN);
    }
  
  }
  else { // game has ended
    // Time taken to complete game has not been printed on screen
    if (DID_PRINT == false) {
      // stop stopwatch (record game end timestamp)
      end_time = millis();
      // print time elapsed in seconds
      Serial.print("Congratulations! It took you: ");
      int time_elapsed = (end_time - start_time) / 1000;
      Serial.print(time_elapsed);
      Serial.println(" seconds.");
      }
    // sound sensor --> sound detector. LEDs have not blinked yet
    // DID_PRINT ensures LED's are only blinked once
    if ((analogRead(SOUND_PIN) < sound_threshold) && (DID_PRINT == false)) {
      // sound detection --> light transducer
      // begin light show
      digitalWrite(LED_PIN, HIGH);
      delay(500);
      digitalWrite(LED_PIN, LOW);
      delay(200);
      digitalWrite(LED_PIN, HIGH);
      delay(500);
      digitalWrite(LED_PIN, LOW);
      delay(200);
      digitalWrite(LED_PIN, HIGH);
      delay(500);
      digitalWrite(LED_PIN, LOW);
      // LED flash and text print has been performed
      // Set to so that it does not happen again (only once)
      DID_PRINT = true;
      }  
    }
  // update switch state for next step
  OLD_SWITCH_STATE = current_switch_state;
}

 

]]>
Double Transducer: Double Transducer: Responsive Proximity Sensor (Annoying Tamagotchi) https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-double-transducer-responsive-proximity-sensor-annoying-tamagotchi/ Mon, 18 Feb 2019 05:36:35 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5924 by Karen Kim and Enock Maburi

Description

Responsive Proximity Sensor is a double transducer that uses proximity and sound levels as inputs to display facial expressions on an LED matrix and different messages on an LCD display. The first parts of our machine are the proximity IR sensor and a speaker. Depending on your detected proximity from the sensor, the volume of the sound emitted from the speaker will increase or decrease. When you are further from the sensor, the sound will be the loudest. Next, the sound level is detected by a microphone and there are three different sound levels, and each threshold triggers a specific response on the LED matrix and the LCD display as follows:

volume level LED matrix response LCD display phrase
loud smiley face 🙂   Too close.
moderate neutral face 😐 You’re okay.
quiet frowning face 🙁 Come closer.
An overall view of our machine.
A detailed look at one of the facial expressions created on the LED matrix when lit up, as triggered by the sound detector.
A closer look at the connections going on in our machine with most of the parts in the display, including the speaker, sound detector, LED matrix, button, and LCD display.
A close-up photo of the proximity IR sensor and the LCD display that shows messages of your distance. The sound detector controls which message comes up, depending on the volume detected by the sensor.

https://youtu.be/8eY3kp2UMo4
Video of the whole process.

Process

Making progress with the LED matrix and the LCD display. We had some trouble with the LED matrix, and only the white LED matrix seemed to actually be responding to anything. To try and fix the address of the LED matrix, we de-soldered a part on the back of the matrix as well as jumping one of the resistors, but in the end couldn’t get the green one to work.
https://youtu.be/6XpLupwGtr4
The first IR sensor that we used seemed to not be responding to any of our movements despite the wiring and code looking corrects, so we hooked it up to an oscilloscope to see what was happening. The sensor ended up being really questionable and not seeming to emit any type of signal, so we moved on to using a different proximity sensor.
In the middle of the process, we decided to add an on button to our machine since the sounds from the speaker were pretty loud and grating. We wanted to use a really large button that would just stay by itself in its own space, and it was really fun learning how to connect a more analog- looking button to the arduino.

Schematic

Code

/* 
Project: Double Transducer: Responsive Proximity Sensor ( Annoying Tamagotchi )
* * Names: Karen Kim, Enock Maburi * * Description: This code takes the input of distance from an IR Proximity Sensor and converts the distance to a volume in range [0, 255] which will be played on a speaker and that volume level will be picked up by a microphone which will display certain messages and images based on which range the volume is in. * * Credit: https://learn.sparkfun.com/tutorials/sound-detector-hookup-guide * I2C LED Backpack 8x8 Matrix Example * Volume Test Sounds example
* */

// Libraries #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Adafruit_GFX.h> #include "Adafruit_LEDBackpack.h" #include "Volume.h" // Pin Mapping #define IR_SENSOR A0 // refer to proximity sensor #define SPEAKERPIN 5 // refer to speaker #define PIN_ANALOG_IN 2 // refer to button #define BUTTONPIN 4 // refer to on button #define MICANALOG A2 // refer to sound detecting sensor // Variable Instantiation int intSensorResult; // sensor result int status = 0; int value; int volume; int buttonState; // Object Instantiation LiquidCrystal_I2C screen(0x27, 16, 2); Adafruit_8x8matrix matrix1 = Adafruit_8x8matrix(); Volume vol; static const uint8_t PROGMEM smile_bmp[] = { B00111100, B01000010, B10100101, B10000001, B10100101, B10011001, B01000010, B00111100 }, neutral_bmp[] = { B00111100, B01000010, B10100101, B10000001, B10111101, B10000001, B01000010, B00111100 }, frown_bmp[] = { B00111100, B01000010, B10100101, B10000001, B10011001, B10100101, B01000010, B00111100 }, hi_bmp[] = { B00000000, B01001010, B01001010, B01001010, B01111010, B01001010, B01001010, B00000000 }; void setup() { Serial.begin(9600); // Setup communication with computer to present results serial monitor // LCD Initialization screen.init(); screen.backlight(); screen.home(); // LED Matrix Initial Face matrix1.begin(0x70); matrix1.clear(); matrix1.drawBitmap(0, 0, hi_bmp, 8, 8, LED_ON); matrix1.writeDisplay(); vol.begin(); vol.setMasterVolume(1.00); pinMode(MICANALOG, INPUT); pinMode(BUTTONPIN, INPUT); pinMode(IR_SENSOR, INPUT); pinMode(SPEAKERPIN, OUTPUT); pinMode(PIN_ANALOG_IN, INPUT); } void loop() { // Reading Inputs intSensorResult = analogRead(IR_SENSOR); value = analogRead(MICANALOG); buttonState = digitalRead(BUTTONPIN); // Mapping the distance to a volume volume = map(intSensorResult, 20, 600, 0, 255); // Press button to start if (buttonState == LOW) { if (status) status = 0; else status = 1; } if (status) { screen.display(); vol.tone(880, volume); vol.delay(300); // Displaying messages and Faces based on volume picked up from mic if (value >= 345) { screen.print("Too close."); matrix1.clear(); matrix1.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON); matrix1.writeDisplay(); } else if ( (value > 295) && ( value < 345) ) { screen.print("You're okay"); matrix1.clear(); matrix1.drawBitmap(0, 0, neutral_bmp, 8, 8, LED_ON); matrix1.writeDisplay(); } else { screen.print("Come closer."); matrix1.clear(); matrix1.drawBitmap(0, 0, frown_bmp, 8, 8, LED_ON); matrix1.writeDisplay(); } Serial.println(value); delay(200); } else { screen.clear(); } }

 

Discussion

The process of building our double transducer was definitely a learning process, and a lot of trial and error. During the initial ideation process, it was interesting to learn a bit about our working styles. While Enock’s strengths lie more in coding and understanding what would be feasible to carry out, I (Karen) was more fixated on what kind of machine we should build and how we could build a story around it. This ended up working out really well throughout the process, as I would do research about our parts and try to focus on how everything would come together, and Enock was able to put our parts and wires together in a technical way.

When we were deciding which parts to use, we found that there were a lot of options that would work for our machine. It took us several different proximity sensors, speakers, and mics/sound detectors until we decided on which parts to use. We ran into problems with the proximity sensors having really unusual readings and some just not working at all. It took us some time until we figured out how to work with it and implement changes to adapt, such as reading the measurements in an inverse way so that it would make more sense in terms of the display. Additionally, we had planned on using a mic as a sound detector at first, but had trouble finding information about some of the parts and the small mic we intended to use wasn’t picking up the sound as well as we had hoped. However, we were able to mitigate these problems pretty easily by borrowing some items from lending and doing some deep google searches.

As a non-technical major, I had a lot of fun learning skills such as soldering and just building a more technological product. For example, we decided to connect a large button to our machine as an on button, and needed to solder the wires onto the plug of the button. Enock and I were able to both able to learn about little skills like how to connect/wire/code a button like that to our arduino that we hadn’t expected to, which was really cool.

Although knowing the usage of sound as an input would be difficult, we still decided to use it. Depending on whether we used it as an input or an output would have changed the project completely and would have probably made the process much easier. Sound detection is difficult on the arduino and is very noisy and thus we could have replaced it as the intermediate step and just used it as another output in addition to the LED matrix and LCD. This project was definitely a learning experience especially just about the need to adapt quickly, which will be really helpful moving onto projects that have even more moving parts involved!

]]>
Double Transducer: Heartbeat Visualizer https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-heartbeat-visualizer/ Mon, 18 Feb 2019 05:16:41 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5968 By Alton Olson and Eliza Pratt

Description

This project detects a user’s pulse and triggers a servo motor to move in sync. When a heartbeat is detected, the servo pushes a micro switch, which in turn lights up a configuration of LEDs.

Project Images

Overview: Double transducer contains a pulse sensor with pipecleaner cover, a servo, a microswitch, and LEDs with a heart display.
Transducer reading a pulse
Intermediate interaction: a servo is positioned to push a micro switch
 

Double transducer in action

Process Images

Testing a force sensor as the secondary input. While we had originally imagined using a button, we needed a component the servo could realistically push.
Ziptying the servo to the breadboard to prevent it from pushing itself upwards.

Pulse sensor picking up too much noise and detecting false pulses.

Device adjusted successfully with accurate pulse representation.

Schematic

Code

/*
 * Heartbeat Visualizer
 * By Alton Olson and Eliza Pratt
 * 
 * Description: This program reads input data from a pulse
 * sensor and tracks the maxes and mins to determine heart rate. 
 * When a heartbeat is detected, it triggers a servo to push
 * a switch that turns on a configuration of LEDs. 
 * 
 * Credit: Code for measuring heart rate was adapted from 
 * user VE7JRO's contribution to the StackExchange Arduino forum.
 * View discussion: https://tinyurl.com/y3vuvgpp

 */

#include <Servo.h>

// Pin constants
const int BUTTONPIN = 2;
const int SERVOPIN = 3;
const int LED1PIN = 4;
const int LED2PIN = 5;
const int LED3PIN = 6;
const int LED4PIN = 7;
const int LED5PIN = 8;
const int PULSEPIN = A1;

// Servo position constants
const int SERVO_DOWN_POSITION = 155;
const int SERVO_UP_POSITION = 135;

// We have two thresholds to detect a pulse
const int RISING_THRESHOLD = 520;
const int FALLING_THRESHOLD = 510;
// Ignore reading keeps track of the state of the pulse
bool ignoreReading = false;
// Time in ms of the last beat
long lastBeatTime = 0;

Servo servo;

void setup(){
  Serial.begin(9600);
  servo.attach(SERVOPIN);
  pinMode(BUTTONPIN, INPUT);
  pinMode(LED1PIN, OUTPUT);
  pinMode(LED2PIN, OUTPUT);
  pinMode(LED3PIN, OUTPUT);
  pinMode(LED4PIN, OUTPUT);
  pinMode(LED5PIN, OUTPUT);
  pinMode(PULSEPIN, INPUT);
}

void loop(){
  int reading = analogRead(PULSEPIN);

  Serial.println(reading);
  
  // Heart beat leading edge detected.
  if(reading > RISING_THRESHOLD && !ignoreReading
    && millis() - lastBeatTime > 500){
    // Trigger servo if <120 bpm
    servo.write(SERVO_DOWN_POSITION);
    ignoreReading = true;
    lastBeatTime = millis();
  }

  // Heart beat trailing edge detected.
  if(reading < FALLING_THRESHOLD && ignoreReading){
    servo.write(SERVO_UP_POSITION);
    ignoreReading = false;
  }

  // Turn on LEDs if the switch is pressed
  digitalWrite(LED1PIN, digitalRead(BUTTONPIN));
  digitalWrite(LED2PIN, digitalRead(BUTTONPIN));
  digitalWrite(LED3PIN, digitalRead(BUTTONPIN));
  digitalWrite(LED4PIN, digitalRead(BUTTONPIN));
  digitalWrite(LED5PIN, digitalRead(BUTTONPIN));

  delay(150);
}

 

Discussion

Decision Making: Throughout this project, one of our biggest realizations was that we can’t rely on physical components to behave exactly as we expect. While we knew we wanted a servo to be our initial output, finding the proper device to respond to it proved to be harder than we expected. We ended up testing buttons, force sensors, and a variety of switches in order to find the right fit. Large buttons presented a problem as they were too springy and would require a stronger servo to actuate. FSRs solved the recoil issue, but we weren’t able to get a solid reading from the small servo arm. We ended up opting for the micro switch as it required very little force to actuate but provided a reliable binary input.

One design decision we had to make early on was whether to have the transduced signal be continuous or discrete. Originally we were going to transduce the user’s heartrate, having the servo move to different positions based on the frequency of the user’s pulse. It was difficult to get a reliable heartrate reading, though, and we thought that reading individual pulses would be a simpler problem and easier to tune.

Challenges: Getting reliable input from the pulse sensor was by far our biggest challenge. The raw data was noisy and unreliable, and it was difficult to pinpoint the source of the noise. We discovered that where the sensor is on your finger, how hard you press on the sensor, and the amount of oil and sweat on your skin all affect the data. Our first algorithm used a simple threshold, where we’d count a heartbeat if the pulse sensor data crossed from below to above the threshold. The noise in the signal generated lots of false positives. So we added a second “falling” threshold, below the first, such that the signal had to drop below the falling threshold before the rising threshold would count again. This helped isolate only the bigger spikes in the waveform and filter out some of the noise. Lastly, to try and throw out more false positives, we added a max heartrate, so that the algorithm would ignore pulses that were within a certain time of other pulses.

Three small fixes we could have made:

  1. A better filtering algorithm. We still had issues with noise fooling our algorithm, and maybe an additional filter could have helped. Perhaps a low pass filter could eliminate some of the higher frequency noise while keeping the pulses intact.
  2. Adjustable thresholds. We hardcoded the rising and falling thresholds, but allowing the user to adjust these with a potentiometer could allow the device to perform better under different conditions.
  3. Taking pulse readings from somewhere else. The documentation for the pulse sensor recommends to try using an ear clip attachment to take readings from a user’s ear. We didn’t have the ear clip attachment, but this could have provided better data than the finger.
]]>
Double Transducer: Automated Plinko Game https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-automated-plinko-game/ Mon, 18 Feb 2019 04:15:25 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5855 by Elena Deng and Seema Kamath

Description:

We decided to make the game Plinko with some added twists. We have an acrylic box with a motor, three release spots and three small bins. When someone nearby makes a loud enough noise, the motor will remove the acrylic piece that was blocking the ball from falling through one of the holes. The ball will then roll down the ramp and land in one of the sections. Depending on which one it lands in, a different pattern of colors will light up the ramp.

Final Project Images:

Overview photo for proportions and scale
Three bins with FSR’s that trigger different color settings in the LED strip which correspond with the etched faces
Servo motor and three holes for ball placement. Servo motor swings the acrylic piece out when sound is triggered
Overall wiring for the project
https://gfycat.com/icycornyasiaticgreaterfreshwaterclam
Red light setting
https://gfycat.com/firmdrearygermanpinscher
Rainbow light setting
https://gfycat.com/faroffunlawfulkookaburra
Green light setting

Process Images:

Prototype One: Used for placement of the Arduino and location of Servo motor
First prototype testing
Prototype Two: Used to explore the dimensions of the bottom sections and angle of the ramp

Final project setup

Wiring Schematic:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Discussion:

One important thing we learned electrically was that electrical components will interact in interesting ways if there isn’t enough power provided. After making sure the sound detector and the motor worked properly separately, we combined the two as that was the first stage of our transducer. The code and the wiring was checked many times with no missteps found; however, with the motor connected, the sound detector thought the noises near it were very loud no matter the noise level. After lots of testing, we were advised to try using an outside power source for the servo motor. This thankfully solved the problem as it turned out that the amount of power the motor was drawing significantly detracted from what went to the sound detector which messed up the analog reading of it.

Design wise, the importance of having prototypes was really reiterated from this project. Our first one was a proof of concept that the double transducer could work with our components. Our second prototype was potentially going to be our final one, but it ended up being a good example of some things that we neglected to plan in our new streamlined design. One important thing was leaving a big enough gap between the ramp and the bins for the FSR wires to fit underneath. When there wasn’t a big enough gap, the FSRs were being triggered randomly from the weight of the cardboard. Additionally, it was very hard to move them and see whether or not one of the wires came loose. Moreover, we needed to make the space for the FSRs a little wider so there wouldn’t be any pressure from them touching the cardboard edges. We also made them a little thinner so that the ball would definitely land on the FSRs. Another aspect we gained from the second prototype was making a cutout in the back where we could keep the Arduino and breadboard.

Overall, the difficulties we faced were primarily from the electrical components. With the sound sensor we had difficulty managing the threshold of the sound in different spaces due to the audio differences in the environment. We also had difficulty finding a feasible library for the LED light strips, however after some assistance from Zach we were able to find the correct library and manage the colors of the strip. The “easy” parts of the project were the physical aspects of the project. Putting the laser cut pieces together and making the project look aesthetically pleasing overall was simple to do. Something we could have improved with another iteration is the placement of the panels on the ramp. We would also like to explore using the clear smoky acrylic as opposed to a sheet of paper for the ramp in the next iteration. Overall, we are happy with the way that the project turned out and the fun that we had in the process of making this triple transducer game.

 

]]>
Double Transducer: Fidget https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-fidget/ Sun, 17 Feb 2019 23:36:37 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5823 by Erin Ryan and Helen Yu

Fidget transforms the user’s movement of a joystick into a colored light, which in turn plays a note through a speaker.

Images:

Overview shot of Fidget while the joystick is being used. You can also see that the green LED is lighting up.
Bird’s eye view of fidget without anyone interacting with it.
Detail shot of joystick being used while the yellow LED lights up.
https://vimeo.com/317862064
Fidget in action

Process Photos:

LED and photo cell circuitry assembled separately from speaker and joystick. This was before we added black tape to shield the photocells from other sources of light.
Circuit and joystick circuitry assembled. At first we had the joystick movement translate directly to sound.
https://vimeo.com/317853270
At this point our prototype had issues with both the code, and the power source (which we talk about in our discussion), both of which contribute to the LED to sound not working as it should.

Schematic:

Code:

/*
 * Project 1
 * Double Transducer: Fidget
 * Erin Ryan (eeryan)
 * Helen Yu (heleny1)
 * 
 * Description: Code takes in position input from the joystick, translates it to one of four colored LEDs, then takes readings from four photocells to determine which of the four LEDs is on, and plays a note on a speaker based on which LED was lit.
 */

#include "pitches.h"
//joystick variables
int SPEAKPIN = 10;
int X_pin = A0;
int Y_pin = A1;

//photocell variables
const int BLUEPHC = A2;
const int GREENPHC = A3;
const int YELLOWPHC = A4;
const int REDPHC = A5;

int redBright;
int blueBright;
int yellowBright;
int greenBright;

void setup() {

  pinMode(SPEAKPIN, INPUT);
  Serial.begin(115200);
  pinMode(BLUEPHC, INPUT);
  pinMode(GREENPHC, INPUT);
  pinMode(YELLOWPHC, INPUT);
  pinMode(REDPHC, INPUT);
  pinMode(2, OUTPUT); //red
  pinMode(4, OUTPUT); //yellow
  pinMode(8, OUTPUT); //green
  pinMode(11, OUTPUT); //blue

}

void loop() {
  //joystick test
  int xVal = analogRead(X_pin);
  int yVal = analogRead(Y_pin);

  int blueBright;
  int greenBright;
  int yellowBright;
  int redBright;
  blueBright = analogRead(BLUEPHC);
  greenBright = analogRead(GREENPHC);
  yellowBright = analogRead(YELLOWPHC);
  redBright = analogRead(REDPHC);

  //joystick values
  Serial.print("X-axis: ");
  Serial.print(xVal);
  Serial.print("\n");
  Serial.print("Y-axis: ");
  Serial.println(yVal);
  Serial.print("\n\n");

  //joystick to LED
  
  //red light on/off
  if(((xVal <= 1023) && (xVal > 511)) && ((yVal > 511) && (yVal<=1023)))
  {
    digitalWrite(2, HIGH);
  }

  else{
    digitalWrite(2, LOW);
  }

  //yellow light on/off
    if(((xVal <= 511) && (xVal > 0)) && ((yVal > 511) && (yVal<=1023)))
  {
    digitalWrite(4, HIGH);
  }

  else{
    digitalWrite(4, LOW);
  }

  //green light on/off
    if(((xVal <= 511) && (xVal > 0)) && ((yVal > 0) && (yVal<=511)))
  {
    digitalWrite(8, HIGH);
  }

  else{
    digitalWrite(8, LOW);
  }
    
    //blue light on/off
    if(((xVal <= 1023) && (xVal > 511)) && ((yVal > 0) && (yVal<=511)))
  {
    digitalWrite(11, HIGH);
  }

  else{
    digitalWrite(11, LOW);
  }

  //LED to sound
  String bluestr = "blue " + String(blueBright);
  String greenstr = "green " + String(greenBright);
  String yellowstr = "yellow " + String(yellowBright);
  String redstr = "red " + String(redBright);
  
  if (redBright > 400){
    tone(SPEAKPIN, NOTE_C1, 500);
    noTone(SPEAKPIN);
    Serial.println(redstr);
    }
  if (yellowBright > 600){
    tone(SPEAKPIN, NOTE_C2, 500);
    noTone(SPEAKPIN);
    Serial.println(yellowstr);
    }
  if (greenBright > 600){
    tone(SPEAKPIN, NOTE_C3, 500);
    noTone(SPEAKPIN);
    Serial.println(greenstr);
    }
  if (blueBright > 600){
    tone(SPEAKPIN, NOTE_C4, 500);
    noTone(SPEAKPIN);
    Serial.println(bluestr);
    }
  }

 


Discussion:

While working on this double transducer, there were several components that proved to be more challenging than expected. The joystick, which is the first input, does not operate over the cartesian plane like we expected. So, in order to get the LEDs to light up, the directional values of the joystick were mapped and organized to suit our project. In addition to the counterintuitive joystick plane, the joystick wasn’t always centered at its neutral axis. As a result, some LEDs would light up without a physical input from the user. In retrospect, we could resolve this issue by placing a correction factor on the joystick position values. That way, even when the joystick is slightly off center, the device will not read this misalignment as an input from the user.

The different colored LED outputs were easier to put together and program. This part of the transducer consisted of four different colored LEDs and photocells that read the brightness of the LEDs. To control the brightness, black electrical tape was wrapped over the photocells and LEDs. However, because most of the LEDs were covered by the black electrical tape, a physical component of this transducer was hidden. Given the time, we would have installed a more permanent and visible display. Additionally, we think it would have been very interesting to build something that read the color, not the brightness of an LED.

During the first iteration of this double transducer, we discovered that, when plugged into the computer, the speaker produces a tone that is barely audible. To counter this effect, we powered the double transducer with a 5V battery source. However, after running several test trails, we noticed that the light and sound outputs weren’t consistently working the way they had been when plugged into a laptop. When the Arduino is plugged into the computer, the directional input from the joystick translates to the different colored LED almost immediately. This reaction time is shown in the demo video. We had originally attributed this slower reaction time to the runtime of the code, and part of it may have been, as for a while we didn’t have noTone() statements in the code. But even with the updated code, there’s still a very noticeable difference in the functionality of the transducer when it’s using the 5V power source vs a laptop power source.

So, at this point, the cause of this issue is unclear. This functionality issue can be attributed to the loose wiring of the power adapter. It can also mean that the power adapter is not necessarily the best power source for our device. While making this double transducer, we unknowingly made a tradeoff between a better execution and a louder final output. Looking back, it was necessary to change our power source because we did not know the speaker was producing a sound unless we put out ear next to the speaker. Maybe there was a better power solution we could have looked for if we had realized sooner than the crit the issue that the 5V supply was causing.

]]>
Double Transducer: Sequence Detector Safe https://courses.ideate.cmu.edu/60-223/s2019/work/double-transducer-sequence-detector-safe/ Sun, 17 Feb 2019 21:03:26 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5877 Welcome to the sequence detector safe! The serial output from the Arduino introduces the concept behind the sequence detector safe and allows the user to set a new password using the keypad. Once the password has been set, the user is instructed to turn the screen “towards a friend” to allow them to start taking guesses at the password. The password is only 4 digits longs. The lockbox takes in a stream of digits from the keypad, and uses the last four of the stream as the current guess. Four LED’s are used as hints to the guessing friend, each LED corresponding to a digit. If the first digit is correct, the LED will light up, and then the second will light up indicating if the second is correct. Thus, even if digits 2, 3, 4 are correct, because 1 is not correct, no lights will light up (and same applies for the rest of the digits). Once the passcode has been given correctly, all four LED’s light up and the box will open. As long as the passcode is correct, the box will remain open. If inputting another digit at this time causes the guess to be incorrect, the box will close again.

Testing out the LED matrix component with the keypad. The four-digit 7-segment display component was faulty, so this was used as a substitute instead.
Initial configuration of LED’s and photoresistors to detect on/off. This is before the duct tape was applied to reduce the effect of ambiance on sensor data.

Testing out the use of the servo in opening a cardboard box. A simple mechanical lever (the popsicle stick) was able to suffice. Being able to use this box transformed our project from having a metaphorical “lock” to having a truly physical component.


Putting an art major and an ECE major together proved to be a very interesting combination. Sophia was very focused on having a narrative during the initial ideation, which I (Claire) had a hard time following. I focused more on trying to think of a project that I thought the professor or the class wanted to see, rather than something that I would be excited about. Personally, it was hard for me to imagine what would be impressive or meaningful for other people, as I was having a hard time envisioning myself being satisfied with anything.

The creative process was entirely new to me, and I found it hard to navigate the lax requirements and lack of a real “problem” (which is common in engineering labs and problem sets). However, once we were able to decide what we wanted to do, I felt fairly confident in coding and working with sensors I never have before to make them do what I want them to do. Sophia was extremely helpful in creating a vision for the project and generating overall excitement, while I was more motivated by code development and pulling the project together one component at a time.

I found myself more and more engaged with the project the more time I invested in it, even if we had to take a few detours along the way. Perhaps the biggest obstacle for me is simply starting. I realized that a project that is seemingly simple can have complexity if I want it to. It can be as refined or as crude as I make it, but there is still a lot of room for technical learning. Knowing this, I hope that I will be able to finish projects earlier in the future so I can add little details to it that make it more sophisticated.


I (Sophia) approached this project at first more like one I would find in my Studio classes, where we would start more with a concept under the topic rather than think about the requirements. The fumbling towards a cohesive idea was a small difficulty, but in the end we were able to stamp our foot down on some things and come out with a project that we were both happy with.

Claire was very easy to work with given her much more advanced experience with electrical components which all in all contributed to the brainstorming process quite well. She fiddled mainly with code and electrical components, and I mainly worry-hovered, given that I was more used to doing the virtual side of things and felt oddly about not having that role this time (not bad oddly, just weird oddly).

One thing I would say was difficult was that I was tasked with the mechanical construction of the box based on my more readily available resources in that department. As someone who usually works in the digital realms, mechanical work (hardware, namely) doesn’t come as naturally to me. I had to worry about it breaking after I had fixed it which isn’t so much of a problem in debugging, and also had to worry about material logics such as how to stick things to things. Overall, it was an enjoyable, enlightening, and stressful experience. I hope to do more work in the physical world.

In the future, I think working out a production schedule and roles at the start of the line to make sure that we have more room for mistakes and exploring would help. I think we did well nevertheless this time, however.

 

Password is correct! All LED’s are on, the LED matrix is smiling, and the box is open.

 

Close-up of the simple yet effective box-opening technique.

Close-up of the final keypad, LED matrix, and LED lights setup.

The final working project without the serial output. This video showcases the various functionalities of the project: setting a passcode, LED lights giving hints as new guesses are given, the box opening at the correct input, as well as back to closing when the guess is no longer correct.

/*
 * Title: Sequence Detector Safe
 * Made by: Claire Ye, Sophia Cao
* Description: Reads numeric input from a keypad, sets a password, and detects a reentry of the password. This is done via LED photocell communication as well as an LED matrix.
* Pin mapping: the photocells and LED matrix are hooked to analog pins, and the servo is hooked to a PWM-enabled pin. Everything else is digital.
 * 
 * 
*/ #include <Keypad.h> #include <Servo.h> #include <Wire.h> #include <Adafruit_GFX.h> #include "Adafruit_LEDBackpack.h" const int LED1_PIN = 10; const int LED2_PIN = 11; const int LED3_PIN = 12; const int LED4_PIN = 13; #define STARTUP_TEXT "***\nWelcome to our sequence detector safe!\nSet a passcode to guard your secrets.\nLet your friends freely input numbers to take a guess at your passcode\nHowever, there is a security flaw! D:\nIf your friend guesses at least one of the sequences right, the LED's will light up to show the number correct.\nAnyways, at any point, press # to reset the password.\n***\n" int valOne, valTwo, valThree, valFour; // Keypad const byte Rows= 4; const byte Cols= 3; char keys[Rows][Cols]= { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'} }; // Smiley face taken from matrix8x8 example code static const uint8_t PROGMEM smile_bmp[] = { B00111100, B01000010, B10100101, B10000001, B10100101, B10011001, B01000010, B00111100 }; Adafruit_7segment codeDisp = Adafruit_7segment(); #define PASSCODE_LENGTH 4 byte rowPins[Rows] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad byte colPins[Cols] = {8, 7, 6}; //connect to the column pinouts of the keypad Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, Rows, Cols ); int password[PASSCODE_LENGTH]; int pass_i=0; int currentSequence[PASSCODE_LENGTH]; int passwordSet; bool LED1, LED2, LED3, LED4; Adafruit_8x8matrix matrix = Adafruit_8x8matrix(); void setup() { pinMode(LED1_PIN, OUTPUT); pinMode(LED2_PIN, OUTPUT); pinMode(LED3_PIN, OUTPUT); pinMode(LED4_PIN, OUTPUT); digitalWrite(LED1_PIN, LOW); digitalWrite(LED2_PIN, LOW); digitalWrite(LED3_PIN, LOW); digitalWrite(LED4_PIN, LOW); passwordSet = false; for (int i = 0; i < PASSCODE_LENGTH; i++) { currentSequence[i] = 0; password[i] = 0; } Serial.begin(9600); matrix.begin(0x70); matrix.clear(); matrix.writeDisplay(); matrix.setRotation(3); Serial.println(STARTUP_TEXT); pass_set(); } /* * Goes into setting passcode mode or continually allows user to input * a new sequence */ void loop() { char key = kpd.getKey(); if (key) { if (key == '#') { pass_set(); } else if (key == '*') { // do nothing yet } else { update_sequence(key - '0'); write_disp(); update_LED(); } Serial.print("Current: "); Serial.print(currentSequence[0]); Serial.print(currentSequence[1]); Serial.print(currentSequence[2]); Serial.print(currentSequence[3]); Serial.println(""); } } /* * Updates the current guess based on input - shifts all other to the left by one and appends last digit. */ void update_sequence(int num) { for (int i = 0; i < PASSCODE_LENGTH - 1; i++) { currentSequence[i] = currentSequence[i+1]; } currentSequence[3] = num; } /* * Sets password */ void pass_set(){ passwordSet = true; matrix.clear(); matrix.writeDisplay(); Serial.println("Please set your new password. Turn the screen away so your friend can't see!"); char key = kpd.getKey(); pass_i=0; while (pass_i < PASSCODE_LENGTH){ if (key){ Serial.println(key); password[pass_i]=(key) - '0'; pass_i++; } key = kpd.getKey(); } Serial.print("Passcode is now: "); Serial.print(password[0]); Serial.print(password[1]); Serial.print(password[2]); Serial.print(password[3]); delay(100); Serial.println("\n\nScreen is self-destructing in 3..."); delay(1000); Serial.println("2..."); delay(1000); Serial.println("1..."); delay(1000); for (int i = 0; i < 500; i++) { Serial.println(); } Serial.println("Try me!"); } /* * Interfaces with LED Matrix to display numbers */ void write_disp() { matrix.clear(); matrix.drawChar(2, 1, '0' + currentSequence[PASSCODE_LENGTH-1], LED_ON, LED_OFF, 1); matrix.writeDisplay(); } /* * Interfaces with second Arduino on LED Lights */ void update_LED() { valOne = passwordSet && (currentSequence[0] == password[0]); valTwo = valOne && (currentSequence[1] == password[1]); valThree = valTwo && (currentSequence[2] == password[2]); valFour = valThree && (currentSequence[3] == password[3]); digitalWrite(LED1_PIN, valOne); digitalWrite(LED2_PIN, valTwo); digitalWrite(LED3_PIN, valThree); digitalWrite(LED4_PIN, valFour); if (valFour) { matrix.clear(); matrix.drawBitmap(0,0, smile_bmp, 8, 8, LED_ON); matrix.writeDisplay(); delay(10); } } /* * File responsible for interfacing with LED lights and servo */ const int LED0_OUT = 2; const int LED1_OUT = 3; const int LED2_OUT = 4; const int LED3_OUT = 5; #include <Servo.h> const int LED0_IN = 6; const int LED1_IN = 7; const int LED2_IN = 8; const int LED3_IN = 9; const int PHOTO0 = A0; const int PHOTO1 = A1; const int PHOTO2 = A2; const int PHOTO3 = A3; const int SETPOINT = 400; const int BOX_LOCK = 10; const int OPEN = 170; const int CLOSE = 0; int led0_val, led1_val, led2_val, led3_val; Servo boxLock; void setup() { // put your setup code here, to run once: Serial.begin(9600); pinMode(LED0_OUT, OUTPUT); pinMode(LED1_OUT, OUTPUT); pinMode(LED2_OUT, OUTPUT); pinMode(LED3_OUT, OUTPUT); digitalWrite(LED0_OUT, LOW); digitalWrite(LED1_OUT, LOW); digitalWrite(LED2_OUT, LOW); digitalWrite(LED3_OUT, LOW); pinMode(LED0_IN, INPUT); pinMode(LED1_IN, INPUT); pinMode(LED2_IN, INPUT); pinMode(LED3_IN, INPUT); pinMode(PHOTO0, INPUT); pinMode(PHOTO1, INPUT); pinMode(PHOTO2, INPUT); pinMode(PHOTO3, INPUT); digitalWrite(LED0_OUT, HIGH); boxLock.attach(BOX_LOCK); } void loop() { Serial.println(""); digitalWrite(LED0_OUT, digitalRead(LED0_IN)); digitalWrite(LED1_OUT, digitalRead(LED1_IN)); digitalWrite(LED2_OUT, digitalRead(LED2_IN)); digitalWrite(LED3_OUT, digitalRead(LED3_IN)); led0_val = analogRead(PHOTO0) < SETPOINT; led1_val = analogRead(PHOTO1) < SETPOINT; led2_val = analogRead(PHOTO2) < SETPOINT; led3_val = analogRead(PHOTO3) < SETPOINT; /* Serial.print(analogRead(PHOTO0)); Serial.println(""); Serial.print(analogRead(PHOTO1)); Serial.println(""); Serial.print(analogRead(PHOTO2)); Serial.println(""); Serial.print(analogRead(PHOTO3)); Serial.println(""); */ if (led0_val && led1_val && led2_val && led3_val) { boxLock.write(OPEN); // Serial.println("OPENED!"); } else { boxLock.write(CLOSE); } delay(200); }

 

]]>