Project 1 – Intro to Physical Computing: Student Work Fall 2022 https://courses.ideate.cmu.edu/60-223/f2022/work Intro to Physical Computing: Student Work Wed, 12 Oct 2022 19:44:22 +0000 en-US hourly 1 https://wordpress.org/?v=6.0.8 Double Transducer: RPM to Light to Magnetism https://courses.ideate.cmu.edu/60-223/f2022/work/double-transducer-rpm-to-light-to-magnetism/ Thu, 29 Sep 2022 05:35:21 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16294 by Gia Marino and Freda Su

Overall Photos

The whole transducer.

The whole double transducer.

Pictures of Each Part and Videos

Electromagnet. It creates a magnetic field when power runs through it’s coils

RC circuit and transistor circuit. Smoothes out and amplifies current.

Soldered LED and photo-resistor circuit. The photo-resistor detects the brightness of the LED

The rotary encoder wiring on a separate bread board. Reads RPM

Videos:

 

Simple Narrative Description

The knob measures how fast it’s spinning. This controls how bright the blue light will be, which is then measured by a light detector. The amount of light measured is used to determine how strong the magnet will be.

Process

Ideation

Or ideation for our double transducer

We had a few ideas for different energy forms, such as light, sound, and heat, as well as moving a magnet different distances. Some things, such as the sound and heat, would be hard to detect accurately, so we eliminated those ideas. Zach said the electromagnet might be a bit tricky but feasible and creative. He also recommended the simpler light transmission/detector system for the middle step, so we get more time to focus on the hard stuff.

Rotary Encoder

Getting rotary encoder to work

serial print screen equations

First, we learned about the Encoder library by Paul Stroffogen. We found a pin diagram online and ran the Basic code given with the library, and watched how the numbers in the Serial monitor changed as we twisted the knob. The numbers increase for rotating in one direction and decrease for the other direction, which is why you see the negative sign in the picture. We originally calculated angular velocity to convert to RPM but then switched to doing the math based on the ticks.

 

The fun part was obviously the math for figuring out how these numbers relate to calculating the RPM.

  1. Count the number of ticks it takes to complete 1 rotation: assuming the ticks are evenly spaced, then we can figure out how much of a rotation has happened by looking at the difference of two ticks.

    For our rotary encoder, there were 80 ticks per rotation, which means [latex]\frac{1 rotation}{80 ticks}[/latex] so each tick is [latex]\frac{1}{80}[/latex]th of a rotation.
  2. Time frame of measurement: counting ticks for a minute would be too slow, so it’s better to measure over a small portion of time and extrapolate the “instantaneous” RPM. We decided to measure over 1ms, since we have the millis() function in Arduino built in. Using dimensional analysis:

    (x ticks/ms) * (1 rotation/80 ticks) * (60000ms/min) ends up with units of RPM.

The LED

Brighter LED

Dimmer LED

We used analogWrite to send different voltages to the LED to give it different brightness. We also used the lowest resistor and tried different colors to see what could give us the brightest LED so that the change in brightness would be most drastic. The Serial monitor shows the brightness detected by the photoresistor, which helped us find the photoresistor range also.

Electromagnet

RC circuit Zach helped us simulate

Electromagnets with different coil size and wrapping

Testing the magnetic field

https://drive.google.com/file/d/1RH2CfyKH7a7ctSiBseOcUsEurZxuO4Pa/view video of capacitor resistor circuit reacting to changing voltages 
(too big to upload)

 

  1. We need to make sure the electromagnet works by itself, so we wrapped copper wire around a metal screw and drove current through it. At first, it wouldn’t pick up any paper clips at all:
    1. First, we added a lot more coils to make the magnet stronger.
    2. When that still didn’t work, we sanded the ends for a better connection with the 9V power supply we were using.
    3. That still wasn’t quite strong enough because it turns out the wire itself interfered with the magnetic field, so we had to sand off the outside layer. As you can see in the comparison between the 2 screws, the wire on the top (unsanded) is a stronger bronze color than the bottom screw (sanded), where you can see a lighter, more sandy/dusty brown where it was successfully sanded.
    4. We experimented with 2 different wire thicknesses for the coils and discovered that the thicker wire became a stronger magnet for about the same amount of coils. In addition, the thinner wire would break a lot more often when trying to sand the ends, so we like the thicker wire better.
  2. We learned that Arduino’s analog output isn’t truly a constant voltage, but more of a rapid cycle between the amount of time it sends 5V and 0V such that the “average voltage” is somewhere in between (Pulse Width Modulation). However, the electromagnet wouldn’t like the sudden changes between 5V and 0V, so we need to smooth out the voltage with a RC (resistor capacitor) circuit so it would become some close to “constant” voltage between 5V and 0V, turning it into a true analog voltage.
    1. This website simulates how the duty cycle turns into a smooth line: https://falstad.com/circuit/circuitjs.html?ctz=CQAgjCAMB0l5YCcyWrQDhAZjDA7HuugKwBMW6iYeFALNgGwjHOTMCmAtGGAFADyIWnHAM2xMKVFsZvAOZCRpIorZZSMqLwBOIUsOmrDbUht4BjI2DFHlmGbAQhu0WsmLoshPJH1IsWFCOfABKegbWbNRMkVB6VpoOtKS8APZx6nHCiJgsMPCQiHgMxAykeeC8FEIgAGIQFbhs3CAAIgCuAC4AngA6AM4Awt3mADbsvAAW2PFYvEA
  3. We also needed a transistor (IRLB8721PbF) in order to give the electromagnet enough current, since the Arduino doesn’t have much. The transistor takes in a voltage at the Gate terminal (output of RC circuit) and amplifies the output to the electromagnet. The value of transistors is that a small change in the Gate voltage would result in a much larger difference in the current, which would thus change the magnetic field strength.
    1. We started with trying the default parameters given in the website, because why not. Those values turned out to work well, so we kept those.
    2. We also added a heat sink to the transistor because it became hot quickly.

