Project 1 – Intro to Physical Computing: Student Work Spring 2020 https://courses.ideate.cmu.edu/60-223/s2020/work Intro to Physical Computing: Student Work Wed, 19 Feb 2020 16:27:26 +0000 en-US hourly 1 https://wordpress.org/?v=5.3.17 A Double Transducer : Sound volume to Light Intensity https://courses.ideate.cmu.edu/60-223/s2020/work/a-double-transducer-sound-volume-to-light-intensity/ Wed, 19 Feb 2020 08:06:31 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9690 Description

The device takes sound as an input and outputs light based on how loud the sound is. On one end is a microphone, which is the input. Based on how loud the sound picked up by the microphone is, a servo will turn a potentiometer a certain amount. Based on the position of the potentiometer, the LED will light up brighter or dimmer. In the end, louder sounds will lead to more light, while quieter sounds will lead to less light.

Demonstration:

Progress Videos

The progress videos of the input and output domains.

The overall layout of the project

Input Sound detector to detect the volume of the sound produced.

“Servometer” (servo coupled to  potentiometer)  controlled by the input from the sound volume. It rotates the potentiometer.

LED output to produce different variation of brightness as controlled by the input from potentiometer

Discussion

We’ll start with what was easy. Getting the input, output, and middle man wired up was probably the easiest part. The code wasn’t too difficult, but there was some tweaking needed in order to get the desired behavior (mostly tweaking the ranges).

One of the big challenges was actually assembling the middle man portion of the device; namely, coupling the servo motor to the potentiometer. At first, I tried just taping a horn to the potentiometer shaft, but that wasn’t reliable enough, and looked terrible. In the end, I 3D printed a shaft coupler made for an SG-90 hobby servo (what we used) and drilled a hole in the other end of it to friction-fit onto the potentiometer. That did the job.

Schematic

Code

/*
   Project 1
   Seth Geiser (sgeiser) and Olaitan Adisa (okadisa)

   Collaboration: Ola is my partner and she came up with the
   schematic and other ideas.
   Some references:
   
   https://courses.ideate.cmu.edu/60-223/s2020/tutorials/I2C-lcd
   https://courses.ideate.cmu.edu/60-223/s2020/reference/class-log#codebook-periodic-updating

   Challenge: The biggest challenge was coupling the servo to
   the potentiometer so that we could adjust the LED brightness.

   Next time: I don't know... keep going?

   Description: The purpose is to create a double-transducer. The
   input is sound, read by a sound detector. The sound reading
   triggers a servo (coupled to a potentiometer) to change its
   position, thus changing the potentiometer's position. This
   changes the brightness of the LED. Louder sounds should result
   in the LED emitting brighter light, and vice versa.

   Pin mapping:

   pin  | mode  | description
   -----|-------|------------
   A0     input   potentiometer
   A1     input   sound detector
   11     output  LED
   2      output  servo
   SDA/SCL        display

*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
Servo myservo;

/*
   Create an LCD display object called "screen" with I2C address 0x27
   which is 16 columns wide and 2 rows tall.
*/

LiquidCrystal_I2C screen(0x27, 16, 2);

const int POTPIN = A0;
const int SOUNDPIN = A1;
const int LEDPIN = 11;
const int SERVOPIN = 2;

// "global" variables in this section can be used by any function in the whole sketch
float filtered_val = 0;  // this variable, of type float (decimal), holds the filtered sensor value
const float PREV_WEIGHT = 0.99; // this variable, of type float (decimal), determines the previously filtered value's weight.
const float CUR_WEIGHT = 1 - PREV_WEIGHT; // this variable, of type float (decimal), determines the current sample's weight.

unsigned long timer = 0; // variable for timing
const int INTERVAL = 250; // milliseconds between updates


void setup() {
  pinMode(POTPIN, INPUT);
  pinMode(SOUNDPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);

  myservo.attach(SERVOPIN);
  screen.init();
  screen.backlight();
  screen.home();

  filtered_val = analogRead(SOUNDPIN);

}

void loop() {

  int soundValue = (float) analogRead(SOUNDPIN); // casting from int to float

  int soundValueLCD = map(soundValue, 0, 500, 0, 99);  // mapping first input (sound)to range 0-99

  filtered_val = filtered_val * PREV_WEIGHT + soundValue * CUR_WEIGHT;

  int servoPosition = map(filtered_val, 0, 400, 0, 180); // mapping sound values to range 0 - 180 degrees

  int servoValueLCD = map(servoPosition, 0, 180, 0, 99); // mapping first output (servo)to 0-99

  myservo.write(servoPosition);

  int potVal = analogRead(POTPIN);
  int potValueLCD = map(potVal, 1, 1024, 0, 99);

  int mapPotVal = map(potVal, 1, 1024, 0, 254);   // pot to LED values

  analogWrite(LEDPIN, mapPotVal);
  int ledValueLCD = map(mapPotVal, 0, 254, 0, 99);


  // updating the LCD output
  if (millis() >= timer) {
    screen.setCursor(0, 0);
    screen.print((String) "i:" + soundValueLCD);
    screen.setCursor(6, 0);
    screen.print((String) + "m:" + servoValueLCD);
    screen.setCursor(8, 1);
    screen.print((String) potValueLCD );
    screen.setCursor(12, 1);
    screen.print((String) "o:" + ledValueLCD);
    timer = millis() + INTERVAL; // and update timer
  }

}

 

]]>
Double Transducer: Light Intensity to Rotation Speed https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-light-intensity-to-rotation-speed/ Wed, 19 Feb 2020 04:42:48 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9525

 

Description

This double transducer is designed to convert light intensity to magnetic field strength, which in turn is converted to rotational speed. The photocell on the far left side of the stage measures surrounding light intensity. This value is then inputted into a 180-degree hobby servo motor that moves the attached magnet to different angle values. At higher light intensity, the magnet is at a lower position angle and is thus closer to the triple-axis magnetometer positioned in the middle of the breadboard. At lower light intensity, the magnet is positioned farther away from the compass. Only the magnetic field strength in the  y-axis is measured because of the more linear correlation between the readings in the y-axis and the servo’s angle position. The magnetic field value then serves as an input for the 360-degree servo motor positioned at the far right of the stage. The rotational speed of the 360-degree servo motor increases as the magnet gets closer to the compass and the magnetic field strength is greater. Thus, increasing light intensity on the left side of the stage increases the rotational speed on the far right side.

Top view of Double Transducer: Light Intensity -> Magnetic Field -> Rotational Speed

Light intensity input values are obtained using a photoresistor

I2C LCD displays the input and output values of the double transducer

The servo with the magnet attached adjusts its position depending on light intensity. Position is measured using a triple-axis compass.

Position of the magnet varies the rotational speed of this 360 hobby servo.

 

Progress Images

Project before components were positioned onto the board.

The first few components are zip tied to the board.

Wires are rearranged because the LCD was facing the wrong decision.

Battery is added so that it doesn’t rely on a laptop.

Discussion

Working with some of the components were easy, as we had worked with them before. We had no problems working with the photoresistor or servo motors. The challenge came from working with the magnetometer. Neither of us had ever worked with a compass before. Learning how it worked required research, and finding the right library was a difficult process. The library we initially found was unnecessarily complicated and only worked part of the time. It required a lot of fiddling with to determine what exactly was the problem.  Figuring out how a new component worked and finding an appropriate library for the device was a new experience for us.

The project required a lot of tweaking to the maximum and minimum values in our code. Ensuring the inputs never went out of range and that the outputs were exactly what we wanted required us to constantly make slight edits and see how that affected the project. In addition to that, we had to make temporary code to test the range of values. Slight shifts in max and min values completely changed the rotation speed output, requiring us to carefully puzzle out the perfect range. The process gave us a new appreciation for constants in our code and valuable experience in testing a project.

 

Schematic

 

Code

/*
   Project 1: Light Intensity to Rotation Speed
   Leah Walko
   Gracia Genero

   Description: A double transducer that turns light intensity into 
   magnetic fields into rotation speed. A photoresistor picks up the 
   intensity of the light. That value changes the location of a magnet. 
   In turn, this affects the magnetic fields read by a compass, and based 
   off that value a servo motor rotates at a faster or slower speed.

   Credit: QMC5883LCompass library creator, MRPrograms, for their xyz 
   example code, and to the course's lectures and website. 

   Pin mapping:

   pin   | mode   | description
   ------|--------|--------------------------------------------
   A1    | input  | photocell to measure light intensity
   A4    | input  | triple axis compass SDA input
   A5    | input  | triple axis compass SCL input
   10    | output | Servo motor that moves the magnet around
   11    | output | 360 degrees servo motor with variable rotation speed
   SCL   | output | I2C LCD
   SDA   | output | I2C LCD

*/

