Project 1 – Intro to Physical Computing: Student Work Fall 2021 https://courses.ideate.cmu.edu/60-223/f2021/work Intro to Physical Computing: Student Work Tue, 05 Oct 2021 19:30:13 +0000 en-US hourly 1 https://wordpress.org/?v=5.8.9 Double Transducer: Position to Brightness https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-position-to-brightness/ Tue, 05 Oct 2021 19:06:34 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13850 Project 1: A Double Transducer: Position to Brightness

Peter Heo, John Hewitt, Evangeline Mensah-Agyekum

Gallery

View of Completed Project

View of Servo Motor Arm and Round FSR

 

 

Progress Images

Initial design for pressure actuation onto round FSR. Use of a shorter arm compared to final project.

Second version of pressure actuation using servo motor. Used a sort of piston to press down on the round FSR; however, it was finicky and not very stable.

I had everything ready to be soldered, but I didn’t realize the wires weren’t going to be long enough

my broken FSR

Discussion

In terms of difficulty, the brainstorming was one of the more easier parts, as we were able to quickly come up with what we wanted for our middle step to be. However, what was more difficult were the more specific mechanics of the middle step, as we were initially unsure of how to convert slide position into pressure. We started with a couple of ideas but we were able to narrow it down to an arm that would press down on the sensor. Peter tried to come up with a piston that would press and lift a weight onto the round FSR, and a sort of linear actuator that would press against a vertically placed FSR sensor, but both of these proved to be unwieldy and unreliable. We eventually settled on using a straw attached to a lever (servo arm) with enough flexibility to bend when pressed against the FSR.

There were also a couple of challenges along the way, as the code proved to be one source of difficulty. The motor would jitter and stutter periodically, making the servo arm unreliable in pressing down on the FSR sensor. The initial approach to this problem was try to smooth out the values, as we thought it was the potentiometer sending jumpy values, but we learned that it was actually timer collisions between the servo motor and the Neopixel LED ring causing the servo motor to jitter. From this, we learned that sometimes it is not the main user code that is unreliable, but the type of libraries and mechanical components we use.

It was also interesting to calibrate the sensors to not only work with each other, but fit into a 1-100 range on the lcd. It took some finessing, but as we messed around with the values it began to feel more intuitive.

Both Evangeline and Peter experienced a specific wiring issue with the potentiometer, where they switched up input with ground/5V wiring, causing a short circuit. However, after learning that the wiring was fixed, this problem was easily addressed. Soldering also proved an interesting step for Peter, as he was unfamiliar with it, and learning how to solder was something he enjoyed learning, albeit a bit nervously. John preemptively soldered up the different parts of the project without realizing the wires were not long enough to reach the opposite ends of the board, and had to re-solder things. John also snapped a round FSR when trying to reposition it, it was interesting to use such a delicate piece. John also learned to what a heatsink was, and would have probably damaged the FSR if he hadn’t been warned ahead of time.

This project also made us think more spatially, as organization and how components would be physically laid out on the board were also part of the project near the end of putting the whole thing together. However, the straightforwardness of the setup and its processes made it easier to assemble.

Block Diagram and Schematic

Code

//Project: Double Transducer (Position to Brightness)
//Peter Heo
//John Hewitt
//Evangeline Mensah-Agyekum

//The code below allows for the user to control a slide potentiometer, which then controls a servo motor.
//The slide potentiometer readings are converted to a servo motor angle.
//Said servo motor exerts pressure onto a round FSR through the use a physical arm.
//The round FSR readings are then converted to a certain degree of brightness for an Neopixel LED ring.
//All the input and output values of the mechanisms aforementioned are displayed onto an LCD screen...
//...all mapped to a range of 0-99.

//Pins                    Mode                    Description
//A0                      Input                   Slide potentiometer readings
//A3                      Input                   Round FSR readings
//6                       Output                  LED Ring
//10                      Output                  Servo motor

//Library and implementation for the Neopixel servo motor object:
//https://learn.adafruit.com/neopixels-and-servos/the-ticoservo-library

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_TiCoServo.h>
#include <Adafruit_NeoPixel.h>

//Intitialize pin for LED ring and the number of lights on said LED ring, as well as the LED object.
#define PIN  6
#define NUMPIXELS  16
Adafruit_NeoPixel light = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//Initialize pins for the slide potentiometer, the round FSR, and the servo motor, as well as the LCD screen object.
LiquidCrystal_I2C screen(0x27, 16, 2);
const int SLIDEPIN = A0;
const int PRESSUREPIN = A3;
const int SERVOPIN = 10;

//Initialize the time between display updates, and the timer itself.
const int LCDUPDATETIME = 250;
long prevDisplayTime = 0;

Adafruit_TiCoServo servoMotor;

void setup() {
  //Set up pins for inputs and outputs:
  //Slide potentiometer...
  //...Servo motor...
  //...and Round FSR sensor.
  pinMode(SLIDEPIN, INPUT);
  servoMotor.attach(SERVOPIN);
  pinMode(PRESSUREPIN, INPUT);

  //Set up LCD display.
  screen.init();
  screen.backlight();
  screen.display();

  //Set up light.
  light.begin();
  light.setBrightness(0);
  light.show();

  //Open the serial communication port to talk back to the computer.
  Serial.begin(9600);
}

void loop() {

  //Look at current time.
  long currentTime = millis();

  //Initialize slide potentiometer value.
  int slideVal = analogRead(SLIDEPIN);

  //Range should be about 11 degrees.

  //Set servo motor angle proportional to slide pot value.
  int servoVal = map(slideVal, 0, 1023, 142, 156);
  servoMotor.write(servoVal);


  //Read pressure value from FSR sensor.
  int pressureVal = analogRead(PRESSUREPIN);

  //Set brightness of LED ring proportional to pressure value.
  int brightnessVal = map(pressureVal, 0, 410, 0, 255);
  light.setBrightness(brightnessVal);

  //Set color of all LEDs on the LED ring to green.
  for (int i  = 0; i < NUMPIXELS; i++) {
    //Set color of all LEDs on the LED ring to green.
    light.setPixelColor(i, light.Color(128, 150, 128));
  }
  light.show();

  //Update LCD display every 250 milliseconds.
  if (currentTime - prevDisplayTime >= LCDUPDATETIME) {
    screen.clear();

    //Print slide values on a range of 0-99 on LCD display.
    int slideDisplayVal = map(slideVal, 1023, 0, 0, 99);
    screen.home();
    screen.print("i:");
    screen.print(slideDisplayVal);

    //Print servo motor value on a range of 0-99 on LCD display.
    int servoDisplayVal = map(servoVal, 142, 156, 99, 0);
    screen.setCursor(6, 0);
    screen.print("m:");
    screen.print(servoDisplayVal);

    //Print pressure values on a range of 0-99 on LCD display.
    int pressureDisplayVal = map(pressureVal, 0, 410, 0, 99);
    screen.setCursor(8, 1);
    screen.print(pressureDisplayVal);

    //Print brightness values on a range of 0-99 on LCD display.
    int brightnessDisplayVal = map(brightnessVal, 0, 255, 0, 99);
    screen.setCursor(12, 1);
    screen.print("o:");
    screen.print(brightnessDisplayVal);

    prevDisplayTime = currentTime;

  }



}

 

]]>
Double Transducer: Magnetic Field to Color Gradient https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-magnetic-field-to-color-gradient/ Tue, 05 Oct 2021 13:06:42 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13860 Our project was to create a double Tansducer that converted a magnetic field strength to a printed color output. To accomplish this, we chose two middle steps to convert the input signal to the output.