Discussion

While the project seemed pretty straightforward at first, there were details that we didn’t really consider that took more time than it should have. For example, even with simple parts of the system, such as the LED, we had to try different colors of LEDs/resistor combinations, because it turns out that they glow with different brightness when they all have the same amount of current. We wanted to use the brightest LED in order to give the photoresistor the widest range of values possible, since every LED can be dimmed to the same level, but not every LED can be brightened to the same level. Other small things that took longer than it should have included needing to sand all the wire used around the electromagnet, since we didn’t realize that the coating could interfere with the magnetic field until we tested it. Another problem we ran into was that the LCD would flash in order to refresh the correct values, and we tried for a while to get rid of the flashing at first, but couldn’t. So we realized it was probably just a necessary quirk of the system. Also we realized that we should’ve put more time into figuring out the LCD because ours didn’t end up working properly and it was most likely a little mapping issue we missed.

 

Secondly, for some reason one of the circuits that was working normally before suddenly decides to shut off whenever the rotary encoder spins. Not sure why that happens, and Zach wasn’t able to figure it out either. Another team suggested later that it could be that the other components use too much power, causing the Arduino to shut off. Of course, there could be other issues such as poor connections as well, but we double checked those, and again, it was working before without modifying anything.

 

If we were doing this again, perhaps we could consider doing different strategies for dividing the work. For this project, we both worked on the same part of the system at once. The benefits were that we both learned more deeply about each part of the circuit, and could help each other debug better because of it. However, this may not have been the most time efficient approach, since the output produced would be limited to whatever part of the system we were doing, instead of perhaps being able to make progress on multiple parts at once. But at the same time, by each working on different parts, perhaps we wouldn’t have as deep of an understanding of each step, and thus be less effective at helping each other debug. Perhaps we could take a mixture of these approaches and do the easy parts concurrently, but work on the hard parts together.

Functional block diagram and schematic

 

Code

/* Double Transducer: RPM to Magnetism 
Gia Marino and Freda Su

Our code takes inputs from a rotary encoder and translates them to positions on a 
tiny circle in the encoder. We used these positions to calculate the RPM based on 
the number of ticks that go by. Then we take that number and map RPM into a 
0 to 255 so that the LED can change brightness based on the rotation speed. Then we 
have the photoresistor that reads the brightness of the LED and maps it into a range
of 0 to 255 so it can send a voltage to our electromagnet. Lastly, the code takes the
RPM, LED input value, LED brightness, and input value into the electromagnet and maps
it into a range of 0-99. then prints it on the LCD screen

 Arduino pin | role   | description
 ------------|--------|-------------
 A3            input    photoresistor pin
 9             output   electromagnet pin
 6             output   LED pin
 2             input    rotary encoder pin A
 3             input    rotary encoder pin B
 SDA           output   LCD serial pin
 SCL           input    LCD clock pin
 GND           input    ground
 5V            output   5v

We based our if statement for the rotary encoder around Paul Stoffregen's Encoder
library code: https://github.com/PaulStoffregen/Encoderthe, specifically the "basic" 
example that comes with the library. We had a lot of assistance and influence from Remy's 
code for the LCD display (https://github.com/remyfrank01/Double-Transducer/blob/main/DOUBLE_TRANS.ino).
Lastly, we copied the LCD tutorial code and worked off of that, so our code for the LCD display 
is also heavily influenced by this tutorial: https://courses.ideate.cmu.edu/60-223/f2022/tutorials/I2C-lcd*/



#include <LiquidCrystal_I2C.h>  ////https://courses.ideate.cmu.edu/60-223/f2022/tutorials/I2C-lcd
#include <Wire.h> //LCD display
#include <Encoder.h> //https://www.pjrc.com/teensy/td_libs_Encoder.html

const int PHOTOPIN = A3; // photoresistor pin
const int LEDPIN = 6; // LED pin
const int EMPIN = 9; // electromagnet pin
const int totalTicks = 48; // amount of steps in one full rotation of the rotary encoder
const double anglePerTick = 1.0/totalTicks; // one Rotation/total ticks

/* Create an LCD display object called "lcd" with I2C address 0x27
    which is 16 columns wide and 3 rows tall. You can use any name you'd like. (col, row)
    (0,0) (1,0) (2,0) ... (15,0)
    (0,1) (1,1) (2,1) ... (15,1)*/
LiquidCrystal_I2C lcd(0x27, 16, 2);
Encoder myEnc(2,3); //pins 2 & 3 should have interrupt capablility

void setup() {
  pinMode(PHOTOPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);
  pinMode(EMPIN, OUTPUT);
  Serial.begin(9600);
  Wire.begin();
  
  // initialize the lcd (only need to do this once)
  lcd.init();

  // turn on the backlight to start
  lcd.backlight();
  lcd.clear();

}

unsigned long time0 = millis(); // time since last checked the rotary encoder
unsigned long timerLCD = millis(); // timer used for LCD dispkay
long oldPosition = -999;
float angVel; // anglular velocity
unsigned int brightness = 0; //the input for the LED that controls the brightness
unsigned int photoVal = 0; // the value we get from the photoresistor
unsigned int voltage = 0; // electromagnet voltage
unsigned int rpm = 0; // rotations per min calculated from rotary encoder steps