// servo library
#include <Servo.h>

// compass and lcd libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <QMC5883LCompass.h>


// pin values
const int PHOTOPIN = A1;
const int MOTORMAGPIN = 10;
const int MOTORPIN = 11;

// range of possible values
const int ROTVAL_MAX = 89;
const int ROTVAL_MIN = 0; 
const int MAGANGLE_MAX = 50;
const int MAGANGLE_MIN = 0; 
const int LIGHT_MAX = 1023;
const int LIGHT_MIN = 0; 
const int MAGVAL_MAX = 2700;
const int MAGVAL_MIN = -7500;

// timer for updating LCD
const int INTERVAL = 1000; 
unsigned long timer = 0;

// LCD values
int screenInput = 0;
int screenMInput = 0;
int screenMOutput = 0;
int screenOutput = 0; 

// compass values
int x;
int y;
int z;

// motor objects
Servo magnetMotor; // create a motor "object" called magnetMotor
Servo rotatingMotor; // create a motor "object" called rotatingMotor

// compass and LCD objects
QMC5883LCompass compass; // create a compass "object" called compass
LiquidCrystal_I2C screen(0x27, 16, 2); // create a LCD "object" called screen

void setup() {

  // setup the screen
  screen.init();
  screen.backlight(); // turn on the backlight to start
  screen.home(); // set cursor to home position
  screen.print("i:"); // print out the unchanging parts of the values
  screen.setCursor(6, 0);
  screen.print("m:");
  screen.setCursor(12, 1);
  screen.print("o:");
  delay(2000); // do nothing for 2 seconds
  
  // setup the input pin -> photocell
  pinMode(PHOTOPIN, INPUT);

  // Initializing compass
  compass.init();

  // setup the output -> motor moving the magnet + motor rotating
  magnetMotor.attach(MOTORMAGPIN);
  rotatingMotor.attach(MOTORPIN);
  rotatingMotor.write(90);

  Serial.begin(9600);

}

void loop() {

  // get info on light intensity
  int lightVal = analogRead(PHOTOPIN);

  // calculate the location of the magnet and move the motor to that angle
  int magAngle = map(lightVal, LIGHT_MIN, LIGHT_MAX, MAGANGLE_MIN, MAGANGLE_MAX);
  magnetMotor.write(magAngle);

  // get the compass value 
  // Read compass values
  compass.read();
  // Return XYZ readings from the compass
  x = compass.getX();
  y = compass.getY();
  z = compass.getZ();

  // calculate magnetic value
  int magVal = y; 

  // calculate rotation speed
  int rotateVal = map(magVal, MAGVAL_MIN, MAGVAL_MAX, ROTVAL_MIN, ROTVAL_MAX);

  // servo rotates at a speed of rotateVal (0 is faster, 90 is slower)
  rotatingMotor.write(rotateVal);
  
  // Update LCD screen values
  screenInput = map(lightVal, LIGHT_MIN, LIGHT_MAX, 0, 99);
  screenMInput = map(magAngle, MAGANGLE_MIN, MAGANGLE_MAX, 0, 99);
  screenMOutput = map(magVal, MAGVAL_MIN, MAGVAL_MAX, 0, 99);
  screenOutput = map(rotateVal, ROTVAL_MIN, ROTVAL_MAX, 0, 99); 

  // Display updated LCD screen values only at specific time intervals
  if (millis() >= timer) {
    // print light val input
    screen.setCursor(2, 0);
    screen.print(screenInput);
  
    // print the magnetic field output value
    screen.setCursor(8, 0);
    screen.print(screenMInput);
  
    // print the magnetic field input value
    screen.setCursor(8, 1);
    screen.print(screenMOutput);
  
    // print the rotation speed output value
    screen.setCursor(14, 1);
    screen.print(screenOutput);

    // reset timer
    timer = millis() + INTERVAL;

    // serial output for testing purposes
    Serial.println((String)"X: " + x +
                         "; Y: " + y +
                         "; Z: " + z);
    
    Serial.println((String)"room brightness: " + lightVal +
                         "; magVal: " + magVal +
                         "; rotateVal: " + rotateVal +
                         "; magAngle: " + magAngle );
  }
}

 

]]>
Double Transducer: Rotation Speed to Magnetic Field Strength https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-rotation-speed-to-magnetic-field-strength/ Wed, 19 Feb 2020 04:28:07 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9442

The IR proximity sensor measures when something is near, allowing us to use the timing to calculate rotational speed.

Faster rotational speed makes the LED brighter. The photoresistor detects the brightness to change the output.

The servo motor powered by the external battery moves a magnet, changing magnetic field strength.

 

Description:

The IR proximity sensor reads the distance of a specific point of the rotating disk. If it takes less time to get closer to the sensor, the rotational speed is faster. When rotational speed is faster, the LED gets brighter. The photoresistor senses the brightness of the light. The brighter the light, the servo motor moves the magnet further away, which creates a stronger magnetic field.

Progress:

First, we used a potentiometer to test how the middle step, the LED and photoresistor, would affect the rotation of the servo motor.

We switched to a smaller IR proximity sensor that detected more specific distances, because the larger one was meant for further distances. This was an important change in our project.

We attached an external battery to the servo motor to decrease electrical noise that affected the LED. This helped us reduce the flickering of the LED and the twitching of the motor.

 

Discussion:

Something that was difficult to figure out was how to use the IR proximity sensor to read rotational speed. We needed it to measure difference in the time when the stick got close to it at first and then close to it again. We wrote software to have the sensor read the time only the first time it gets approached, before spinning away and back again.

Another thing that was difficult was getting the servo motor to stop being really jittery. At first, we noticed that the LED light was flickering, which affected the motor, which in turn made the light flicker. We use an external power to power the servo motor to give it a steady supply of electricity, which stopped the LED from flickering.

We then realized that the noisy data that the photoresistor was picking up was causing the problem. We figured out through testing the range of the LED with analog write that there was a consistent pattern of spiking. We solved this problem by using moving average filter code from the course website. This tweak got rid of the majority of the twitching of the servo motor.

 

Schematic:

/*
   Project 1: Double Transducer: Rotation Speed to Magnetic Field Strength
   
   Varsha Shankar and Suzanne Nie (varshas)+(suzannen)

   Description: The double trasnducer takes in rotational speed through the IR proximity sensor and outputs 
   that as a LED brightness. Then a photoresistor reads the LED brightness and outputs that to a servo motor 
   that moves a magnet towards the front as the LED gets brighter and opposite and LED gets dimmer.
   This changes the magnetic field.

   Pin mapping:

   pin   | mode   | description
   ------|--------|------------
   5      output     LED
   A2     input    photoresistor
   A3     input    potentiometer[testing]
   A1     input    IR sensor 
   3      output   servo

   Libraries Used:
   LiquidCrystal Arduino library for I2C LCD displays

*/

#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>



//used to make the data coming in less noisy
float filtered_val = 0;  
const float PREV_WEIGHT = 0.8; 
const float CUR_WEIGHT = 1 - PREV_WEIGHT;

LiquidCrystal_I2C screen(0x27, 16, 2);


int LED_brightness;
const int LEDPIN = 5;

const int PHOTOPIN = A2;
int photopinval;


Servo servo;
const int servopin = 3;
int servopos;

//Testing purpose
//const int potpin = A3;
//int oldpotval;
//int newpotval;
//int potval;


int IRval;
int IRPin = A1;

//used to measure the time difference between each rotation
unsigned long timer;
unsigned long oldtime;
unsigned long newtime;
unsigned long timediff;

//used to take only one measurement each rotation
bool far;


void setup() {
  pinMode(PHOTOPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);
  //can use this to test
  //pinMode(potpin, INPUT);
  pinMode(IRPin, INPUT);
  servo.attach(servopin);
  Serial.begin(115200);
  screen.init();
  screen.backlight();
  screen.home();

  timer = millis();
  oldtime = 0;
  newtime = 0;
  timediff = 0;

}