Overall Photo of Matthew’s Transducer

Overall photo of Shenai’s Transducer

Project Details

This three-axis magnetometer senses the strength of a magnetic field in 3 dimensions.

Shown is the Stepper Motor Driver. This provides the “brains” of the stepper, controlling direction, rotation angle and speed the motor rotates at.

The Stepper Motor is attached to a rack and pinion system that slides back and forth depending on how strong the magnetic field is. The system slides on a piece of acrylic to reduce the kinetic friction between the 3-D printed material and the cardboard surface.

The Ultrasonic Ranger detects how far the rack is using sonar, and sends the signal back to the Arduino. The ranger is soldered to a soldering board with a screw terminal soldered on behind it, making it easy to connect wires since they don’t move around as our transducer is at work.

The Arduino maps the distance measurement to an angle and instructs the Servo Motor to rotate to the specific angle accordingly.

The rotation of the Servo Motor reveals a part of the color gradient as it rotates. This color output is then read by the next group’s transducer.

Project Video

 

Project Description

We wanted to take input from the forces produced by a magnet, and give that information to a rotating motor. Then, the motor would rotate a gear, which would move a horizontal block with ridges that fit into the gear. We then sensed the position of the horizontal block, and then gave that information to another rotating motor. This second rotating motor was attached to a curved piece of paper, which had a range of colors on it. Our final output was the outward-facing color on the paper. All the information about positions and level of magnetic strength were printed as numbers on a screen for the viewer to see.

Progress Pictures

We used SolidWorks to model a rack and pinion we could 3-D print. The assembly was designed to move 1 cm for each 1/10th rotation of the motor, meaning the teeth on the rack are spread apart by 1 cm, the same measuring resolution as the ranger.

We utilized the IDEATE skylab to 3-D print the parts we needed for our project in a timely fashion.

process stepper wiring, was wired to smaller motor so we had to redo

We created the printed color using a semi-cylinder with a color gradient surface. We used a popsicle stick as the axel so we could easily incorporate the servo motor into our design. We changed to a gradient from a rainbow pattern to allow us to print any hue value that the magnetic field strength is mapped to.

Discussion:

We both learned a lot from this process. We enjoyed teaming up together because we were equally invested in the success of the project, and because both had (semi-overlapping but still) different areas of technical expertise, which allowed us to synthesize knowledge from our respective fields. For example, Matthew is a Mechanical Engineering major and really good at CAD, so we were able to make quality, physical mechanisms within our middle step via our rack and pinion. Shenai is an Electrical and Computer Engineering major and she tackled a lot of the wiring and programming challenges.

Comments from Shenai: I enjoyed the team aspect of this project, as well as the hands-on quality of the work, which was something I had been lacking in my other classes. I also felt that I grew as a debugger, both in code and circuitry. It was really satisfying when I spotted that Matt’s stepper motor wasn’t properly grounded, and was able to fix it quickly. Another super satisfying moment was when I combined our separate code into a single file, very quickly checked for timing issues/duplicated pins and variables, and it compiled and ran perfectly on the first try.

However, we had a terrible time with the stepper motor. We had to go through 3 different chips and 2 motors, and we had to discard our custom-made 3-D print that housed the smaller motor because the smaller motor wouldn’t work properly with any of the chips. The 3-D print made us want to hang onto the smaller motor because we had already put so much time into setting up our machine specifically for it, but finally we ended up pivoting to the more powerful and reliable motor. I think if we had been more willing to pivot entirely instead of changing little things to fit our past decisions, we would have breezed through this process faster. Although, I will say, I got really fast at unwiring and rewiring various stepper motors and their drivers, so this iterative process was helpful for my technical growth in that respect.

Comments from Matthew: It was very exciting to have the opportunity to apply my knowledge of mechanical systems and CAD into this project. Despite having to change which stepper motor we used, the parts I created for the project all worked without the need for iterative design and further prototypes. This saved the course budget some money as we didn’t have to keep printing and printing more materials. Together, we were able to synthesize electrical and mechanical design in an effective and creative way to produce a successful end result. In addition to applying my Mechanical Engineering skills, I was able to learn a lot on the electrical side as well. I was able to brush up my coding skills in Arduino, become familiar with different electrical connections and sensors (magnetometer, ultrasonic ranger, stepper and servo motors, etc), and I even got to learn how to strip and solder wires for the first time. I feel that the acquisition of these new skills will help me grow as an engineer and will allow me to be more versatile in upcoming projects.

Functional Block Diagram

Functional Block Diagram of our Project using diagrams.net

Electric Schematic

Electric Schematic for our project. We had access to a more simple LCD Display which allowed us to use SDA and SCL pins for both the magnetometer and the LCD display.

Code:

/*
 * Magentic Field to Color Gradient Double Transducer
 * 
 * By: Matt Wagner and Shenai Chan
 * 
 * Desc: Takes in a reading of magnetic field strength, 
 * converts it to rotational position on stepper motor, 
 * uses Ultrasonic Ranger to sense the position of a 
 * rack driver by the motor, feeds that information
 * to rotational position on a servo motor with substantial
 * delay to allow for proper reading of the servo position.
 * Sends input/output/intermediate information to an LCD display.
 * 
 * Pin mapping table:
 * SDA & SCL connected to both LCD and 3-axis magnetometer
 * 6  <-> servo 
 * 8  <-> dir on stepper motor
 * 9  <-> step on stepper motor
 * 11 <-> echo on ultrasonic ranger
 * 12 <-> trig on uultrasonic ranger
 * 
 * Credit:
 * Referenced starter code by Robert Zacharias on timing signal sends;
 * instantiating USR, LCD, and servo; and writing to LCD.
 * 
 * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/code-bites
 * https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd
 * 
 * Referenced docs from Patrick Wasp's AccelStepper lib & 
 * MPrograms' compass lib
 */

/* First, import some libraries: */

// I2C Library
#include <Wire.h>
// For the 3-axis magnetometer
#include <QMC5883LCompass.h>
// For the stepper motor
#include <AccelStepper.h>
// For the ultrasonic ranger
#include <NewPing.h>
// For the Servo Motor
#include <Servo.h>
// For the LCD display
#include <LiquidCrystal_I2C.h>

/* Then, instantiate objects that correlate to wired devices */

// Make LCD obj
LiquidCrystal_I2C screen(0x27, 16, 2);
int iVal;
int mVal1;
int mVal2;
int oVal;

// Make magnetometer obj
QMC5883LCompass compass;

// Make stepper obj, as well as corresponding variables for waits
// between writes, and keeping track of current position
#define dirPin 8
#define stepPin 9
#define motorInterfaceType 1
const int wait = 1000;
long timer = 0;
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);
int currentPos;

// Make USR obj
#define TRIGGER_PIN  12  
#define ECHO_PIN     11  
#define MAX_DISTANCE 200 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 