void loop() {

unsigned long globalTime = millis(); 
  
  long newPosition = myEnc.read();

  // convert ticks to rpm
  if (globalTime - time0 >= 1) { // checks every second
    double countTicks = abs(newPosition - oldPosition); // figures out how many steps passed by 
    oldPosition = newPosition;

    // reset time so when loop runs again we base our math on the most recent last position
    time0 = globalTime; 

    angVel = countTicks * anglePerTick;
    rpm = angVel * 60000; // converts just calculated angular velocity to RPM
  }

    // takes RPM and calculates a mapped output voltage so brightness is affected by rpm speed
    brightness = map(rpm, 0, 60, 0, 255);  //120-360 deg/sec to 0-255 analogWrite
    analogWrite(LEDPIN, brightness);

    //takes photoresistor value and maps an output voltage for the electromagnet so
    // it varies based on the photoresistor values
    photoVal = analogRead(PHOTOPIN);
    voltage = map(photoVal, 0,1023, 0, 255); //adjust photoVal range: 0-1023 photoVal to 0-255 analogWrite
    analogWrite(EMPIN, voltage);

  //LCD display
  
  if (millis() - timerLCD >= 250) {// every 1/4 sec
    timerLCD = millis();  //reset
    lcd.clear();
    // set cursor to home position, i.e. the upper left corner
    lcd.home(); 
    int mappedRPM = map(rpm, 0, 60, 0, 99);
    if (mappedRPM > 99) mappedRPM = 99;
    else if (mappedRPM < 0) mappedRPM = 0;
    lcd.print("i:" + String(mappedRPM));  //convert to rpm

    lcd.setCursor(6,0);  //middle of first row
    int mappedBrightness = map(brightness, 0, 255, 0, 99);
    if (mappedBrightness > 99) mappedBrightness = 99;
    else if (mappedBrightness < 0) mappedBrightness = 0;
    lcd.print("m:" + String(mappedBrightness)); //convert brightness
    // move cursor to column 8, row 1
    
    lcd.setCursor(8, 1);
    int mappedPhotoVal = map(photoVal, 0, 1023, 0, 99);
    if (mappedPhotoVal > 99) mappedPhotoVal = 99;
    else if (mappedPhotoVal < 0) mappedPhotoVal = 0;
    
    lcd.print(String(mappedPhotoVal));
    // move cursor to column 13, row 2
    lcd.setCursor(12, 1);

    int mappedVoltage = map(voltage, 0, 255, 0, 99);
    if (mappedVoltage > 99) mappedVoltage = 99;
    else if (mappedVoltage < 0) mappedVoltage = 0;
    lcd.print("o:" + String(voltage));
  }

  //goes to RC circuit, transistor etc

}

 

]]>
Double Transducer: Light Brightness to Rotational Speed https://courses.ideate.cmu.edu/60-223/f2022/work/double-transducer-light-brightness-to-rotational-speed/ Thu, 29 Sep 2022 03:19:07 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16089 Jonathan Lindstrom + Frances Adiwijaya

Narrative Description:

Our work uses a little thing which reads how bright the lights are in the room. When it is really bright it will tell a tiny blue thing that turns to move a stick stuck to it up and when it is dark the same tiny blue thing that turns moves the stick down. We then have a small black thing that reads how far between it and the nearest thing. This thing is lined up with the stick so when the lights are low and the thing that turns has the stick down the small black thing will read them as close together and when it is bright it will read them as farther away from each other. If they are close together our work will decide that a second, different thing that turns should go slow and if they are far then the same thing that turns goes faster.

Discussion:

During our project process, we ran into several roadblocks, but ultimately, we managed to power through and get make a cute double transducer.

Our original plan for our middle step was to use a linear actuator to change the height of the lego guy. While originally, it seemed like a good idea, it ended up not working, and we decided to change the middle step into a servo motor several days before the due date. The linear actuator only had two pins, and even using a motor driver, it could only go forwards, backwards, and change the speed of how fast it was moving. This was an issue for us, since we needed the linear actuator to be able to know its position, as we wanted it to change its position value based on the read of the photoresistor. While other groups found ways to work around the linear actuator, we decided to conserve our time and effort and switch to a servo motor, which worked for our purposes just fine. If we had stuck with using the linear actuator, we could have had a completely different product, (for good or for bad, who knows?) but in retrospect, I think we made a good decision to save our time and use an output we were already comfortable with.

I think in the future, it might be a good idea to do the more difficult parts of the project first. We spent a lot of time waiting for the linear actuators to arrive, even though we knew it was the most complicated part. Because we didn’t have a lot of information, we didn’t know that the linear actuators couldn’t sense their position. When they did arrive, we were slightly panicked, as our middle step wasn’t feasible. It might have been good to have a backup plan at that time, instead of coming up with one on the fly after the linear actuators were delivered.

I am also very glad that we had tried to line our output up with group 2’s input the night before the project. Because we tried everything the night before, when we ran into some issues, e.g. having our motor be too fast and not powerful enough, we had some time to change motors.

As a student with no experience in hardware nor software, I learned a lot during this project. I soldered for the first time, spent a lot of time debugging code, and did a lot of googling on Arduino sensors. We ran into several roadblocks, such as issues with our DC motor, LCD displays, as well as our linear actuator, but ultimately, our problem solving led to a successful end product.

Story Behind Our Project:

When we were building our transducers we thought that the mess of wires and such kind of looked like an alien planet. So we decided to base our project off Star Wars. Specifically the battle of Umbara. In this battle the droid army has control over the planet and is using it as their primary supply chain. The two clones are attempting to battle the droid army and take control of the Umbaran capital (the arduino + the lego tower) to cut off this supply chain.

Final Images:

Jonathan’s Final Image

Frances’s Final Image

Detail Photos:

This is the other main function of our project, the photoresistor. It will take in light and return an analog value based on the intensity of the light around it. You can see a clone hiding in the wires attacking the droids in the image below.

A nice focus shot of the droids and the lego pieces that make up the story of our double transducer.

A close up of our servo motor. We have attached a popsicle stick so it covers the laser distance sensor below. Furthermore you can see the clone using the popsicle stick as leverage to attack the Spider Droid below.

Close up of our laser distance module. This reads how far the distance from the popsicle stick above it is.

This shot highlights one of the main functions of our project, the motor driver and DC motor

Working Videos of Both Projects:

 

Progress Images:

Jonathan Progress Image #1: In this I got the laser sensor and motor working together so that changes in the laser sensor resulted in decreased or increased motor speed