void loop() {
  timer = millis();

  photopinval = 925 - analogRead(PHOTOPIN);
  //checks to see if an object is close enough starting and ending a rotation
  IRval = analogRead(IRPin);
  if (IRval > 450 and far == false) {
    far = true;
    newtime = timer;
    if ((newtime - oldtime) > timediff) {
    }

    timediff = newtime - oldtime;
    oldtime = newtime;
  }
  if (IRval < 450 and far == true) {
    far = false;
  }


  LED_brightness = map(timediff, 0, 750, 0, 255);
  if (LED_brightness > 255) {
    LED_brightness = 255;
  }
  else if (LED_brightness < 0) {
    LED_brightness = 0;
  }
  analogWrite(LEDPIN, 255 - LED_brightness);
  filtered_val = filtered_val * PREV_WEIGHT + photopinval * CUR_WEIGHT;
  servopos = map(filtered_val, 29, 750, 0, 180);
  servo.write(servopos);

  screen.home();
  screen.print("i:");
  screen.setCursor(12, 1);
  screen.print("o:");
  screen.setCursor(6, 0);
  screen.print("m:");
  if (timer % 50 == 0) {

    screen.setCursor(2, 0);
    if (timediff <= 0) {
      timediff = 0;
    }
    if (timediff > 750){
      timediff = 750;
    }
    screen.print(99 - map(timediff, 0, 750, 0, 99));
    screen.setCursor(14, 1);
    screen.print(map(servopos, 0, 180, 0, 99));
    screen.setCursor(8, 0);
    screen.print(99 - map(LED_brightness, 0, 255, 0, 99));
    screen.setCursor(8, 1);
    if (filtered_val < 29) {
      filtered_val = 29;
    }
    if (filtered_val > 750) {
      filtered_val = 750;
    }
    screen.print(map(filtered_val, 29, 750, 0, 99));

  }
}
]]>
Double Transducer: lightSpin https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-lightspin/ Wed, 19 Feb 2020 04:25:52 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9640 Project description

 

A 10’’ by 10’’ cardboard has a light sensor on the top side. When the light goes from dim to bright, a blue motor on the left side pushes the Popsicle stick to the right. As the Popsicle stick gets closer to the distance sensor, the servo motor on the bottom spins faster.

 

 

Input sensor: Photoresistor detects light brightness

Middle actuator: Servo motor drives linear actuation

Middle sensor: IR proximity sensor reads linear actuation

Output actuator: Continuous servo motor

Process

Testing the accuracy of one specific type of IR sensor.

Bending the metal arm to create linear motion

Testing to see if a plastic straw help to stabilize the motion of the popsicle

 

Discussion

This project was an excellent experience for us to learn new skills and gain confidence in creating interactive computing projects. The process of ideating helped us think of creative ways to transmit energy. We were initially interested in converting the light brightness to vibration intensity, which would ultimately manifest as rotation speed. However, after our initial check-in, we realized that having linear movement as the middle step instead of the vibration intensity would produce a stronger and more foolproof signal, as we later saw on demo day. 

Throughout the project, we were fascinated by the idea of converting one form of energy into a seemingly unrelated form. When the project was introduced to us, we were very intrigued by the thought of turning light brightness to rotation speed. This fascination didn’t stop there. Something as simple as using a servo motor to produce linear movement using very simple materials from the physical computing lab was exciting. 

Overall we were proud of how our project turned out. On demo day, one of the guest critics commented on our use of two servo motors in very different ways to produce various kinds of motion. That helped to crystallize the diverse applications of the components that were available to us in the lab and the endless possibilities with them! 

Schematic

Schematic

Code

/*
  Project title: lightSpin

  Description: The following code direct the arduino to read values from a photoresistor and an IR proximity sensor,
  and manipulate a servo motor and a continuous servo motor accordingly.The value given by the photoresistor is mapped
  to a range of degree (0-180) that the servo motor turn to. The value given by the IR proximity sensor is mapped to a
  range of rotation speed for the continuous servo motor. Finally, the working percentages of each input and output are
  displayed on a LCD screen in a range of 0-99.

  Pin mapping:

   pin   | mode   | description
   ------|--------|------------
   3      output    Determines the rotation speed of the continuous servo motor
   7      input     Turns on the flashlight
   12     output    Turns the servo motor to different degree (0-180)
   A0     input     For the photoresistor to detect the light intensity
   A2     input     For the IR proximity sensor to detect the linear distance  to the popsicle
   A4     output    Serial data; for communication to the LCD screen
   A5     output    Serial clock; for communication to the LCD screen

  Credit:
  When wiring the circuit for the IR sensor we have refered to the tutorial on the class webpage to set up the potentiometer.
  In addition, the tutorial of the LCD was also used. The function 'lcd' was written under the help of Esteven.

*/

//include the library for LCD and servo motor
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//Setting up the constants for pins and the variables
const int photoPin = A0;
int brightness;
const int servopin = 12;
int servoTurn;
const int proxPin = A2;
int distance;
const int motorPin = 3;
int rotaSpeed;
int rerangedBrightness;
int rerangedServoTurn;
int rerangedDistance;
int rerangedRotaSpeed;
int counter = 0; // variable to use for periodic updates
const int FREQ = 20; // number of loop()s to run before update


/* Create an LCD display object called "screen" with I2C address 0x27
   which is 16 columns wide and 2 rows tall. You can use any name you'd like. */
LiquidCrystal_I2C screen(0x27, 16, 2);


/* Definition of a custom character called "frowny" which will be used later.
   Each digit is a pixel of the 5x7 array that forms a single character on this display.
   Draw your own at https://maxpromer.github.io/LCD-Character-Creator/ */
byte frowny[] = {
  B00000,
  B11011,
  B11011,
  B00000,
  B00000,
  B01110,
  B10001,
  B00000
}; // (note the extra row of zeros at the bottom)


//Nme the two servo motors
Servo smallMotor;
Servo bigMotor;

/*This function "write" the four elements onto the LCD screen. This is crucial as
  it sloves the porblem of incorrect numbers popping up when the values are 3 digits*/
void lcd(LiquidCrystal_I2C screen, int input, int m1, int m2, int output) {
  // set cursor to home position, i.e. the upper left corner
  screen.setCursor(0, 0);
  // print the input sensor value
  screen.print((String)"i:" + input);

  screen.setCursor(6, 0);
  screen.print((String)"m:" + m2);

  screen.setCursor(8, 1);
  screen.print((String) m2);

  screen.setCursor(12, 1);
  screen.print((String)"o:" + output);
}


void setup() {
  //set up the pins
  pinMode(photoPin, INPUT);
  pinMode(proxPin, INPUT);
  pinMode(motorPin, OUTPUT);

  //set up the servomotor
  smallMotor.attach(servopin);
  bigMotor.attach(motorPin);

  //LCD setup
  screen.init();
  screen.backlight(); // turn on the backlight to start

  //setup the serial feedback
  Serial.begin(9600);
}

void loop() {

  //Sense the brightness of the environment
  brightness = analogRead(photoPin);

  //Turn the servo motor
  servoTurn = map(brightness, 50, 300, 0, 180);
  servoTurn = constrain(servoTurn, 0, 180);
  smallMotor.write(servoTurn);

  //Sense the distance of the popsicle
  distance = analogRead(proxPin);


  //determine the rotation speed
  rotaSpeed = map(distance, 200, 350, 90, 0);
  rotaSpeed = constrain(rotaSpeed, 0, 90);
  bigMotor.write(rotaSpeed);

  //values are reranged so that they are displayed as 0-99 on the LCD
  rerangedBrightness = map(brightness, 50, 300, 0, 99);
  rerangedServoTurn = map(servoTurn, 0, 180, 0, 99);
  rerangedDistance = map(distance, 200, 350, 0, 99);
  rerangedRotaSpeed = map(rotaSpeed, 90, 0, 0, 99);

  rerangedBrightness = constrain(rerangedBrightness, 0, 99);
  rerangedServoTurn = constrain(rerangedServoTurn, 0, 99);
  rerangedDistance = constrain(rerangedDistance, 0, 99);
  rerangedRotaSpeed = constrain(rerangedRotaSpeed, 0, 99);


  //show the current vlaues of the inputs and outputs; for testing
  Serial.println((String)"brightness " + brightness + " servoturn " + servoTurn + " distance " + distance + " rotaspeed " + rotaSpeed + " " + rerangedRotaSpeed   );

  // running a periodic non-blocking task
  if (counter >= FREQ) { // if counter meets or exceeds FREQ

    lcd(screen, rerangedBrightness, rerangedServoTurn, rerangedDistance, rerangedRotaSpeed);



    counter = 0; // and reset counter
  }
  counter++; // increment counter every loop cycle

}

 

 

]]>
Double Transducer: Color to Linear Position https://courses.ideate.cmu.edu/60-223/s2020/work/9626-2/ Wed, 19 Feb 2020 02:35:22 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9626