// Make servo obj
Servo gaugeMotor;
const int GAUGEMOTORPIN = 6;
const unsigned long DISPUPDATETIME = 500;
const unsigned long TIMEBETWEENREADS = 3000;
unsigned long lastMove = millis();
unsigned long lastUpdate = millis();

void setup() {
  // Initialize the serial port
  Serial.begin(9600);
  // Initialize I2C
  Wire.begin();
  
  // Initialize the servo motor
  gaugeMotor.attach(GAUGEMOTORPIN);
  
  
  // Initialize the magnetometer
  compass.init();
  
  // Initialize the stepper motor
  stepper.setCurrentPosition(0);
  stepper.setMaxSpeed(500);

  // Initialize the LCD 
  screen.init();
  // turn on the backlight to start
  screen.backlight();
  // set cursor to home position, i.e. the upper left corner
  screen.home();
}

void loop() {
  // read ultrasonic ranger
  int currDist = sonar.ping_cm ();
  mVal2 = currDist;
  Serial.print("Ping: ");
  Serial.print(currDist); // Send ping, get distance in cm and print result (0 = outside set distance range)
  Serial.println("cm");

  // map range distance to degree range 15 to 165 degrees
  int mappedAngle = map(currDist, 3, 11, 15, 165);
  oVal = mappedAngle;

  // write to LCD with appropriate timing
  if (millis() - lastUpdate >= DISPUPDATETIME){
    screen.clear();
    screen.print("i:");
    screen.print(iVal);
    screen.setCursor(5, 0);
    screen.print("m:");
    screen.print(mVal1);
    screen.setCursor(7, 1);
    screen.print(mVal2);
    screen.setCursor(11, 1);
    screen.print("o:");
    screen.print(oVal);
    screen.home();

    lastUpdate = millis();
  }

  // write to servo with appropriate timing
  if (millis() - lastMove >= TIMEBETWEENREADS){
    // move motor
    if (mappedAngle >= 15 && mappedAngle <= 165){
      gaugeMotor.write(mappedAngle);
      lastMove = millis();
    }
  }

  // read from magnetometer and write to stepper with appropriate timing
  if ((millis() - timer) >= wait) {
    
    if (stepper.distanceToGo() == 0) {
      Serial.println("in if statement");
      
      int z;
      compass.read();
      z = compass.getZ();
      iVal = map(z, 0, 37000, 0, 99);

      Serial.println(z);

      int loc = map(z, 0, 37000, 0, -200);
      if (0 <= z && z <= 37000) {
        if (0 >= loc && loc > -50) {
           stepper.moveTo(0);
        }
        else if (-50 >= loc && loc > -100) {
           stepper.moveTo(-50);
        }
        else if (-100 >= loc && loc > -150) {
           stepper.moveTo(-100);
        }
        else if (-150 >= loc && loc >= -200) {
           stepper.moveTo(-150);
        }
        Serial.println(loc);
        stepper.setMaxSpeed(25);
        stepper.setAcceleration(25);
        timer = millis();
        mVal1 = map(z, 0, 37000, 0, 99);
      }
    }
  }
  stepper.run();
}
]]>
Double Transducer: Rotational Speed -> Magnetic Field Strength https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-rotational-speed-magnetic-field-strength/ Tue, 05 Oct 2021 12:37:23 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13932 Alice’s Board

Overall photo

Video of working machine: when rotational speed is at its highest, the propeller spins fast and presses down on the force sensor a lot, which translates to the maximum rotation of servo motors bringing the magnet all the way to the right edge of the board.

Detail photos of parts

Servo motors holding the magnet (our output)

Propeller connected to a DC motor, standing upright with force sensor at the bottom of the propeller arm (our middle step)

Noel’s Board

Video

The encoder constantly samples rotational speed and determines the voltage passed on to the motor. The greater force translates across the FSR and is translated into servo position. Jittery human input produces similarly jittery magnet position.

Detail photos of parts

Our solution to the servo-encoder interface between projects, featuring hot glue and luck

The DRV8833, the chip that allows us to drive the DC motor with more than on and off

Narrative Description:

Our input is rotational speed. That is measured by how fast the knob (rotary encoder) gets turned at the left of our boards. This rotational speed is then used to calculate how fast we turn our DC motor, which is connected to a propeller. The propeller sits upright in the middle of our board and it is built in a way so that depending on the spinning speed, the propeller arm tilts in differing amounts to press onto the square force sensor resistor at the bottom. Based on the force reading, degree of rotation for the servo motors are determined. The servos are holding a piece of magnet and greater rotation equals to further distance of the magnet to the next team’s board. With different distances, we create different strengths of magnetic field which is our output. 

Progress Images:

An early mock up of the motor wiring. Using a motor with an encoder and voltage regulator board. Replaced by simple motor and motor driver.

Force Sensor Testing: wiring up the fsr to see how it reads by making it print in Serial Monitor Testing out the positions of servo motors. This was a critical step as we changed from our original design of using rubber bands arms sliding the magnet along a track to using a rigid arm.

One of the final troubleshooting phases. After re-seating every cable and testing every sensor, an incompatible power supply was the culprit.

Discussion:

The development of this project was a rollercoaster. Initial ambitions were blinding and made issues very easy to overlook, and there were several that we did. We made two simple backups and spent all of our creative power on integrating a windmill. We easily overlooked issues like creating linear motion with servos, balancing and connecting a propeller, and the interface between encoder and previous project’s servo. Our imagined outcome was not adherent to all the real physical limitations, and this would be apparent when we tried to share ideas. Solidworks,  hand drawings, and real physical progress ultimately bridged the gap between the imagined and the real.

Through our build we were constantly reminded of the many tools that had already been developed before us. In an attempt to use as powerful a motor as we could find, we showed our lack of intuition for thrust and knowledge of available parts. Thanks to our professor, online resources, and physical testing, we derived something efficient and conceivable. These resources were invaluable, as neither of us had any idea a DRV8833 (dual motor driver carrier) was an available tool. Peers also helped ideation, and the learning environment was a significant help in understanding what we could accomplish. Miniature breadboards and switching to square FSRs were two results of the vibrant learning environment.

One of the most frustrating steps was connecting libraries and pre-existing code with our own. From completely unresponsive to mapped and tuned scaling, the troubleshooting often involved scrapping entire drafts and scouring for relevant sample code. Here it was obvious that we were “standing on the shoulders of giants.” With documentation from the course webpage and from online users with valuable experience with each part, we had plenty of comments to read and build our understanding of how we could connect each sensor and output device. Despite the plethora of technical documentation, we still sometimes found ourselves adjusting parameters through trial and error. In combining available resources and first hand experience, we were able to develop the system we imagined.

Functional Block Diagram:

Schematic:

Code:

/*Project Title: Double Transducer: Rotational Speed -> Magnetic Field Strength
  Team Members: Noel Barnes, Yuqi (Alice) Zhou
  Description: Arduino takes in input from a rotary encode to calculate the rotational speed. 
             This speed is then mapped into a speed that's feed into the DC motor to drive a propeller.
             FSR then picks up a force reading from the tilt of the Propeller arm.
             The force reading is then mapped to a rotation degree for the servo motor. 
             Servo motors are moved based on the rotation to move the magentic forward. 
  Pin mapping:
    Arduino pin | role   | description
    ------------|--------|-------------
    A0            input     FSR for force sensing
    2             input     rotary encoder pin A
    3             input     rotary encoder pin B
    5             input     drv8833 A2 IN
    6             input     drv8833 A1 IN 
    10            output    servo motor #1
    11            output    servo motor #2 
    SCL                     SCL of LCD
    SDA                     SDA of LCD

  Rotoray Encoder Code adapted from https://dronebotworkshop.com/rotary-encoders-arduino/#Arduino_Motor_Encoder_Sketch DroneBot Workshop 2019
  DC motor code adapted from https://courses.ideate.cmu.edu/16-223/f2016/text/lib/WheelDrive.html#wheeldrive-doxygen
*/

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