Jonathan Progress Image #2: In this I took our success in Progress Image and I #1 and added the lcd and phototransistor so that the lcd will display the values of the laser sensor, motor, and phototransistor

Frances Progress Image #1: LCD Display working with the photoresistor. Photoresistor values would display in the LCD screen.

Frances Progress Image #2: Trying to figure out how to run the DC motor through the motor driver.

Video of the servo motor and the laser distance sensor working:

Schematics and Block Diagrams:

Block Diagram representing the inputs and outputs of our project

Electrical Schematic of our final project

 

Code:

/*
  Double Transducer Project: from light brightness to rotational speed
    Frances Adiwijaya and Jonathan Lindstrom
  Description: 
    In this project, we had to create a device that would use double transduction, 
    and convert the brightness of light to rotational speed. 
    We decided to use a servo's position and a laser distance sensor to be our 
    intermediate transduction. 
  Pin Mapping: 
     Arduino pin | description
   ------------- |-------------
   A0             Photoresistor
   5              DC Motor Driver
   6              DC Motor Driver
   3              Servo Motor
   SDA            SDA pin, LCD Display/Laser Distance Sensor (VL53L0X)
   SCL            SCL pin, LCD Display/Laser Distance Sensor (VL53L0X)
  References: 
  
*/

// import Libraries
#include "Adafruit_VL53L0X.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

Adafruit_VL53L0X lox = Adafruit_VL53L0X();

LiquidCrystal_I2C screen(0x27, 16, 2);
Servo gaugeMotor;

// set the variables for the pins
const int MOTORPIN1 = 5;
const int MOTORPIN2 = 6;
const int PHOTORESISTORPIN = A0;
const int GAUGEMOTORPIN = 3;
const int QUARTERWAIT = 250;
unsigned long quarterTimer = 0;

void setup() {
  // initialize the pins and LCD
  screen.init();
  screen.backlight();
  pinMode(MOTORPIN1, OUTPUT);
  pinMode(MOTORPIN2, OUTPUT);
  pinMode(PHOTORESISTORPIN, INPUT);
  gaugeMotor.attach(GAUGEMOTORPIN);

  digitalWrite(MOTORPIN1, LOW);
  digitalWrite(MOTORPIN2, LOW);
  Serial.begin(9600);

  
  //Setting up the laser sensor
  while (! Serial) {
    delay(1);
  }
  Serial.println("Adafruit VL53L0X test");
  if (!lox.begin()) {
    Serial.println(F("Failed to boot VL53L0X"));
    while(1);
  }
  Serial.println(F("VL53L0X API Simple Ranging example\n\n")); 
}


// DC motor functions
void set_motor_pwm(int pwm, int IN1_PIN, int IN2_PIN)
{
  if (pwm < 0) {  // reverse speeds
    analogWrite(IN1_PIN, -pwm);
    digitalWrite(IN2_PIN, LOW);

  } else { // stop or forward
    digitalWrite(IN1_PIN, LOW);
    analogWrite(IN2_PIN, pwm);
  }
}
void set_motor_currents(int pwm_A)
{
  set_motor_pwm(pwm_A, MOTORPIN1, MOTORPIN2);
}
void spin_and_wait(int pwm_A)
{
  set_motor_currents(pwm_A);
}

void loop() {
  // assigning a variable to the light input
  int lightVal;
  lightVal = analogRead(PHOTORESISTORPIN);
  lightVal = map(lightVal, 220, 1000, 0, 100);

  // laser sensor stuff 
  VL53L0X_RangingMeasurementData_t measure;
  Serial.print("Reading a measurement... ");
  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
  if (measure.RangeStatus != 4) {  // phase failures have incorrect data
    int distanceVal = measure.RangeMilliMeter;
    Serial.print("Distance (mm): "); Serial.println(distanceVal);
  } else {
    Serial.println(" out of range ");
  }

  // set a variable for the servo's position, and change it based on the photoresistor value
  int motorPos = 0;
  motorPos = map(lightVal, 0, 100, 170, 92);
  gaugeMotor.write(motorPos);

  // change the motor speed depending on the laser sensor's read
  int motorspeed = map(measure.RangeMilliMeter, 20, 100, 65, 194);
  set_motor_currents(motorspeed);

  // store the laser sensor's read in a variable
  int distanceVal = measure.RangeMilliMeter;

  if ((millis() - quarterTimer) >= QUARTERWAIT) {
    
    //print the intermediate values (mapped)
    
    screen.clear();
    screen.home();
    screen.print("i:");
    screen.print(lightVal);

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

    motorPos = map(motorPos, 170, 92, 0, 99);
    screen.print(motorPos);

    screen.setCursor(8, 1);
    distanceVal = map(distanceVal, 20, 100, 0, 100);
    screen.print(distanceVal);

    screen.setCursor(12, 1);
    screen.print("o:");
    motorspeed = map(motorspeed, 65, 194, 0, 100);
    screen.print(distanceVal);

    quarterTimer = millis();
  }

}

 

]]>
Double Transducer: Height to Pitch https://courses.ideate.cmu.edu/60-223/f2022/work/double-transducer-height-to-pitch/ Thu, 29 Sep 2022 01:47:50 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16087

Ethan’s final double transducer

Harry’s final double transducer

Process Images:

Our initial layout (Harry)

The LCD screen working (Ethan)

VL53L0X Laser Sensor working (Ethan)

Starting to connect each step together (Harry)

Preparing to shave the hot glue stick (Harry)

Every step wired up together (Ethan)

Detail Photos:

Beautiful soldering job by Harry

The linear actuator is attached to slide potentiometer with a stick of hot glue

Double Transducer in Action:

 

Narrative Description:

    When an object moves within one to four inches of the laser distance sensor, the linear actuator will either push or pull along the slide potentiometer. Based on the location of the slide potentiometer knob, a respective pitch is played.