Top view of the entire double transducer

Input: Adafruit TCS34725 Color Sensor

Middle Step: ADXL335 Accelerometer on a popsicle stick structure attached to a servo motor

Output: an popsicle arm that moves forward when the servo motor rotates

Description

This double transducer reads a color sensor on the left side and converts the result into corresponding rotational movements of a motor which is connected to an accelerometer with popsicle sticks structure. The movements of the accelerometer result in the linear movement of a popsicle stick. The closer the color is to red, the less linear movements the popsicle structure makes.

Discussion

The most important part of this project was in communication with other teams. To make our double transducer work transducer to transducer, we did some experiments by setting each sensor results to specific values in an Arduino. For an input color sensor specifically, we used different colors of objects to give different signals to a color sensor. While it worked internally, because we are getting input from the previous team’s output, we needed to communicate with other teams and test our model to theirs. Since we were the last team, we met up with the previous team and tested our color sensor. Throughout this process, we learned communication not only within a team but also among multiple working units is critical especially in a big project. 

One of the challenging parts was the final ‘linear’ movement. We first started off with popsicle sticks with their original length to see how it works; however, it, first, makes circular movements, and, second, moved too much which was not appropriate to the given board size. We did some experiments to find proper lengths of popsicle sticks corresponding to the board size, and found out shorter popsicle sticks make smoother and more gradual movements. However, it still did not make a perfectly linear movement, so we built cardboard houses that fasten a popsicle stick. Before building the linear movement structure, we calculated the lengths and degrees, but it unexpectedly did not turn out that way. Throughout the multiple problem-solving processes we ended up having the model we wanted, which was different from what we first designed. We realized there always can be differences between calculations and the actual executions. 

As for the middle part, we originally decided to use a joystick and popsicles to move the joystick. However, we found out that the joystick tends to latch on to the middle or the two ends, and it is very hard to make it stay in the middle. With a backup plan during our idea session, we made the motor rotates an accelerometer instead, which turned out to be successful. We learned that we should always consider alternatives in case our plan fails, and if something doesn’t work, we should not dwell on it but should look for different solutions.

Process Images

Originally we planned to use a joystick for our middle step, but we had difficulties making it stay in the middle, so we switched to an accelerometer instead.

Planning the mechanism for converting motor rotation into linear movement. We realized a popsicle stick attached to the motor is too long.

Near-finished product for our output. We decided to make cardboard “houses” to hold the popsicle stick in place.

Schematic

Code Submission

/*
 * Project 1: A Double Transducer - Color to Linear Position
 * Alan Zhu and Yun Lee
 * 
 * Description: the code here first takes the rgb input from the 
 * color sensor and map it to a single value (hue), and uses that
 * value to determine the degree and turn the first servo attached
 * to the accelerometer. It then reads the accelerometer input and
 * determine the degree and turn the second servo attached to popsicle
 * sticks for linear movement. It also updates the LCD screen regularly
 * to show values for input, middle input, middle output, and output.
 * 
 * Collaboration: the code to transform rgb colors into hue is
 * taken from the second response in 
 * https://stackoverflow.com/questions/23090019/fastest-formula-to-get-hue-from-rgb
 * 
 * Pin mapping:
 * pin   | mode   | description
 * ------|--------|------------
 * A0     input    accelerometer
 * 5      output   servo motor 1 (accelerometer)
 * 6      output   servo motor 2 (popsicle)
 * 
 * 
 */

#include <NewPing.h>
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Adafruit_TCS34725.h"


int counter = 0;
const int FREQ = 500;

const int MOTORPIN1 = 5;
const int MOTORPIN2 = 6;
const int ACCPIN = A0;

unsigned long timer = 0; // variable for timing
const int INTERVAL = 250; // milliseconds between updates 4 times a second

// set up servos, color sensor, and LCD
Servo steeringMotor1, steeringMotor2;
Adafruit_TCS34725 tcs = Adafruit_TCS34725();
LiquidCrystal_I2C screen(0x27, 16, 2);

void setup() {
  Serial.begin(9600);
  if (tcs.begin()) {
    Serial.println("Found sensor");
  }
  steeringMotor1.attach(MOTORPIN1);
  steeringMotor2.attach(MOTORPIN2);
  pinMode(ACCPIN, INPUT);

  screen.init();
  screen.backlight();
  screen.home();
}

void loop() {
  // read rgb and map to a single value
  float r, g, b;
  tcs.getRGB(&r, &g, &b);
  int hue = rgbToHue(r, g, b); 

  // map color to degree and rotate popsicle stick attached to accelerometer
  int degree = map(hue, 0, 360, 0, 90); 
  steeringMotor1.write(degree);

  // read accelerometer and revert to original position
  int acc_pos = analogRead(ACCPIN);
  delay(100);
  steeringMotor1.write(-degree);
  delay(100);

  // for testing purposes
  Serial.println((String)"degree:"+degree);
  Serial.println((String)"X:"+acc_pos);
  Serial.println((String)"r:"+r+" g:"+g+" b:"+b+" h:"+hue);
  
  // map accelerometer input to degree and rotate motor 2
  int degree2 = map(acc_pos, 420, 365, 0, 50);
  steeringMotor2.write(degree2);
  delay(100);
  steeringMotor2.write(-degree2); // move back to original position
  delay(100);

  // map all values to 0-99 scale
  int led_lcd = map(hue, 0, 360, 0, 99);
  int motor1_lcd = map(degree, 0, 90, 0, 99);
  int acc_lcd = map(acc_pos, 420, 365, 0, 99);
  int motor2_lcd = map(degree2, 0, 50, 0, 99);
  
  if (millis() >= timer){
      screen.clear();
      // run LCD screen update procedure here
      timer = millis() + INTERVAL; // and update timer
      screen.setCursor(1, 0);
      screen.print((String)"i:" + led_lcd);
      screen.setCursor(6, 0);
      screen.print((String)"m:" + motor1_lcd);
      screen.setCursor(8, 1);
      screen.print(acc_lcd);
      screen.setCursor(12, 1);
      screen.print((String)"o:" + motor2_lcd);
  }
}

// map red, blue, and green into hue
int rgbToHue(float r, float g, float b){
    int red = (int)r;
    int green = (int)g;
    int blue = (int)b;
    float mn = min(min(red, green), blue);
    float mx = max(max(red, green), blue);
    if (mn == mx) return 0;
    float hue = 0;
    if (mx == red) 
      hue = (green - blue) / (mx - mn);
    else if (mx == green)
        hue = 2 + (blue - red) / (mx - mn);
    else 
        hue = 4 + (red - green) / (mx - mn);
    hue = hue * 60;
    if (hue < 0) hue = hue + 360;
    return round(hue);
}

 

]]>
Color-Humidity-Displacement Double Transducer https://courses.ideate.cmu.edu/60-223/s2020/work/color-humidity-displacement-double-transducer/ Wed, 19 Feb 2020 01:30:39 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9331 Overall Photo

Our Double Transducer in all its glory!

Detail Photos

A festive green pipe cleaner was stuck into the arm of the servo and threaded through a straw to convert rotational motion into linear motion.

This Adafruit RGB Color Sensor used a built-in LED to help with detecting color.

Brief Movie

Narrative Description

First the RGB Color Sensor reads the color temperature of a given colored light. Lower values of the Color Sensor match to red lights, and higher values of the Color Sensor match to violet light. Then from the values read from the color sensor, the Ultrasonic Humidifier outputs lower levels of humidity for lower color readings and higher levels of humidity for higher color readings. The DHT Humidity Sensor then reads the percent humidity output from the Ultrasonic Humidifier. The reading from the Humidity sensor then corresponds to an output from the Servo Motor. Higher humidity readings lead to a higher degree of the motor’s position, and lower humidity readings lead to a lower degree in the motor’s position. Finally, the radial motor position is then translated to a linear displacement with an arm.

Progress Images

Early-on in our journey, this image shows the LCD screen, servo motor, and humidity sensor hooked up to the breadboard.