LiquidCrystal_I2C screen(0x27, 16, 2);

// Motor encoder output pulse per rotation (change as required)
#define ENC_COUNT_REV 374

// Encoder output to Arduino Interrupt pin
#define ENC_IN 3

// Pulse count from encoder
volatile long encoderValue = 0;

// interval for measurements of rotational speed
int interval = 100;
const int quaterSec = 250;
unsigned long timer = 0;
long previousMillis = 0;
long currentMillis = 0;

int rpm = 0;
int rpmDisplay; // RPM display for the LCD screen
int motorRPM; // DC motor speed
int dcDisplay; // DC motor speed display for LCD screen

//make a servo object
Servo lilMotor;
Servo secMotor;
const int LILMOTORPIN = 11; // Servo Motor to pin 11
const int SECMOTORPIN = 10; // Servo Motor to pin 10

int force; // the analog reading from the FSR 
int forceDisplay; // force display for the LDC screen
int fsrPin = 0;     // the FSR and 10K pulldown are connected to a0

int rotation; // rotation degree for lilMotor
int rotationTwo; // rotation degree for secMotor
int rotationDisplay; // servo motor rotation display for LDC screen

#define A1PIN 6
#define A2PIN 5

void setup() {
  // Set encoder as input with internal pullup
  pinMode(ENC_IN, INPUT_PULLUP);
  // Attach interrupt
  attachInterrupt(digitalPinToInterrupt(ENC_IN), updateEncoder, RISING);
  // Setup initial values for timer
  previousMillis = millis();

  // initialize DC motor
  pinMode(A1PIN, OUTPUT);
  pinMode(A2PIN, OUTPUT);
  digitalWrite(A1PIN, LOW);
  digitalWrite(A2PIN, LOW);

  // tell Arduino where the servo motors are plugged in
  lilMotor.attach(LILMOTORPIN);
  secMotor.attach(SECMOTORPIN);
  // initialize the screen (only need to do this once)
  screen.init();
  // turn on the backlight to start
  screen.backlight();

  // lcd display template
  screen.home();
  screen.print("i:");
  screen.setCursor(6, 0);
  screen.print("m:");
  screen.setCursor(12, 1);
  screen.print("o:");

}

void loop() {
  // Update RPM value
  currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    // Calculate RPM
    rpm = (float)(encoderValue * 60 / ENC_COUNT_REV);
    //follow the range of 20-60 for rotational speed
    if (rpm > 60) {
      rpm = 60;
    }
    if (rpm < 20) {
      rpm = 20;
    }
    encoderValue = 0;
  }
  //rotational speed display
  if (millis() - timer >= quaterSec) {
    screen.setCursor(2, 0);
    rpmDisplay = map(rpm, 20, 60, 0, 99);
    screen.print(rpmDisplay);
  }

  // run DC motor with RPM 
  motorRPM = map(rpm, 20 , 60, 80, 255); //normalize rotational speed to input as DC motor speed
  digitalWrite(A1PIN, LOW);
  analogWrite(A2PIN, motorRPM);
  delay(2);
  
  // DC motor speed display
  if (millis() - timer >= quaterSec) {
    screen.setCursor(8, 0);
    dcDisplay = map(motorRPM, 80, 255, 0, 99); // normalized for display
    screen.print(dcDisplay);
  }


  // force reading
  force = analogRead(fsrPin);
  // limit to a force range that's appropriate for the propeller arm setup
   if (force > 400) {
      force = 400;
    }
    if (force < 50) {
      force = 50;
    }
    
  // force sensor display
  if (millis() - timer >= quaterSec) {
    forceDisplay = map(force, 50, 400, 0, 99); // normalized force for display
    screen.setCursor(8, 1);
    screen.print(forceDisplay);
  }

  // calculate rotation for the two servo motors
  rotation = map(force, 50, 400, 10, 170); // map force reading to motor rotation degree
  rotationTwo = 180 - rotation;
  
  // servo motor rotation degree display
  if (millis() - timer >= quaterSec) {
    rotationDisplay = map(rotation, 10, 170, 0, 99); // normalized rotation degree for display
    screen.setCursor(14, 1);
    screen.print(rotationDisplay);
    timer = millis();
  }

  // rotate the servo motors
  lilMotor.write(rotation);
  secMotor.write(rotationTwo);

}

void updateEncoder()
{
  // Increment value for each pulse from encoder
  encoderValue++;
}

 

]]>
Double Transducer: Rotational Position to Frequency of Tapping https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-rotational-position-to-frequency-of-tapping/ Tue, 05 Oct 2021 06:17:05 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13965 Description

This project is a double transducer that takes a rotational position (0-90) as input and produces a frequency of tapping (20-60 taps per min) as output. The double transducer takes in the rotational degree and adjusts the rotation of the stepper motor accordingly. The stepper motor then drives the pulley to change the position of the obstacle. The ultrasonic ranger detects the distance between the obstacle and itself to adjust the frequency of tapping in accordance.

Overall Logic:

Rotational Position (Potentiometer) –> Stepper Motor Rotation –> Pulley System Movement –> Obstacle Movement –> Distance Detection by Ultrasonic Ranger –> Frequency of Tapping (Solenoid)

Overall Photos

Serena’s Overall

Bella’s Overall

Detailed Photos

Stepper Motor

Stepper Motor Pulley System

Stepper Motor Driver Closeup

Solenoid Driver Circuit

Stepper Motor (left) and Solenoid (right) Driver Circuits

Movies

Serena’s video:

 

Bella’s video:

 

Progress Images

Figuring out the solenoid voltage problem

Figuring out how to use a solenoid

Initial prototype for pulley system with stepper motor and potentiometer

Added LCD display and solenoid to the mix after switching to a stepper motor for a more hefty rotational force

Discussion

The project taught us that implementing a fun idea could present so many challenges, ones that we anticipate and ones that come out of nowhere. For this project, some parts we were prepared to tackle with patience from the beginning, such as the pulley system controlled by the stepper motor. We spent a long time brainstorming on how to use a stepper motor, how to make the pulley work, how to move the obstacle attached to the pulley, etc. But the real frustration came from the parts that we thought would be as easy as a breeze, such as a solenoid. The solenoid seemed to be simpler than most of the things we’ve seen in class. However, it was unexpected that adding the solenoid would cause a disruption for the voltage for the entire project. The solenoid causes the 5V to drop to around 3V whenever it is activated, causing the LCD display to dim and the stepper motor to shake. After an hour of tinkering in the OH on the night before the due, we still could not find the problem. Since we left the solenoid to be the last to do, it was too late to find an alternative to solve the issue. This experience shows that every part of the project should be tested out in the beginning stage, even the ones that appear to be easy to implement.