Discussion:

    We initially struggled with connecting the linear actuator with the other components of the project board. We were successfully able to devise a method to create a temporary connection between the knob of the slide potentiometer and the linear actuator using a slightly melted stick of hot glue. Once we had this connection it was very easy to incorporate the linear actuator with the project board — we simply just masking tape. However, this was only the beginning of our troubles with the linear actuator. Due to the nature of our double transducer, we wanted to be able to control the position of the linear actuator. Unfortunately, there were only two default motions — extend and retract. Luckily, we were cleverly able to implement a type of feedback control. Otherwise, we were going to need to scrape our entire middle step.

 

    There were technically components of this project that came easily to the both of us due to previous experience. For example, soldering the connections for the middle step and breadboard management were both emphasized in our previous coursework. Looking towards the future, a few areas of improvement are wire management and reducing the number of loops. If we were able to implement our double transducer more minimally, it would increase the quality of the project board.

 

    Throughout the project timeline, we need to make important decisions regarding how the double transducer was going to be designed. One noticeable example is how we decided to replace the ultrasonic ranger with a VL53L0X laser distance sensor. This decision was overall made because we desired a more accurate measurement for our middle step. On the software-side, we needed to make decisions regarding how each signal would behave with each other. For example, we had the resistance value of the slide potentiometer relate directly to the pitch of the speaker. This decision was born out of the desire of a more direct relationship between the potentiometer and the pitch.

 

Block Diagram:

 

Electric Schematic:

 

 

Code:

/*
* @Project Title: Double Trasducer: Height to Pitch
* 
* @Authors: Harry Schneider & Ethan Lu
* 
* @Description: 
* - Using a VL53L0X Laser Sensor to measure the height
* of an index card (1''- 4'').
* 
* - The measured height determines how far the linear
* actuator moves, which drags the knob of the slide
* potentiometer.
* 
* - The Ardunio maps the potentiometer signal to [200, 2000]
* and sends a frequency to be played by the speaker.
* 
* @Mapping: 
* Arduino pin | role | description
* ------------|--------|-------------
* A0 INPUT Potentiometer Signal
* A4 INPUT VL53L0X SDA
* A5 INPUT VL53L0X SCL
* SCD INPUT LCD SDA
* SCL INPUT LCD SCL
* 7 OUTPUT Linear Actuator INPUT
* 8 OUTPUT Linear Actuator INPUT
* 9 OUTPUT Speaker Signal
*/


/* import header files */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <VL53L0X.h>

/* object declaration */
VL53L0X sensor;
LiquidCrystal_I2C screen(0x27, 16, 2);

/* pin-related variables */
const int res_pin = A0;
const int laser_SDA = A4;
const int laser_SCL = A5;
const int act_out1 = 7;
const int act_out2 = 8;
const int pitch_out = 9;

unsigned long prev_distance = 0;

/* VL530X-related varaibles*/
unsigned long lcd_timer;
unsigned long count = 0;

/* unfiltered sensor variables */
unsigned distance_val;
unsigned pot_val;
unsigned pitch_val;

/* filtered sensor variables */
unsigned adjusted_distance_val;
unsigned adjusted_pot_val;

void setup() {
  Wire.begin();

  /* pin setup */
  pinMode(res_pin, INPUT);
  pinMode(laser_SDA, INPUT);
  pinMode(laser_SCL, INPUT);

  pinMode(act_out1, OUTPUT);
  pinMode(act_out2, OUTPUT);
  pinMode(pitch_out, OUTPUT);
  
  /* VL53L0X Sensor */
  sensor.init();
  sensor.setTimeout(500);
  sensor.startContinuous();
  
  /* I2C display */
  screen.init();
  screen.backlight();
  screen.home();
}

/* sends out the linear actuator */
void extend() {
  digitalWrite(act_out1, LOW);
  digitalWrite(act_out2, HIGH);
}

/* returns the linear actuator */
void retract() {
  digitalWrite(act_out1, HIGH);
  digitalWrite(act_out2, LOW);
}

void loop() {
  distance_val = sensor.readRangeContinuousMillimeters();

  if (distance_val < 154) {
    pot_val = analogRead(res_pin);
    pitch_val = map(pot_val, 0, 918, 200, 2000);
  
    /* send signal to speaker */
    tone(pitch_out, pitch_val);
    
    if (prev_distance < distance_val) {
      extend();  
    }
    else if (distance_val < prev_distance) {
      retract();
    }
  
    /* status display */
    if (250 <= millis() - lcd_timer) {
      /* safety clear */
      if (count % 2 == 0){
        screen.clear();
      }
 
      adjusted_distance_val = map(distance_val, 52, 154, 0, 99);
      adjusted_pot_val = map(pot_val, 0, 1050, 0, 99);

      /* update LCD screen*/
      screen.setCursor(0, 0);
      screen.print("i:" + String(adjusted_distance_val));
  
      screen.setCursor(6, 0);
      screen.print("m:" + String(adjusted_distance_val));
  
      screen.setCursor(8, 1);
      screen.print(String(adjusted_pot_val));
  
      screen.setCursor(12, 1);
      screen.print("o:" + String(pitch_val));
  
  
      screen.home();
      lcd_timer = millis();
      count++;
    }
    
    prev_distance = distance_val;
  }
  else {
    /* safety clear */
    if (count % 2 == 0){
        screen.clear();
    }

    /* return to base LCD screen */
    screen.setCursor(0, 0);
    screen.print("i:" + String(99));

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

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

    screen.setCursor(12, 1);
    screen.print("o:" + String(99));
    noTone(9);
    
    count++;
  }
}

 

]]>
Double Transducer: Rotational Position to Height of Index Card https://courses.ideate.cmu.edu/60-223/f2022/work/double-transducer-rotational-position-to-height-of-index-card/ Thu, 29 Sep 2022 01:38:32 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16149 Ethan Hu & Francesca Menendez

Simple Narrative Description:

When a knob is turned, a fan blows wind faster or slower accordingly, which then activates an airflow sensor. This then makes an index card move up and down.

Technical Description:

Our double transducer consists of a potentiometer, DC fan, servo motor, wind sensor, and an Arduino to change the height of an index card according to the rotational position of the potentiometer. To achieve this, we first took the analog input of the potentiometer and translated that into a PWM signal which was then sent to a MOSFET that controls the RPM of the fan. Placed in front of the fan, we had our wind sensor taking measurements of the wind speed created by the fan, which was then processed through a series of calculations to determine an output value to the servo motor that then moves the index card vertically.

Final Double Transducers:

Board 1

Board 2

Detailed Photos:

MOSFET + Soldering

Moving the Index Card using Servo Motor

Fan + Airflow Sensor

Potentiometer Input

Discussion

The easiest part of our project was coming up with the idea. Using the requirements given to us – rotational position to the movement of an index card, we thought it would be interesting – albeit ridiculous – to use a fan blowing wind into a wind sensor (which neither of us had used before) to then make an index card move up and down using a servo motor. Beyond the simple initial ideation, we ran into lots of issues while building that required us to constantly recheck our work and debug, but ultimately worked when added to the chain of devices other students made.

Because we were working with varied voltages – 5V, 12V, 9V – we had to be especially careful when connecting to laptops and adding power. And though we did spend a lot of time testing each part of our device, we still ended up burning one servo motor and having to replace it due to connecting power sources incorrectly. Once we smelled the smoke, we were able to backtrack and fix the issue. However, we still faced a series of issues that continued to be problematic even when presenting our final devices. For example, the potentiometer could only read a specific range of values and if it would go under or over the threshold (500-900), it would completely freeze and only when back in the correct range, would begin working again as expected. Due to this potentiometer issue, we also found that the wind sensor could now only sense wind speed from 4-8 mph) and, because it was integer mapped, it could only move to 5 positions total, making it clunky when moving. However, we were able to fix this problem by multiplying our values by 10 so that, for example, instead of reading 5.5 mph, it can be read as 55, 60, etc. and, because it is an integer, it can be read more easily. Additionally, we faced some challenges in getting the LCD display to work properly, but by going through our code multiple times, we were able to solve the problem, although it still cannot read any value outside of the range created by the potentiometer problem. While we were able to get the first board working mostly as we expected, the second board, due to not testing the MOSFET before soldering it, the soldering might be one reason as to why it is not working as we would like it to.

While we did face a lot of challenges in building our two boards, we spent a lot of time using the multimeter testing different parts of our device and learned a lot about how to deal with issues that may arise in future projects. We especially learned how important it is to check power sources before connecting them so that nothing goes wrong. It was also especially interesting to learn about MOSFET and the possibilities in redirecting different voltages to avoid anything getting too much voltage. If given the opportunity to do this project again, we would take the time to test the MOSFET before soldering to make sure the second board works as planned and, if given time, we would figure out how to solve the potentiometer issue with range. Ultimately, despite the issues, our two devices reflect the time we put into working and reworking most things on the boards.

 Process Images

Testing Fan Action

Testing mosFET after Fan Failure

Airflow Sensor Coding

Testing 12V Power Source

Functional Block Diagram & Schematic

Electrical Schematic

Code

/*Copyright <2022> <Ethan Hu, Francesca Menendez>

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.

*/

/*Double transducer: Rotational position to the height of an index card.
Ethan Hu & Francesca Menendez



1. Take the input of the potentiometer
2. Output signal to the MOSFET, which will control the fan speed.
3. Take the input from the Wind Sensor.
4. Sent PWM signal to the Servo to adjust the height of the index card.

pin mapping:

Arduino pin / role  /  description
 A0           input    Wind speed input from the wind sensor
 A2           input    Temperature input from the wind sensor
 A5           input    Initial potentiometer input
 6            output   Signal to the MOSFET to control the fan speed
 7            output   PWM signal to the servo moter

*/

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

LiquidCrystal_I2C screen(0x27, 16, 2);
Servo smotor;

int potVal = 0;
int wind = 0;
int temp = 0;
int fanIn = 0;
int motorIn = 0;

void setup() {
  Serial.begin(9600);
  pinMode(6,OUTPUT);
  smotor.attach(5);
  Wire.begin();
  screen.init();
  screen.backlight();
  screen.home();
  screen.print("It's working!");
  delay(1000);
}

void loop() {
  int time = millis();
  //initial input from the potentiometer
  potVal = analogRead(A5);
  
  //the input that the fan is taking in
  fanIn = map(potVal, 0, 1023, 0, 255);
  analogWrite(6,fanIn);
  
  //direct and processed information from the wind sensor
  wind = analogRead(A0);
  float windMPH = pow((((float)wind - 264.0) / 85.6814), 3.36814);
  
  //input send out to the servo
  motorIn = map(windMPH*10,0,80,0,180);
  
  //motor set to execute input every quarter of a second
  if (time % 250 == 0){
     smotor.write(motorIn);
  }
  
  //LCD screen/serial monitor update data every half of a second
  if (time % 250 == 0){
    screen.clear();
    
    //update input from the potentiometer
    Serial.print("potVal:");
    Serial.println(potVal);
    screen.setCursor(0, 0);
    screen.print(potVal);
    
    //update the output to the fan
    Serial.print("fan input:");
    Serial.println(fanIn);
    screen.setCursor(8, 0);
    screen.print(fanIn);
    
    //update the wind speed from the wind sensor
    Serial.print(windMPH);
    Serial.println("MPH");
    screen.setCursor(0, 1);
    screen.print(windMPH);
    
    //update the output to the servo
    Serial.print("motorIn:");
    Serial.println(motorIn);
    screen.setCursor(8, 1);
    screen.print(motorIn);
  }
}

 

 

]]>
Double Transducer: Printed Color to Servo Angle https://courses.ideate.cmu.edu/60-223/f2022/work/double-transducer-printed-color-to-servo-angle/ Wed, 28 Sep 2022 19:14:09 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16103