The humidity sensor is now mounted and the RGB color sensor is hooked up and mounted as well!

The Double Transducer is almost complete! This image shows the ultrasonic humidifier dipping into the Playdoh container of water.

Discussion

While certain components used in the double transducer were very kind to us, others were a little less reliable and difficult to control. For example, the humidity sensor read values very accurately, without much noise, and also had clear, concise sample code available in the libraries. However, the ultrasonic humidifier was difficult to control as it was not the most responsive of components. Sometimes when given an analogWrite value of 0, the humidifier was on full-blast, while at other times, it was off. We learned that the more complex the component, the more important quality was.  Having a higher quality humidifier could have significantly improved the reliability of our transducer.

One close-call we had was accidentally switching a ground and 5V wire connected to the humidity sensor and, thus, connecting 5V directly to the ground. This made the Arduino completely unresponsive and some boards also became hot-to-the-touch. After a few minutes of tinkering with the plugs, with a hint from Zach, we realized our error and switched the wiring back. Thankfully, nothing on the board had broken. We learned how helpful colored wires can be and how you should not rush the initial plugging in of the 5V and ground wires.

Schematic

Schematic for our Project 1 Double Transducer

Code Submission

/*
 * Project Title: The Color-Humidity-Displacement Double Transducer
 * 
 * Names: Achilles Lin, Sue Lee
 * 
 * Description: The code takes in 2 sensor inputs (RGB color sensor and
 * DHT Humidity Sensor) and produces 3 outputs (LCD Display, Ultrasonic 
 * Humidifier, and Hobby Servo Motor). The values printed to the LCD Display 
 * are mapped to the range [0, 99]. The color temperature is used as the
 * primary value which affects the Ultrasonic Humidifier value. The output 
 * to the Ultrasonic Humidifier is updated every 2 seconds.
 * 
 * Pin mapping:
 * 
 * pin   | mode   | description
 * ------|--------|------------
 * 2       input     DHT Humidity Sensor
 * 5       output    Ultrasonic Humidifier
 * 6       output    Hobby Servo Motor
 * 
 * Credit: Code samples and inspiration were taken from the following websites 
 * and example files:
 *  - DHT Humidity Sensor: https://www.electroschematics.com/arduino-dht22-am2302-tutorial-library/
 *  - timer-based periodic updating: Professor Zach <3
 *  - RGB Color Sensor: Humidity Sensor: tcs34725.ico example code
 * 
 * License Notice: 
 * Copyright 2020 Achilles Lin and Sue Lee
 * 
 * Permission is hereby granted, free of charge, to any person obtaining 
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
 * 
 */
#include "DHT.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Adafruit_TCS34725.h"
#include <Servo.h>

#define DHTPIN 2
#define DHTTYPE DHT22
#define MOTORPIN 6
#define HUMPIN 5

#define RED_LOWER_BOUND 2000
#define VIOLET_UPPER_BOUND 30000
#define HUMIDIFIER_UPPER_BOUND 120
#define RETRACTED_SERVO_VALUE 10
#define EXTENDED_SERVO_VALUE 180

unsigned long timer = 0;    // variable for timing
const int INTERVAL = 3000;  // milliseconds between updates

LiquidCrystal_I2C lcd(0x27, 20, 4);           // LCD I2C Display
Adafruit_TCS34725 tcs = Adafruit_TCS34725();  // RGB Color Sensor
DHT dht(DHTPIN, DHTTYPE);                     // DHT Humidity Sensor
Servo steeringMotor;                          // Hobby Servo Motor

void setup() {
  // initialize Serial port, LCD, color sensor, humidifier, humidity sensor, servo
  Serial.begin(9600);
  
  lcd.init();
  lcd.backlight();
  
  tcs.begin();
  
  pinMode(HUMPIN, OUTPUT);
  
  dht.begin();
  
  steeringMotor.attach(MOTORPIN);
}

/* displayLCD takes in normalized input/output values (0 - 99) and
 * prints them to the LCD display in the format:
    i:xx m:xx
           xx o:xx*/
void displayLCD (int colorTemp, int hum, int humSensor, int servo) {
  lcd.setCursor(1, 0);
  if (colorTemp < 10) { // adds space if value is single digit
    lcd.print(String("i: ") + colorTemp + " m:" + hum);
  } else {
    lcd.print(String("i:") + colorTemp + " m:" + hum);
  }
  lcd.setCursor(8, 1);
  if (humSensor < 10) { // adds space if value is single digit
    lcd.print(humSensor + String("  o:") + servo);
  } else {
    lcd.print(humSensor + String(" o:") + servo);
  }
}

void loop() {
  delay(300);
  lcd.clear();

  // initialize variables
  int colorTemp, colorTempNorm = 0;
  int hum, humNorm = 0;
  int humSensorNorm = 0;
  int servo, servoNorm = 0;

  // TRANSDUCER 1 (color (red->violet) to humidifier)
  // read color temperature
  uint16_t r, g, b, c;
  tcs.getRawData(&r, &g, &b, &c);
  colorTemp = tcs.calculateColorTemperature(r, g, b);
  Serial.println(String("color temp: ") + colorTemp);

  // normalize color temperature
  colorTemp = constrain(colorTemp, RED_LOWER_BOUND, VIOLET_UPPER_BOUND);
  colorTempNorm = map(colorTemp, RED_LOWER_BOUND, VIOLET_UPPER_BOUND, 0, 99);
  Serial.println(String("color temp norm: ") + colorTempNorm);
  Serial.println("");

  // update humidifier value every 2 seconds
  hum = map(colorTempNorm, 0, 99, 0, HUMIDIFIER_UPPER_BOUND);
  if (millis() >= timer) {
    analogWrite(HUMPIN, hum);
    timer = millis() + INTERVAL;
  }
  Serial.println(String("hum: ") + hum);

  // normalize humidifier value
  humNorm = map(hum, 0, HUMIDIFIER_UPPER_BOUND, 0, 99);
  Serial.println(String("hum norm: ") + humNorm);
  Serial.println("");

  // TRANSDUCER 2 (humidity sensor to linear displacement (3 cm))
  // read humidity sensor percentage input
  humSensorNorm = dht.readHumidity();
  Serial.println(String("hum sensor norm: ") + humSensorNorm);
  Serial.println("");
  
  // update servo position if humidifier input is valid
  if (!isnan(humSensorNorm)) {
    int hconstr = constrain(humSensorNorm, 20, 99);
    servoNorm = map(hconstr, 20, 99, 0, 99);
    servo = map(servoNorm, 0, 99, RETRACTED_SERVO_VALUE, EXTENDED_SERVO_VALUE);
    steeringMotor.write(servo);
  }
  Serial.println(String("servo: ") + servo);
  Serial.println(String("servo norm: ") + servoNorm);
  Serial.println("");

  displayLCD(colorTempNorm, humNorm, humSensorNorm, servoNorm);
}
]]>
Double Transducer – Vibration to Color Change https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-vibration-to-color-change/ Wed, 19 Feb 2020 00:58:18 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9586

Double transducer- from vibration to color

Overview of our double transducer

accelerometer; senses the vibration motor

Thermistor and the light bulb; transduces the vibration in to heat

LED; changes from purple to red depending on the heat level

Static State; the LED is purple

Heat generated based on the level vibration; the LED is now red

Description – 

We created a double transducer that takes in vibration signals and turns it into lamp brightness, which will then affect the thermistor and leads to color change from purple to red.

When the vibration gets higher, the lamp will be brighter, and it leads to temperature raising up around the thermistor; the thermistor will then send signals to the LED strip to change color from purple to red. When vibration stops, lamp turns off, temperature drops and the color goes back to red.

 

Process Images – 

getting the LED strip working

Combine two parts – Chloe blowing thermistor to cool it down

Getting the lamp to work

Discussion – 

Overall, the transducers we made are relatively easy to implement. Since we mainly need to deal with codes and the wiring is something similar to what we learnt in class, it is much simpler than those groups requires some physics knowledge. However, there are still some challenges during the process, especially in finding the right components that fit together. As a person who really don’t like reading instruction manuals (yes, no shame), this project taught me a lot about reading the labels and looking up manuals online to find out the correct ways to connect components together. I got a lot of instructor help to figure out the correct components and correct schematics hopefully next time I can improve a bit by being able to read the manuals figure out the wiring on my own.