This project also showed the importance and efficiency of the “divide and conquer” strategy. During the early stages, we experimented with different sections of the project. While one of us attempts to figure out how to use a stepper motor, the other one would fiddle with the LCD display. Being responsible for different tasks and piecing them together in the end allowed us to work efficiently and effectively within a limited time.

Functional Block Diagram

Schematic Diagram

Code

/*
 * Double Transducer: Rotational Position -> Frequency of Tapping
 * 
 * Team Members: Bella Liu, Serena Ying
 * 
 * Description:
 *   The program is designed to receive input from the potentiometer and convert
 *   the value to appropriate rotatoin for the stepper motor in order to control
 *   the pulley and therefore move the obstacle. The ultrasonic ranger then detects 
 *   the distance between itself and the obstacle, and converts it to a frequency for
 *   the solenoid tapping accordingly.
 *  
 * Pin Mapping Table:
 * 
 *   Arduino pin | description
 *   ------------|-------------
 *   A0            Potentiometer
 *   
 *   11            Echo pin, ultrasonic ranger
 *   12            Trigger pin, ultrasonic ranger
 *   
 *    2            Step pin, A4988 driver chip for stepper motor
 *    3            Direction pin, A4988 driver chip for stepper motor
 *    
 *   SDA           SDA pin, LCD display
 *   SCL           SCL pin, LCD display
 *
 *
 * References: 
 *  https://courses.ideate.cmu.edu/60-223/f2021/tutorials/ultrasonic-ranger
 *  https://courses.ideate.cmu.edu/60-223/f2021/tutorials/stepper
 *  https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd
 */

// Libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
#include <NewPing.h>

//Pin for potentiometer
const int POTEN = A0;

//Pins for ultrasonic ranger
const int TRIGGER_PIN = 12;
const int ECHO_PIN = 11;
const int MAX_DISTANCE = 200;

//Pins for A4988 diver chip
const int STEP_PIN = 2; // A4988 "STEP" pin wired to Arduino pin 2 (you can change this)
const int DIR_PIN = 3; // A4988 "DIRECTION" pin wired to Arduino pin 3 (you can change this)

//Variables for LCD display
unsigned long LCDtimer = 0;
const int LCDwait = 500;
bool LCDstate = LOW;

//Pin and variables for solenoid
const int SOLENOID = 5;//This is the output pin on the Arduino
unsigned long SOLEtimer = 0;
int SOLEwait = 0;
bool SOLEstate = LOW;

//Setups
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 
LiquidCrystal_I2C screen(0x27, 16, 2);
AccelStepper motor(1, STEP_PIN, DIR_PIN);

void setup() {

  //Pin setup
  pinMode(POTEN, INPUT);
  pinMode(SOLENOID, OUTPUT);

  Serial.begin(9600);
  Serial.begin(115200);

  //LCD screen initialization
  screen.init();
  screen.backlight();

  // set cursor to home position, i.e. the upper left corner
  screen.home();

  //Stepper motor initialization
  motor.setMaxSpeed(6000); // measured in steps per second
  motor.setAcceleration(8000); // measured in steps per second squared

}

void loop() {

  //Variables to store values for potentiometer and ultrasonic ranger
  int potVal = analogRead(A0);
  int ranger = sonar.ping_cm();

  //Printing potentiometer and ultrasonic ranger values in the monitor for debugging
  Serial.println(potVal);
  Serial.print("ping:");
  Serial.print(sonar.ping_cm());
  Serial.println("cm");

  //Adjust the stepper motor rotation according to the rotational degree
  int motorPos = map(potVal, 0, 1023, 0, 400);
  motor.moveTo(motorPos);
  motor.run();  

  //Adjust the frequency of tapping based on the distance detected by the ranger
  SOLEwait = map(ranger, 19, 0, 800, 300);
  if ( (millis() - SOLEtimer) >= SOLEwait ) { 
    SOLEstate = !SOLEstate; 
    SOLEtimer = millis(); 
  }
  digitalWrite(SOLENOID, SOLEstate);

  //display on the LCD
  if ( (millis() - LCDtimer) >= LCDwait ) { // millis() is always the number of milliseconds since the Arduino powered up
    LCDstate = !LCDstate; // this will "toggle" the value of "quarterStateLight" to its opposite
    LCDtimer = millis(); // reset the timer before exiting the function.
  }

  //display values for the LCD
  int input = map(potVal, 0, 1023, 0, 99);
  int mid = map(motorPos, 0, 400, 0, 99);
  int mid2 = map(ranger, 20, 0, 0, 99);
  int output = map(SOLEwait, 300, 100, 0, 99);  
  
  if (LCDstate == HIGH) {
      screen.clear();
      screen.print("i:");
      screen.setCursor(6,0);
      screen.print("m:");
      screen.setCursor(6,1);
      screen.print("m:");
      screen.setCursor(12,1);
      screen.print("o:");
      screen.setCursor(2, 0);
      screen.print(input);
      screen.setCursor(8, 0);
      screen.print(mid);
      screen.setCursor(8, 1);
      screen.print(mid);
      screen.setCursor(14,1);
      screen.print(output);
  } 

}

 

]]>
Double Transducer: Color to Rotational Position https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-color-to-rotational-position/ Tue, 05 Oct 2021 05:54:56 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13995

Color to Rotational Position

Our double transducer took color as an input, transforming it into rotational position.

The final double transducer, copy 1.

The second copy of the double transducer we made.

The first part is the color sensor (at the top of the images), which reads what color is put in front of it. After turning it on, we make sure the color sensor is at the correct position, so we have to put a color in front of it and press the button, which makes 1/10th of a full rotation, until the color sensor is pointing to the correct location on the color wheel. When a different color is put in front of the color sensor, the color sensor reads the color and spins the motor to point to the color we are reading. The position of the motor puts pressure on a different sensor. Depending on what location the second sensor is being pressed, the final motor turns a certain amount.

Detail Shots

Detail shot: the small pillars we added in order to stabilize the potentiometer input.

Detail shot: the arm attached to the stepper motor which puts pressure on the potentiometer.

Detail shot: the soldered board.

Detail shot: The second color sensor, with a busted LED that we had to replace.

Progress Images

potentiometer test 1

Our day 1 design for the potentiometer. Unstable, ultimately redone.

First attempt at getting the stepper motor to work

First working color sensor to stepper motor rotation to potentiometer read

Finished the soldering on the first board, and verified that the motor works.

Discussion

The first day we spent the entire two hours of class trying to work with the force sensing circular potentiometer, trying to get a consistent reading on it. We fiddled with it for ages, trying to find something that could move and apply enough pressure to the potentiometer for it to consistently register. Our first attempt at this was to add weights to the pointer part of the arm that would apply pressure. However, by the end of it, it still wasn’t working, and we had to consider our options. We came up with a few: first, we could continue working with the potentiometer, with our next steps being that we would move it to a more stable surface; and two, we could create what was basically our own potentiometer by moving a stepper motor with resistors surrounding it and measuring the change in voltage. However we did end up making the circular potentiometer work by creating a more stable surface, and adding support beams and weights to the arm. This was definitely the hardest part of the project.