Elise Chapman, Remington Frank, Sharon Li, Sarah Yun 

 

Simple Narrative Description

The machine sees the color on the paper strip, then uses that color to extend a motor a certain distance. That distance is detected and is used to angle a stick to a proportional degree.

Technical Description

Our double transducer involves the following components: an RGB sensor, a linear actuator, a VL53L0X distance sensor, a servo motor, LCD, and an Arduino Uno. The RGB sensor reads the RGB values of a printed color off of a given gradient sheet. These values are then used to calculate the respective hue (HSV) value for said color. From here, the hue value (range 0 – 360) is mapped to the time the linear actuator extends (0 – 3 seconds for full range of motion). The extension of the linear actuator is read as a change in distance by the VL53L0X time of flight distance sensor. The range of the linear actuators extension is zero to two inches, so the time of flight sensor reads a range of zero to two inches as well (approximately 0 – 50 millimeters). This distance is then mapped to the servo motor’s rotation (with a range of 0 – 90°). The LCD display shows all the aforementioned outputs as mapped to a 0 – 99 range.

Final Double Transducers

Elise’s Double Transducer

Remy’s Double Transducer 

Sharon’s Double Transducer

Sarah’s Double Transducer

Detail Photos

Clay is used to prop up the different parts of the double transducer, including suspending the initial color sensor. (Elise) 

The tcs34725 color sensor is propped up by an inch, and the color strip is removable (Sarah).

The linear actuator is responsible for driving its piston at a distance dependent on the color read by the RGB sensor (Remington).

The final output of our double transducer is the servo motor which has a specific rotational position that ranges from 0 to 90 degrees based on the previous inputs (Sharon).

Working Videos

Process Photos

At first, we did not have access to a small linear actuator, so we considered using a rack and pinion attached to a motor (Elise).

We were having some trouble getting the laser distance sensor (VL53L0X) to work initially, but I worked with Zach for a bit to get it working (Elise).

We initially started off with Ultrasonic distance sensors but transferred to VL53L0X laser distance sensors (Sarah).

We tested each part individually first before incorporating them into the bigger system (Sarah).

Attempting to debug the VL53L0X sensor. Using a multimeter to probe the pins to ensure proper connections. The error ended up being the RGB sensor and L0X sensor sharing the same default I2C address (Remington).

Here we swapped the stepper motor (for the rack and pinion system) for a linear actuator to simplify the mechanical process of the system (Remington).

All of the individual parts were able to work together but we were not mapping the values in a range from 0 to 99 or the correct format on our LCD display (Sharon).

Switched from the Ultrasonic Ranger to the VL53L0X Sensor where we realized that our soldering negatively affected the functionality and didn’t give us continuous values in the serial output (Sharon).

Discussion

Despite the simplicity of this double transducer in concept, its execution was harder than expected–especially its functionality. Although there were many late additions to our team, we were able to help each other solve similar problems and catch one another up (in which Sharon is very grateful and appreciative) in order to build four double transducers in a short period of time. 

For our input, the color sensor was reading the RGB values of the given color strip because that is how computers perceive color, but we had to convert it to HSV color values, which is how humans perceive color. It was an interesting experience to introduce device the way we perceive color in order to give it a singular value input (hue) to work with the linear actuator.

Another problem we encountered that wasn’t an apparent one to us was the correct and neat soldering of our parts, specifically on the theVL53L0X sensor. This ultimately led to many problems when testing the parts individually as well as together as one entity with the other inputs and outputs. We had to make sure that our VL53L0X distance sensor was facing the right direction and that none of the metals bridged between each other. This trial and error process allowed us to understand how important it was to solder carefully and neatly as well as check our schematics beforehand.

Lastly, mapping the values so that each of the parts works cohesively was a struggle. Because there were multiple values being transferred into the next action and another action after that, it was crucial that we map each value to fit the other components’ range in order to make the board functional. We had to initially test the VL53L0X distance sensor to calculate the distance it reads and limit our range to that, then maps that value to a degree the motor arm should move so that we can visually see its change. Although we got it in the end, it required some calculations and test trials to make the entire system work. 

However, overall we were able to get all the aspects working, and our team finished with functional double transducers.

Functional Block Diagram and Electrical Schematic

Block Diagram

Electrical Schematic

Code

/*
   Double Transducer: From Printed Color to Servo Motor Rotation
   {Sarah Yun, Elise Chapman, Sharon Li, Remington Frank}

   The code below processes and translates an input RGB reading from
   a printed color scale to a servo motor rotation (0 - 90°). It being
   a double transducer, the middle step is a linear actuator that extends
   and has its extension distance then read by a VL53L0x distance sensor.

   Pin Map:
  Pin | role | Description
 ----------------------------------------
   6 | output| VCC input for linear actuator
   7 | input | GND for linear actuator
  9  | output| Servo motor middle pin
 SCL | input | SCL pin on VL53L0X sensor
 SDA | input | SDA pin on VL53L0X sensor
  A4 | input | SCL pin on I2C LCD display
  A5 | input | SDA pin on I2C LCD display
  5V | output| VL53L0X sensor power
3.3V | output| Power for remaining components
 GND | input | Ground for all components

   RGB Sensor code sourced from the Adafruit TCS34725
   RGB Sensor example code. For further reference,
   refer to the following:

   https://learn.adafruit.com/adafruit-color-sensors/arduino-code

   VL53L0X sensor code is sourced from the Pololu library. This
   library can be found at the following link:

   https://github.com/pololu/vl53l0x-arduino

   Conversion from RGB to HSV values makes use of an algorithm sourced from:

   https://www.rapidtables.com/convert/color/rgb-to-hsv.html

   This has also been done as shown on the GeeksForGeeks site, which our
   code is very akin (identical) to:

   https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/
   All credit is due to the above for the respective sections mentioned.

*/

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