The difficult part was struggling with the power source. Our laptop could not accept the transistor when it is connected, so we had to upload our code every time to the arduino board and unplug it, which made it hard to notice the change to the physical part whenever the code was updated. Also using the 12 volt power source was something that we never tried before, however, it was successful. It was hard to wire the lamp, transistor, and the power source together, yet with some help from our professor, it all made sense to us eventually. The accelerometer was the easiest part. We thought using the x value of the accelerometer to convert it into heat would be the best because we realized that the x value was the most-changing value after a few test plotting. Setting the range for x value was the hardest part. Depending on the range, the lamp could be too sensitive or too dull. However, we wrote a separate piece of code to test the ranges and then all we had left was to map it so that the x value will be on the same range as the lamp.

Schematics – 

 

Code submission – 

/*
Double transducer-vibration to color
A double transducer that exchanges the sensed vibration into color.
The vibration is sensed through an accelerometer, then the lightbulb is generated, the thermistor senses it, then the LED changes its color based on the strength.
Based on the strength of the vibration, the color will change from violet to red.
*/

#include <PololuLedStrip.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

/*-------LED------*/
// pin for LED strip
PololuLedStrip<7> ledStrip;
// number of LED on the strip
#define LED_COUNT 60
//an array of colors that is the length of LED_COUNT
rgb_color colors[LED_COUNT];

//pin for lightbulb
const int LIGHTPIN = 11;
/*-----accelerometer----*/
const int XPIN = A0;
const int YPIN = A1;
const int ZPIN = A2;

/*-------Thermistor------*/
const int TEMPPIN = A3;
int temVal;
//room temperature boundary
int UPBOUND = 500;
//hot temperature boundary
int LOWBOUND = 420;

/* Create an LCD display object called "screen" with I2C address 0x27
which is 16 columns wide and 2 rows tall. You can use any name you'd like. */
LiquidCrystal_I2C screen(0x27, 16, 2);

int x = 123;
//record old accelerometer value
int oldX;
int xVal;

byte frowny[] = {
B00000,
B11011,
B11011,
B00000,
B00000,
B01110,
B10001,
B00000
};

void setup() {
// pins for lightbulb and accelerometer
pinMode (LIGHTPIN, OUTPUT);
pinMode(XPIN, INPUT);
pinMode(YPIN, INPUT);
pinMode(ZPIN, INPUT);

// initialize the screen (only need to do this once)
pinMode(TEMPPIN, INPUT);

Serial.begin(9600);

xVal = analogRead(XPIN);

// initialize the screen (only need to do this once)
screen.init();

// turn on the backlight to start
screen.backlight();

delay(1000);
//print constant letters on screen
screen.clear();
screen.home();
screen.print("i:");

screen.setCursor(6, 0);
screen.print("m:");

screen.setCursor(12, 1);
screen.print("o:");
}

void loop() {
// put your main code here, to run repeatedly:

//get the temperature;
temVal = analogRead(TEMPPIN);
Serial.println(temVal);

//compares the difference between the old accelerometer x value and the new value
oldX = xVal;
xVal = analogRead(XPIN);
int dif = abs(oldX - xVal);
int lightVal = map(dif, 0, 520, 0, 255);

//light is off in a static range, else it is on in a range
if ( dif > 10) {
analogWrite(LIGHTPIN, dif);
}
else {
analogWrite (LIGHTPIN, 0);
}

if (temVal >= UPBOUND) {
temVal = UPBOUND;
}
//determine upper bound
if (temVal <= LOWBOUND) {
temVal = LOWBOUND;
}

//map it to the color range
int temMap = map(temVal, LOWBOUND, UPBOUND, 0, 1040);
int h = (temMap >> 2) % 360;

//turn on LED 57,58,59
for (uint16_t i = 57; i < LED_COUNT; i++)
{
colors[i] = hsvToRgb(h, 255, 255);
}

// Write the colors to the LED strip.
ledStrip.write(colors, LED_COUNT);

delay(50);

// just count up to show x changing (the rest of the text will remain untouched)
screen.setCursor(2, 0);
screen.print(map(xVal, 0, 525, 0, 99));

screen.setCursor(8, 0);
screen.print(map(lightVal, 0, 255, 0, 99));

screen.setCursor(8, 1);
screen.print(map(temVal, LOWBOUND, 515, 0, 99));

screen.setCursor(14, 1);
screen.print(map(h, 0, 359, 0, 99));
delay(200);
}

// hsv to rgb converter
rgb_color hsvToRgb(uint16_t h, uint8_t s, uint8_t v)
{
uint8_t f = (h % 60) * 255 / 60;
uint8_t p = (255 - s) * (uint16_t)v / 255;
uint8_t q = (255 - f * (uint16_t)s / 255) * (uint16_t)v / 255;
uint8_t t = (255 - (255 - f) * (uint16_t)s / 255) * (uint16_t)v / 255;
uint8_t r = 0, g = 0, b = 0;
switch ((h / 60) % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
return rgb_color(r, g, b);
}

]]>
Double Transducer: Magnetic Field Strength to Vibration Strength https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-magnetic-field-strength-to-vibration-strength/ Wed, 19 Feb 2020 00:55:35 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9397 Description:

This is a double transducer that converts magnetic field (first input signal) into a motor movement (first output and second input signal) generated by reading the orientation of the motor, then reads that signal to convert to vibration (second output signal). This double conversion overall allows the magnetic field strength to be converted to vibration strength.

Sequence:

  1. (input 1, magnetic field strength) The triple-axis compass magnetometer sensor provides analog magnetic field strength data. The closer the magnet, the stronger the magnetic field strength that is detected by the sensor.
  2. (output 1, input 2, movement) The numbers from the sensor make the hobby servo motor to move within the range of 0 to 90 degrees. As the motor creates a movement, the ADXL335 analog accelerometer that is attached to the motor detects the change in orientation.
  3. (output 2, vibration strength) Then, this data is sent to the pancake vibration motor, generating vibration of according strength.

Overall photo

Detail Images:

Triple-axis compass magnetometer sensor with its 5-pin header on the back side

Triple-axis compass magnetometer sensor with its x, y, z axes marking

Hobby servo motor with ADXL335 analog accelerometer fixed on

Pancake vibration motor

Video:

 

Progress Images:

Intermediate testing step to figure out the motor integration

Parts assembled except for the LCD and magnetometer; serial monitor used to debug

All parts assembled and working except for the magnetometer

Discussion:

We initially had a hall effect sensor to detect the magnetic field, but we replaced it with the triple-axis compass magnetometer sensor so that we could have analog, instead of digital, data. This was perhaps the hardest part because finding the right library took some time, and figuring out the range and calibrating were difficult because the sensor would overload when the magnet got too close to the sensor.

A physical difficulty was fixing the accelerometer well to the hobby servo body and adjusting the angles to be consistent with the angles in the code. We had to use some cardboard pieces to account for the length of the screw and the limited space to put the screw through while making the connection tight, and we had to do some trial and error for making sure that the motor along the correct range of angles. We also had to be careful with the pancake vibrator because the wires were very thin. We resolved by soldering and fixing the vibrator with tape onto the raised foam core board platform.

The challenge towards the end was mapping the magnetic field input range to appropriately to the previous group’s output. Because the magnetic field strength was weaker than anticipated, it was difficult to output the vibration strength strongly enough for the next group to have a good input range.

Some things that were straightforward were wiring and mapping the LCD display system and the movement mechanism parts because they were largely covered during class. Figuring out how to output signals with motors was also relatively easy.

We could gain soldering skills, and learn how to easily calibrate and map values across various parts. We could have done better with wire color-coding.

Schematic:

Code:

/* Title: Double Transducer: Magnetic Strength to Vibration Strength
 * Authors: Ivan Zhang and Youie Cho
 * Libraries: LiquidCrystal by Arduino, Adafruit, QMC5883LCompass by
 * MRPrograms
 *
 * 
 */

//all the libraries
#include <Servo.h>
#include <math.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <QMC5883LCompass.h>
LiquidCrystal_I2C screen(0x27, 16, 2); //create screen called screen
QMC5883LCompass compass; //create compass named compass
Servo steeringMotor; //create a motor object called steeringMotor

//naming all used pins
const int XPIN = A0;
const int YPIN = A1;
const int ZPIN = A2;
const int MOTORPIN = 3;
const int BUZZPIN = 11;

//setup timing
unsigned long timer = 0;
const int INTERVAL = 500;