Something that initially seemed fairly daunting, was soldering. Neither of us had much experience with soldering in the past, and the number of pins that needed to be soldered to run the stepper motor was very scary. However, after the first 10-15 pins, the we picked up a feel for how to work the soldering iron and make pretty solders. The second board soldering process went by much faster, and overall would say that soldering feels fairly easy now especially considering where we started.

One decision that would have changed everything about what we learned and how cool our project ended up being, is using the easy middle step that we had initially discussed. One of our three initial ideas was to use the brightness of a bulb to change a photoresistor resistance and measure that change. We would have had a much easier soldering step, which means we would likely not have been as comfortable as we are now with it, and it also would have removed the entire centerpiece of our final design, which was the color wheel to stepper motor interaction that lets us display color.

Functional Block Diagram

 

 

Schematic

Video

A video demonstration of the color wheel working. There is a slight delay between the color input and the movement of the motors; this is due to the calculations made to get the raw data from the sensors.

Code

/*
Double Transducer: Color to Rotational Position

1. Reads values from the color sensor, 
2. Converts it into a degree value based on the color sensed from the color sensor
3. Moves stepper motor stepwise until it reaches the number of degrees we got from the color sensor
4. Reads potentiometer reading 
5. Translate potentiometer reading into an amount to move the final servo motor

Credit: https://www.py4u.net/discuss/63122 for the code to convert rgb values to hsv values
*/
#include <AccelStepper.h>
#include <Servo.h>
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <LiquidCrystal_I2C.h>


const int STEP_PIN = 12; // A4988 "STEP" pin wired to Arduino pin 2 for stepper 
const int DIR_PIN = 13; // A4988 "DIRECTION" pin wired to Arduino pin 3 for stepper motor
const int buttonPin = 2; // pin for button press
const int servoPin = 3;  // pin for controlling servo motor
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X);
LiquidCrystal_I2C lcd(0x27, 16, 2); // initializes 

typedef struct {
    double h;       // angle in degrees
    double s;       // a fraction between 0 and 1
    double v;       // a fraction between 0 and 1
} hsv;
 
static hsv   rgb2hsv(float r, float g, float b);

// converts rgb values to hsv values
hsv rgb2hsv(float r, float g, float b) 
{
    hsv         out;
    double      min, max, delta;
 
    min = r < g ? r : g;
    min = min  < b ? min  : b;
 
    max = r > g ? r : g;
    max = max  > b ? max  : b;
 
    out.v = max;                                // v
    delta = max - min;
    if (delta < 0.00001)
    {
        out.s = 0;
        out.h = 0; // undefined, maybe nan?
        return out;
    }
    if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
        out.s = (delta / max);                  // s
    } else {
        // if max is 0, then r = g = b = 0              
        // s = 0, h is undefined
        out.s = 0.0;
        out.h = NAN;                            // its now undefined
        return out;
    }
    if( r >= max )                           // > is bogus, just keeps compilor happy
        out.h = ( g - b ) / delta;        // between yellow & magenta
    else
    if( g >= max )
        out.h = 2.0 + ( b - r ) / delta;  // between cyan & yellow
    else
        out.h = 4.0 + ( r - g ) / delta;  // between magenta & cyan
 
    out.h *= 60.0;                              // degrees
 
    if( out.h < 0.0 )
        out.h += 360.0;
 
    return out;
}

Servo serv;

// make an AccelStepper motor object. 
AccelStepper myMotor(1, STEP_PIN, DIR_PIN);

long long count = 0;
long total = 0;
long sampleSize = 0;

int calibPos = 0;
int pos = 0;
long long setPosTimer = 0;
long long buttonTimer = 0;

long long 
int degree = 0;
char color[20];


void setup(){
  Serial.begin(9600); // begin Serial communication
  pinMode(buttonPin, INPUT);
  //checks color sensor is working
  Serial.println("setupr");
  if (tcs.begin()) {
    Serial.println("Found sensor");
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  } 

  // starts the display
  lcd.begin();
  lcd.backlight();
  lcd.print("Hi!");
  
  // you can change the below values as you'd like
  myMotor.setMaxSpeed(150); // measured in steps per second
  myMotor.setAcceleration(50); // measured in steps per second squared
  serv.attach(servoPin);

  setPosTimer = millis();
  myMotor.moveTo(0);

  while(myMotor.distanceToGo() != 0) {
    myMotor.run();
  }
}

void loop(){
  // calculates color input
  if (millis() - setPosTimer >= 5000 && millis() >= 10000) {
    uint16_t r, g, b, c;
    tcs.getRawData(&r, &g, &b, &c);
    float red, green, blue;
    tcs.getRGB(&red, &green, &blue);


    red = red / 255;
    green = green / 255; 
    blue = blue / 255;
    
    double h = rgb2hsv(red, green, blue).h;
    int hue = int(h);
    Serial.println(hue);

    if (hue >= 0 && hue < 15) {
      strncpy(color, "red", 3);
    } else if (hue >= 15 && hue < 45){
      strncpy(color, "orange", 6);
    } else if (hue >= 45 && hue < 75){
      strncpy(color, "yellow", 6);
    } else if (hue >= 75 && hue < 150){
      strncpy(color, "green", 5);
    } else if (hue >= 150 && hue < 200){
      strncpy(color, "cyan", 4);
    } else if (hue >= 200 && hue < 255){
      strncpy(color, "blue", 4);
    } else if (hue >= 255 && hue < 285){
      strncpy(color, "purple", 6);
    } else if (hue >= 285 && hue < 315){
      strncpy(color, "pink", 4);
    } else if (hue >= 315 && hue < 360){
      strncpy(color, "red", 3);
    } else {
      strncpy(color, "N/A", 3);
    }
    lcd.home();
    lcd.print("Color: ");
    lcd.print(color);
    strncpy(color, "          ", 10);

    // moves stepper motor
    int stepperTarget = map(hue, 0, 360, 0, 200) + calibPos;
    Serial.print("Current pos and Target pos: ");
    Serial.print(pos);
    Serial.print(", ");
    Serial.println(stepperTarget);
    myMotor.moveTo(stepperTarget);
    pos = stepperTarget;
    setPosTimer = millis();
    lcd.setCursor(0, 1);
    lcd.print("moving to: ");
    lcd.print(stepperTarget);

  }
  
  // Use button to calibrate without changing pos
  if (digitalRead(buttonPin) == HIGH && millis() - buttonTimer >= 1000){
    Serial.println("Press");
    calibPos += 20;
    myMotor.moveTo(pos);
    buttonTimer = millis();
  }

  // moves servo motor
  if ( count % 100 == 0 ) {
    int servPos = map(total/100, 0, 1023, 10, 90);
    Serial.println(servPos);
    serv.write(servPos);
    total = 0;
  }
  total += analogRead(A0);
  count++;
  myMotor.run();

}

 

]]>
Double Transducer: Frequency of Tapping to Distance to Beep Pitch https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-frequency-of-tapping-to-distance-to-beep-pitch/ Tue, 05 Oct 2021 04:45:54 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13935 A view of the arrangement of all parts positioned on the 12" by 12" board with the switch input on the left and the speaker on the right.

A view of the arrangement of all parts positioned on the 12″ by 12″ board with the switch input on the left and the speaker on the right.

 

A detailed view of the belt pulley system with the tape board for distance, a stepper motor and corresponding post, and the infrared proximity sensor on top of the block.