VL53L0X sensor;
/* Initialise with default values (int time = 2.4ms, gain = 1x) */
// Adafruit_TCS34725 tcs = Adafruit_TCS34725();

/* Initialise with specific int time and gain values */
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X);
Servo myServo;
LiquidCrystal_I2C lcd(0x27, 16, 2);

const int SERV = 9;
int FWD = 6;
int REV = 7;

// Initialize evaluation criteria and other values

double hue, saturation, value;
long duration;
double distance;

float prevRunTime;
float runTime;

unsigned long trueTime;
unsigned long currTime;
unsigned long delayTime;
unsigned long printTime;

int turn;
int prevTurn;
int counter;

int h_print;
int la_print;
int dist_print;
int s_print;

bool printer = 1;

void setup(void) {
  Serial.begin(115200);
  Wire.begin();

  lcd.begin();
  lcd.backlight();

  bool start = 1;

  // Begin color sensing sensor
  if (tcs.begin()) {
    Serial.println("Found sensor");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  }

  // Initilize pin modes
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  pinMode(FWD, OUTPUT);
  pinMode(REV, OUTPUT);

  digitalWrite(FWD, LOW);
  digitalWrite(REV, HIGH);
  delay(3000);

  myServo.attach(SERV);

  sensor.setAddress(0x52);
  sensor.setTimeout(500);
  if (!sensor.init())
  {
    Serial.println("Failed to detect and initialize sensor!");
    while (1) {}
  }

  // Start continuous back-to-back mode (take readings as
  // fast as possible).  To use continuous timed mode
  // instead, provide a desired inter-measurement period in
  // ms (e.g. sensor.startContinuous(100)).
  sensor.startContinuous();
}

void loop(void) {
  trueTime = millis();
  
  uint16_t r, g, b, c, colorTemp, lux;

  // Gather various color data from RGB sensor
  tcs.getRawData(&r, &g, &b, &c);
  // colorTemp = tcs.calculateColorTemperature(r, g, b);
  colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c);
  lux = tcs.calculateLux(r, g, b);

  Serial.print("Color Temp: "); Serial.print(colorTemp, DEC); Serial.print(" K - ");
  Serial.print("Lux: "); Serial.print(lux, DEC); Serial.print(" - ");
  Serial.print("R: "); Serial.print(r, DEC); Serial.print(" ");
  Serial.print("G: "); Serial.print(g, DEC); Serial.print(" ");
  Serial.print("B: "); Serial.print(b, DEC); Serial.print(" ");
  Serial.print("C: "); Serial.print(c, DEC); Serial.print(" ");
  Serial.println(" ");

  // RGB to HSV conversion
  double rWeight = r / 65535.0;
  double gWeight = g / 65535.0;
  double bWeight = b / 65535.0;

  Serial.println("Red Weight: "); Serial.print(rWeight); Serial.println(" ");

  // RGB to HSV algorithm
  double cmax = max(rWeight, max(gWeight, bWeight)); // maximum of r, g, b
  double cmin = min(rWeight, min(gWeight, bWeight)); // minimum of r, g, b
  double diff = cmax - cmin; // diff of cmax and cmin.
  double h = -1;

  // if cmax and cmax are equal then h = 0
  if (cmax == cmin) {
    h = 0;
  }

  // if cmax equal r then compute h
  else if (cmax == rWeight) {
    h = fmod(60 * ((gWeight - bWeight) / diff) + 360, 360);
  }

  // if cmax equal g then compute h
  else if (cmax == gWeight) {
    h = fmod(60 * ((bWeight - rWeight) / diff) + 120, 360);
  }

  // if cmax equal b then compute h
  else if (cmax == bWeight) {
    h = fmod(60 * ((rWeight - gWeight) / diff) + 240, 360);
  }

  Serial.println(" ");
  Serial.print("Hue: "); Serial.print(h); Serial.print(" ");
  Serial.println(" ");
  //--------------------------------------------------------------------------------------------------------

  // Map Hue value to linear actuator distance extended. Linear actuator has an operating time
  // range of 0 to 3 seconds, hence the mapping to 0 to 3000 milliseconds.
  
  runTime = map(h, 0, 360, 0, 3000);

  if(runTime > prevRunTime) {
    digitalWrite(FWD, HIGH);
    digitalWrite(REV, LOW);
    delay(runTime - prevRunTime);
  } 
  else if (runTime < prevRunTime){
    digitalWrite(FWD, LOW);
    digitalWrite(REV, HIGH);
    delay(prevRunTime - runTime);
  }

  prevRunTime = runTime;
  digitalWrite(FWD, LOW);
  digitalWrite(REV, LOW);

  //--------------------------------------------------------------------------------------------------------
  // Read distance extended of linear actuator by the time of flight sensor
  
  distance = sensor.readRangeContinuousMillimeters() - 63;
  Serial.print(distance);
  if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }

  Serial.println();
  //--------------------------------------------------------------------------------------------------------
  // Map distance detected into 90° rotation of servo

  turn = map(distance, 0, 50, 0, 90);
  if(turn > 90) turn = 90;
  myServo.write(turn);

  //--------------------------------------------------------------------------------------------------------
  // Print inputs and outputs to LCD display

  h_print = map(h, 0, 360, 0, 99);
  la_print = map(runTime, 0, 3000, 0, 99);
  dist_print = map(distance, 0, 50, 0, 99);
  s_print = map(turn, 0, 90, 0, 99);

  // Update the LCD display every 250 milliseconds (4 times per second)
  if(trueTime - printTime >= 250){
    lcd.home();
    lcd.print(String("i:") + String(int(h_print)));
    lcd.setCursor(6,0);
    lcd.print(String("m:") + String(la_print));
    lcd.setCursor(8, 1);
    lcd.print(String(dist_print));
    lcd.setCursor(12, 1);
    lcd.print(String("o:") + String(s_print)); 
  }
}

 

]]>