//naming the intensity of the axis and buzzer
int xAxis;
int buzzInt;
int motorAngle;
int magStr;

//naming the output to LCD
int magPrint;
int buzzPrint;
int motorPrint;
int accPrint;

//defining the range of the input
int magMin = 6000;
int magMax = 8100;

void setup(){
  //set up motor
  steeringMotor.attach(MOTORPIN);

  //set up accelerometer
  pinMode(XPIN, INPUT);
  pinMode(YPIN, INPUT);
  pinMode(ZPIN, INPUT);

  //set up buzzer
  pinMode(BUZZPIN, OUTPUT);
  Serial.begin(9600);

  //set up screen
  screen.init();
  screen.backlight();
  screen.home();

  //set up compass
  compass.init();
}

void loop(){
  //get compass data
  compass.read();
  magStr = abs(compass.getY());

  //TRANSDUCER 1
  //map mag strength to motor angle
  motorAngle = abs(map(magStr,magMin,magMax,0,180));
  //set motor angle
  steeringMotor.write(motorAngle);

  //TRANSDUCER 2
  //map the XPIN to the angle from the rotation
  xAxis = abs(map(analogRead(XPIN), 400, 280, 0, 180));
  //map the buzzer intensity from the xAxis
  buzzInt = map(xAxis, 0, 180, 0, 255);
  //set buzz intensity
  analogWrite(BUZZPIN, abs(buzzInt));

  //debugging serial
  Serial.print(magStr);
  Serial.print(',');
  Serial.print(motorAngle);
  Serial.print(',');
  Serial.print(analogRead(XPIN));
  Serial.print(',');
  Serial.println(buzzInt);

  delay(100);

    
  //timer for LCD
  if (millis() >= timer){
      //reset screen
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("i:    m:");
      screen.setCursor(12,1);
      screen.print("o:");
        
      //map each value to 0-99 for printing
      magPrint = map(magStr, magMin, magMax, 0, 99);
      motorPrint = map(motorAngle, 0, 180, 0, 99);
      accPrint = map(xAxis, 0, 180, 0, 99);
      buzzPrint = map(buzzInt, 0, 255, 0, 99);

      //print numbers
      screen.setCursor(2,0);
      screen.print(magPrint);
      screen.setCursor(8,0);
      screen.print(motorPrint);
      screen.setCursor(8,1);
      screen.print(accPrint);
      screen.setCursor(14,1);
      screen.print(buzzPrint);
      timer = millis() + INTERVAL;
  }
}

 

Double Transducer in Context:

]]>
Double Transducer: Linear Motion to Volume https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-linear-motion-to-volume/ Tue, 18 Feb 2020 21:44:31 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9416 Project Title

Double Transducer

Video and Photos

Our completed double transducer with labels.

Input IR sensor to detect linear distance.

DC motor with IR sensor below to detect fan rotation speed.

Output speaker to produce pitch at different sound volumes.

Overall physical layout of our model.

Simple Narrative Description

There is a sensor on the side that tells a motor to start spinning faster once it detects when something is moving closer towards it. Another sensor reads in the speed of the motor’s rotation and tells a speaker to decrease its volume in response to faster rotation.

Progress Photos

We began by trying to figure out the wiring for the IR photosensor to read in distance values.

We tested out the DC motor at varying speeds depending on amount of voltage passed through.

We tested out the LCD display with varying sensor value inputs.

Discussion

To build our project, it was difficult to first approach all these new components we’d never worked with before. This project went through several iterations before we finally settled on our current design. We had considered using rotation angle as our intermediary step since we could use the hobby servo motor, which we were already familiar with. However, in the end we decided to go with rotation speed because although less familiar, it seemed to be a fun challenge.

Surprisingly the smaller mistakes often caused us the most trouble. For a long time we couldn’t figure out why the speaker wasn’t producing noise, and it turned out the speaker pin was just defined as the wrong number. The wiring for the IR proximity sensor also wasn’t working before we realized it was because we were just missing the pull down resistor at the side. For later components, we noted it was important to wire things carefully and pay attention to all the details of a schematic. In addition, mapping the values caused issues because we were giving the incorrect ranges. We realized it would be much easier to tackle this project one component at a time rather than a bit of everything at once as it would help isolate where errors were occurring.

One of the biggest challenges to tackle for this project was figuring out a way to measure rotation speed. Because there was no component which could directly read in speed, we had to get creative with our options. The Internet was mostly a useful resource as there were online tutorials to help us navigate through working with unfamiliar libraries and parts. But in this case, we over complicated things by trying to follow confusing online tachometer tutorials. In the end, we decided it would be better to take our own approach. We used an IR sensor and distance calculations to track rpm. It was a much easier solution as we got to reuse the same component, just repurposed as a tachometer.

For future improvements, we could try cleaning up the appearance of the physical model, and it might be nice to build a container to hide the messy wires. Despite all our setbacks, we were able to put everything together and get a successful transducer working. We learned not to overcomplicate simple tasks and to tackle future projects with the bigger picture in mind. Overall, this project was a good learning experience as we learned how to use new parts and apply creative problem solving.

Schematic

Code Submission

/*
 * Project Title: Double Transducer
 * 
 * Description: This code reads change in linear distance through 
 * an IR sensor. A motor's fan spins with a faster speed in response 
 * to closer distances and a slower speed in response to further 
 * distances. Another IR sensor acts as a tachometer by reading in 
 * the rotation speed of the fan by tracking # rotations per second.
 * An output speaker's volume decreases in response to faster fan 
 * rotation speeds and increases with slower speeds. An LCD screen 
 * displays updated status of these sensor & actuator states.
 * 
 * Pin mapping:
 * 
 * pin   | mode   | description
 * ------|--------|------------
 * A4      input     IR sensor (input)
 * A0      input     IR sensor (tachometer)
 *  9      output    sound speaker pin
 *  6      output    motor pin
 * 
 * Credit: Incorporated timer-based periodic update code 
 * from the course website: 
 * https://courses.ideate.cmu.edu/60-223/s2020/reference/class-log
 * 
 */

#include <NewPing.h>
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Volume3.h"

#define PHOTOPIN1 A4 // input IR sensor
#define PHOTOPIN2 A0 // rpm measurement IR sensor 
#define SPEAKER_PIN 9 // sound speaker
#define MOTORPIN 6 // DC motor pin
#define FREQUENCY 440 // pitch for speaker to play

Servo steeringMotor; // create motor object
LiquidCrystal_I2C screen(0x27, 16, 2); // create LCD display object

unsigned long timer = 0; // variable for timing LCD
unsigned long timer2 = 0; // variable for timing rpm
const int INTERVAL = 500; // milliseconds between LCD updates
const int INTERVAL2 = 1000; // milliseconds between rps updates
uint16_t frequency = 440; // speaker tone frequency

// val to store previous distance in rpm sensor
int photoVal2prev = 0;
int rotations = 0;
int rps = 0; // how many rotations per sec
int lastCycle = 0;

// update display of sensor & actuator states (4-10x per sec)
void updateScreen(int i, int m1, int m2, int o) {
  // map states within 0-99 range
  screen.setCursor(2, 0);
  screen.print(map(i, 500, 800, 0, 99)); // print input
  screen.setCursor(8, 0);
  screen.print(map(m1, 0, 1000, 0, 99)); // print m1
  screen.setCursor(8, 1);
  screen.print(map(m2, 0, 70, 0, 99)); // print m2
  screen.setCursor(14, 1);
  screen.print(map(m2, 0, 90, 0, 99)); // print output
}

void setup() {
  Serial.begin(9600); // open serial monitor at 9600 baud 
  
  pinMode(PHOTOPIN1, INPUT); // input IR sensor
  pinMode(PHOTOPIN2, INPUT); // tachometer IR sensor
  pinMode(MOTORPIN, OUTPUT); // DC motor
  pinMode(SPEAKER_PIN, OUTPUT); // sound speaker

  // start screen and print value labels
  screen.init();
  screen.backlight();
  screen.home();
  screen.print("i:");
  screen.setCursor(6, 0);
  screen.print("m:");
  screen.setCursor(12, 1);
  screen.print("o:");
}