A detailed view of the belt pulley system with the tape board for distance, a stepper motor and corresponding post, and the infrared proximity sensor on top of the block.

 

A view of the speaker used.

 

A view of the soldering connections made with the A4988 microstepping motor driver.

 

Description

A button is on the left edge of the board. In the center, there is a motor, a rubber band with a piece of paper attached to it, and a sensor. On the right edge of the board, there is a speaker constantly playing a sound. The faster the button gets pressed, the more the motor moves the piece of paper on the rubber band toward the sensor. When the paper gets closer to the sensor, the speaker plays a higher-pitched sound.

Process

At this stage, we were considering using a smaller stepper motor and attaching a 3D printed part to the shaft. However, the code we ran only worked on the bigger motor and the 3D print was difficult to attach, so we decided to go with the larger motor and find parts that are already in the shop.

 

At this stage in the process, the proximity sensor was working and the lever switch was registering the amount of presses in each minute. While each part worked on its own, the parts did not communicate with one another and the refresh rate of the taps was not fast enough to be effective.

 

After the stepper motor was in place, it was a bit of a challenge to figure out how to get the mechanism to run smoothly and reliably. Emily had made wooden posts to hold the opposite side, but it took a lot of experimenting with various ball bearings and posts to rotate smoothly while maintaining proper tension. A thin rubber band was also key for not being so strong that it moves the motor or the post.

 

At this time we were considering changing our design to a change in angle rather than distance as the professor recommended since it would not rely on the mechanics as much. While the deadline was approaching and we still hadn’t actually tested the rubber band belt, thankfully we got the system to work and we decided to go through with our original plan.

 

Discussion

Although it was frustrating, irksome, and disappointing at times, we’re genuinely grateful for the experience as a whole. When we started, we thought it would be easy to execute our vision since we knew exactly what we wanted. We had everything planned out, so assembling the project should have been fairly straightforward; they weren’t though. Due to our lack of knowledge, experience, and skill, we ran into a lot of unforeseen problems that stumped us for hours at times! An example is when David spent hours revising his code and looking over his wiring to get the stepper motor to work only to have the professor point out that the issue was with the external power supply. There are plenty of examples of simple things holding our progress back, but to name them all would require a book. It’s because of these experiences, however, that we can look back and be thankful; now all of our sorrows become our teachers. We won’t forget semicolons or mix up the wiring for infrared sensors in the future because we’ve already dealt with those mistakes in the past. I think that the lessons we learned will enable us to do a lot more in the upcoming projects. Since our foundation is stronger, we’ll have more creative liberty that will enable us to experiment with things that require more advanced techniques. Overall, the project was a profound learning experience.

One thing we’d do differently in the future is allocate more time toward physically assembling our project. We spent a lot of time getting our individual parts to work (partly because we had to learn how to use them), so we didn’t have much time for integrating everything together. This led to assembly feeling rushed, and some parts looking a bit messy. For example, the paper that is attached to the belt will often fall off if it isn’t handled delicately. If we had made more time to experiment with different mediums and structures, we could have found out what would have worked best for our project, and the assembly process would have been a lot smoother.

Overall, we’re happy with what we’ve accomplished. There are no huge design changes we would make because we got to develop an idea we had from the start and see it through to the end. We’re satisfied because we chose to create something that posed a challenge, but it wasn’t so unfeasible that we had to revert to something less complicated. We executed our idea and made things work, and that feels good.

Block Diagram and Schematic

Code

/*
 *  A double transducer: rate of tapping to position to pitch of beeping
 * 
 *  Description: This code measures rate of tapping by evaluating the 
 *  length of time between taps and translates it to a position value 
 *  on a  stepper motor. As the stepper motor moves an index card, an 
 *  infrared proximity sensor measures the distance of the card and 
 *  sends it back to the Arduino where that value is then mapped to 
 *  a value of pitch sent to a speaker.
 * 
 * Inputs: Lever switch, Infrared proximity sensor 
 *  Arduino pin |  input
 *  8              Lever switch
 *  A0             IR Proximity Sensor
 *  
 *  Outputs: Stepper motor direction, stepper motor steps, speaker
 *  Arduino pin |  output
 *  3              Stepper Motor Steps
 *  4              Stepper Motor Direction
 *  10             Speaker
 *  
 */

// Additional help and code adapted from:
// https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd
// https://courses.ideate.cmu.edu/60-223/f2021/tutorials/IR-proximity-sensor
// https://courses.ideate.cmu.edu/60-223/f2021/tutorials/button-and-switch
// https://courses.ideate.cmu.edu/60-223/f2021/tutorials/stepper

//INITIALIZATION
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);

//Pin names
const int CLICKERPIN = 8; //Pin used to record clicker input from tapping
const int STEP_PIN = 3; //Pin used to tell stepper motor how much to move
const int DIR_PIN = 4; //Pin used to tell stepper motor the direction
const int SPEAKER_PIN = 10; //Pin used to tell speaker the pitch
const unsigned long IRPIN = A0; // shortcut to refer to the ir proximeter pin later
unsigned long tap1 = 0; //Records time button was pressed
unsigned long tap2 = 0; //Records last time button was pressed

#include <AccelStepper.h>
AccelStepper myMotor(1, STEP_PIN, DIR_PIN);
int motorPos = 0;

int tapRate = 20; //How many taps per minute
unsigned long timerOne = 0; //Timer for screen refresh rate
int buttonState = 0;

//VOID SETUP
void setup() {
  //Data setup
  Serial.begin(9600);

  //Screen setup
  screen.init();
  screen.backlight();
  myMotor.setMaxSpeed(1000);
  myMotor.setAcceleration(500);

  //Pin Inputs/Outputs
  pinMode(IRPIN, INPUT); // we will be reading the photoresistor pin as an input
  pinMode(CLICKERPIN, INPUT);
  pinMode(SPEAKER_PIN, OUTPUT);
}

//VOID LOOP
void loop() {
  int readVal; // initialize a new integer to store the IR value
  readVal = analogRead(IRPIN); // do the analog read and store the value
  int pitchVal; // initialize a new integer to store the IR value
  pitchVal = map(readVal, 15, 60, 200, 2000); //Mapping the IR distance values to pitch values
  tone (SPEAKER_PIN, pitchVal); //Telling speaker to play a tone at pitch pitchVal

  //Beginning conditional for when neither tap has a value
  if ((digitalRead(CLICKERPIN) == 1) and (tap1 == 0)) {
    tap1 = millis();
    tapRate = (60 / (tap1 * .001));
    if (tap1 <= 1000) {
      tapRate = 60; //Covers boundary conditions, no higher than 60
    }
    if (tap1 >= 3000) {
      tapRate = 20; //Covers boundary conditions, no lower than 20
    }
  }
  
  if ((digitalRead(CLICKERPIN) == 1) and ((millis() - tap1) > 1000)) {
    tap2 = millis();
    Serial.println((String)"tap1:" + (tap1 * .001) + "s");
    Serial.println((String)"tap2:" + (tap2 * .001) + "s");
    tapRate = (60 / ((tap2 - tap1) * .001));
    if ((tap2 - tap1) <= 1000) {
      tapRate = 60; //Covers boundary conditions, no higher than 60
    }
    if ((tap2 - tap1) >= 3000) {
      tapRate = 20; //Covers boundary conditions, no lower than 20
    }
    motorPos = map(tapRate, 20, 60, 0, 200); //Mapping the tapRate values to position values
    myMotor.moveTo(motorPos);
    Serial.println(motorPos);
    tap1 = tap2;
  }
  myMotor.run();

  if (millis() - timerOne >= 300) { //Refresh rate of screen every 300 milliseconds
    // print the input value onto the screen starting at the above cursor position
    screen.clear();
    screen.home();
    screen.print("i:");
    screen.print(tapRate);
    screen.setCursor(5, 0);
    screen.print("m:");
    screen.print(motorPos);
    screen.setCursor(7, 1);
    screen.print(readVal);
    timerOne = millis();
    screen.setCursor(10, 1);
    screen.print("o:");
    screen.print(pitchVal);
  }
}

 

]]>
Double Transducer: Light Brightness to Rotational Speed https://courses.ideate.cmu.edu/60-223/f2021/work/double-transducer-light-brightness-to-rotational-speed/ Tue, 05 Oct 2021 03:40:14 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=13982 Images & Videos