void loop() {
  // sense linear distance from IR sensors
  int photoVal1 = analogRead(PHOTOPIN1);
  int photoVal2 = analogRead(PHOTOPIN2);
  
  // set rotation speed (spin faster at closer distance)
  int motorSpeed = map(photoVal1, 500, 800, 0, 255); 
  analogWrite(6, motorSpeed);

  // measure motor fan rotation speed
  int difference = 0;
  // check change in distance 
  if (photoVal2prev != 0)
    difference = photoVal2 - photoVal2prev;
    
  // when gap is detected, one full cycle completed
  if (difference > 100) {
    // increment rotations
    rotations++;
  }
  photoVal2prev = photoVal2;

  // update value for rotations per second
  if (millis() >= timer2) {
    rps = rotations;
    rotations = 0;
    timer2 = millis() + INTERVAL2; // update timer
  }

  // generate sound volume in response to rps
  int volume = map(rps, 0, 70, 600, 0);
  vol.tone(SPEAKER_PIN, FREQUENCY, volume);

  // update LCD screen display
  if (millis() >= timer) {
    updateScreen(photoVal1, motorSpeed, rps, volume);
    timer = millis() + INTERVAL; // update timer
  }
}

 

]]>
Double Transducer: Magnetic Field to Vibration Strength https://courses.ideate.cmu.edu/60-223/s2020/work/double-transducer-magnetic-field-to-vibration-strength/ Tue, 18 Feb 2020 02:23:16 +0000 https://courses.ideate.cmu.edu/60-223/s2020/work/?p=9350 Magnetic Field to Vibration Strength

Full image of the Double Transducer: Magnetic Field to Vibration Strength

Full image of the Double Transducer: Magnetic Field to Vibration Strength. (At an angle)

Close up of Magnetometer (input) to Light Bulb (output). The magnetometer detects the magnetic strength from the magnet’s distance. This causes the light bulb to light up in different ranges of brightness.

Close up of Light Bulb (Output) and Temperature Sensor (Input). The temperature sensor detects the different ranges of temperature the light bulb is emitting from the light bulb’s different brightness.

Close up of Temperature Sensor (Input) and Vibration Motor (Output). The range of temperature the temperature sensor detects determines the different ranges of strength the motor vibrates.

Description

We created a double transducer which is a device that converts magnetic field (such as the force of a magnet) through two more activities/functions to produce vibration strength (how fast something vibrates).

In our project, the farthest to the left is the magnetometer (the blue rectangular chip), which detects the magnetic field from having a magnet come closer or farther in distance to the chip. We coded this magnetometer so that it only detects forces from its x-axis (one side of this chip). Continuing on is the light bulb to the right. The lightbulb will turn on in different brightness when the magnet is at different distances to the magnetometer. The closer the magnet or when the magnetic field strength is high, the light bulb gets brighter. The lightbulb will produce heat and this will trigger the next mechanism, the temperature sensor (the black half cylinder with three legs/prongs). The temperature sensor will detect the temperature surrounding itself and once it gets to a certain degree, it will trigger a vibration motor (the small metal disk to the very right). The higher the temperature, the more the vibration motor will shake.

Process Images

This was the beginning of us building the hardware of this project.

In the beginning, we were using a hall sensor, but it was not as effective, so we changed to the magnetometer.

We were stuck on our I^2C LCD Screen numbers for a long time. It portrayed the wrong data (it had negative and triple-digit numbers).

Discussion

The easy part of the project was figuring out our idea and what our middle transducers would be. The planning for this was quite interesting as there were many input and output parts we could play with. We learned a lot from drawing each of the possible project schematics and finding different Arduino parts we could use. The most difficult part was the actual assembly of these parts and making them communicate with each other, especially since the temperature sensor was very sensitive and caused a lot of noise in the data and unstabilized the values. The unstable temperature values also led to difficulty in coding for the vibration motor.

Hands-on experience is the fastest way to learn something. After completing this project, we not only learned about Arduino parts that we never knew before but also essential concepts for wiring, such as using external batteries and power sources for parts that needed more than 5 volts. Getting stuck on writing the code and then solving the problems, helped us to remember how to problem-solve with Arduino and better prepare us for similar future problems.

If we were able to do this project again, we would have changed the temperature sensor to another mechanism because it was causing us many problems and was overall not an effective transducer. We could have used a LDR (light dependent resistor) to measure the intensity of the light to trigger our vibration motor.

SCHEMATIC

Our schematic for our double transducer

CODE
/*
   Title: Double Transducer: Magnetic Field to Vibration Strength
   Author: Hojung Kim and Patricia Yu

   Description: The magnetometer (input) measures the magentic strength
   from the distance of the magnet. This causes the light bulb (output)
   to light up in different ranges of brightness. Next, the temperature
   sensor (TMP36 - input) detects the ranges of the temperature the light
   bulb emits from light bulb's different brightness. The different
   temperature the temperature sensor detects determines the different
   strength of the vibration in the vibration motor.
  _____________________________________________________

   Pin mapping:
   pin   | mode   | description
   ------|--------|------------
   A1     input     TMP36 - temperature sensor
   09     output    light bulb
   05     output    vibration motor
  _____________________________________________________

  Credits:
  Temperature Measuring and Conversion Code (Specifically for TMP36):
  https://learn.adafruit.com/tmp36-temperature-sensor/using-a-temp-sensor by lady ada
  https://www.bc-robotics.com/tutorials/using-a-tmp36-temperature-sensor-with-arduino/ by Chris at BCRobotics

  Magnotometer/Compass code library:
  QMC5883LCompass by MRPrograms

  I^2C LCD Screen code library:
  LiquidCrystal_I2C by Frank De Brabander\

  Classmate Meijie and Chloe for helping us decode the LCD

*/

//code added from library:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <QMC5883LCompass.h>

//LCD screen setting initialized
LiquidCrystal_I2C screen(0x27, 16, 2);
int x = 123;

QMC5883LCompass compass;
const int LIGHTBULB = 9;
const int TMP36 = A1; //TMP36 initial temperature range 0° to 175° (0V to 1.75V)
const int VIBRATE = 5;

double voltage; //The variable we will use to store temperature in degrees.
unsigned long timer = 0; // variable for timing
const int INTERVAL = 200; // milliseconds between updates

void setup() {
  Serial.begin(9600);
  compass.init();
  pinMode(LIGHTBULB, OUTPUT);
  pinMode(TMP36, INPUT);
  pinMode(VIBRATE, OUTPUT);

  //Sets up the LCD Screen startup:
  screen.init();
  screen.backlight();
  delay(1000);

  screen.clear();
  screen.home();
  screen.print("i:");

  screen.setCursor(6, 0);
  screen.print("m:");

  screen.setCursor(8, 1);

  screen.setCursor(12, 1);
  screen.print("o:");
}

void loop() {

  //input - compass (magnetic strength):
  int x = 0;
  compass.read(); // Read compass values
  x = compass.getX(); // Return XYZ readings

  //middle output - light (brightness):
  int lightVal = map(x, -2000 , 6000, 0, 255); //limit the range of the light to 0-255
  lightVal = constrain(lightVal, 0, 255);
  analogWrite(LIGHTBULB, lightVal);

  // middle input - temperature sensor:
  int sensorInput = analogRead(TMP36); //read the analog sensor and store it
  voltage = (double)sensorInput / 1024; //find percentage of input reading
  voltage *= 5.0; //multiply by 5V to get voltage

  int temperatureC = (voltage - 0.5) * 100; //Subtract the offset and convert to degrees Celsius

  //output - vibration motor:
  int vibVal = map(temperatureC, 0 , 65, 0, 255); //limit the range of the light to 0-255
  temperatureC = constrain(temperatureC, 0, 255);
  vibVal = constrain(vibVal, 0, 255);

  if (temperatureC >= 30) { //when the tempereature is greater than or equal to 30°C the vibration vibrates at different range
    analogWrite(VIBRATE, vibVal);
  }

  else { // when the tmeperature is anything less than 30°C  it will turn off
    analogWrite(VIBRATE, 0);
  }

  // run LCD screen update procedure
  if (millis() >= timer) {
    timer = millis() + INTERVAL; // and update timer

    //limit the range of the light to 0-99
    screen.setCursor(2, 0);
    int magno = map(lightVal, 0, 255, 0, 99);
    screen.print(magno);

    screen.setCursor(8, 0);
    int light = map(lightVal, 0, 255, 0, 99);
    screen.print(light);

    screen.setCursor(8, 1);
    screen.print(temperatureC);

    screen.setCursor(14, 1);
    int vibration = map(vibVal, 0, 255, 0, 99);
    screen.print(vibration);
  }

}

 

]]>