Close-up image of the three-axis accelerometer attached to the servo motor.

Close-up image of photoresistor and voltage divider circuit.

Close-up image of power rail.

Close-up image of our output, a continuous servo motor that rotates continuously with the speed of around 20 – 60 rpm.

Description

The photoresistor reads light brightness and sends the information to the servo motor which rotates in both directions. The motor turns based on the brightness of light and tilts the three-axis accelerometer that is attached to the servo motor. The accelerometer measures the acceleration in the axes X, Y, and Z and sends information to the continuous motor that rotates continuously in one direction. Based on the tilt of the three-axis accelerometer, the speed of the continuous servo motor changes. 

Progress Images

I made this wooden holder to keep the accelerometer in place. We determined that the long stick part of the holder was unnecessary so we decided to cut that part off.

The first attempt at making something that would hold the accelerometer was a pipe cleaner.

We also tried using a bit of styrofoam to hold the accelerometer.

 

I soldered wires to connect the three-axis accelerometer to the protoboard. I used solid wires at first however it was too stiff causing the movement of the accelerometer to be inflexible as shown above in the image. I later soldered them again using stranded wires.

 

Discussion

Through this project we learned a lot especially from the challenges we faced and the process of problem solving. One of the challenges we faced was reading the changes in tilt of the three-axis accelerometer. We were reading the acceleration values of all three axes, X, Y, and Z, through the serial monitor as we were tilting the accelerometer, however the changes of the values were so subtle it was difficult to tell which axis we were dealing with. We took the norm of all three values to get one total accelerometer reading using this equation: finalReading = sqrt(X^2 + Y^2 + Z^2). This reading has much more variance than each individual sensor reading.

Another challenge that I faced had to do with soldering. I decided to solder wires connecting the accelerometer to the protoboard and I used solid wires which are pretty stiff making the movement of the accelerometer inflexible. I had to solder them again, but this time I used stranded wires, which are a lot more flexible, easing the movement of the accelerometer.

Through this project, we also learned about a type of motor that we used as our output. Our original plan was to use a stepper motor to produce a continuous rotation however we struggled to figure that out because it required a transistor. However, we came up with an alternative solution. We were introduced to a different type of motor that we could use as our output instead of the stepper motor, which is a continuous servo motor. Unlike a hobby servo motor which rotates only up to 180 degrees, the continuous servo motor rotates 360 degrees continuously, allowing us to produce our output of a continuous rotation of around 20 to 60 rpm. Learning about this new part allowed us to create our output effectively and efficiently. 

 

Functional Block Diagram 

Schematic

Code

/*
 * Double Transducer: Light Brightness to Rotational Speed
 * 
 * Team: Ronald Gonzalez and Sohye Park
 * 
 * Description:
 *   The code reads the light input from the photoresistor
 *   going to pin A0 and then sends the signal generated
 *   to the half rotation servo on pin 3 which will rotate
 *   an accelerometer which sends all 3 of it's signals to
 *   pins A1-3 then the norm of those 3 values is taken and 
 *   that is sent to the full rotation servo whose speed
 *   will change based on the signal sent.
 * 
 *     Arduino Pin | Description
 *     ------------|---------------------
 *     A0          | PhotoResistor       
 *     A1          | Accelerometer x       
 *     A2          | Accelerometer y       
 *     A3          | Accelerometer z       
 *     3           | Half Rotation Servo     
 *     5           | Full Rotation Servo
 *   
 * Code for Servo from Michael Margolls
 * Code for LED screen from Frank de Brabander
 */

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

// Pins
#define PHOTORES A0
#define ACCELX A1
#define ACCELY A2
#define ACCELZ A3
#define HALFROTSERVO 3
#define FULLROTSERVO 5

// Servos and Screen
Servo gaugeMotor1;
Servo gaugeMotor2;
LiquidCrystal_I2C screen(0x27, 16, 2);

// Timer
unsigned long lastPrint = 0;

void setup() {
 
  // Pin setup
  pinMode(PHOTORES, INPUT);
  pinMode(ACCELX, INPUT);
  pinMode(ACCELY, INPUT);
  pinMode(ACCELZ, INPUT);

  // Motor setup
  gaugeMotor1.attach(HALFROTSERVO);
  gaugeMotor2.attach(FULLROTSERVO);

  // Screen setup
  screen.init();
  screen.backlight();
  screen.home();
}

void loop() {

  // Analog reads from photoresistor and accelerometer
  int light = analogRead(A0);
  long accelx = analogRead(A1);
  long accely = analogRead(A2);
  long accelz = analogRead(A3);
  
  // Taking the norm of all three accelerometer outputs
  long accelFinal = long(sqrt(((accelx * accelx) + (accely * accely) + (accelz * accelz))));
  
  // Mapping all values to appropriate ranges
  int angle = map(light, 50, 1050, 10, 180);
  int rpm = map(accelFinal, 500, 630, 93, 105);
  int sLight = map(light, 50, 1050, 0, 99);
  int sAngle = map(angle, 10, 180, 0, 99);
  int sRPM = map(rpm, 93, 105, 0, 99);
  int sAccel = map(accelFinal, 500, 620, 0, 99);
  
  // Sending correct angle and rpm to the two servo motors
  gaugeMotor1.write(angle);
  gaugeMotor2.write(rpm);

  // Delay so both motors aren't being updated at extremely 
  // fast rates
  delay(50);

  //Updates screen every 250 milliseconds
  if (millis() - lastPrint >= 250) {
    screen.clear();
    screen.setCursor(0, 0);
    screen.print("i:");
    screen.setCursor(6, 0);
    screen.print("m:");
    screen.setCursor(12, 1);
    screen.print("o:");
    screen.setCursor(2, 0);
    
    if (sLight < 10){
      screen.print("0");
    }
    screen.print(sLight);
    screen.setCursor(8, 0);
    if (sAngle < 10){
      screen.print("0");
    }
    screen.print(sAngle);
    screen.setCursor(8, 1);
    if (sAccel < 10){
      screen.print("0");
    }
    screen.print(sAccel);
    screen.setCursor(14, 1);
    if (sRPM < 10){
      screen.print("0");
    }
    screen.print(sRPM);
    lastPrint = millis();
  }
}

 

]]>