yuhini@andrew.cmu.edu – Intro to Physical Computing: Student Work Spring 2023 https://courses.ideate.cmu.edu/60-223/s2023/work Intro to Physical Computing: Student Work Wed, 15 Mar 2023 01:27:04 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.6 Ulnar Entrapment Elbow Sleeve https://courses.ideate.cmu.edu/60-223/s2023/work/ulnar-entrapment-elbow-sleeve/ Sat, 11 Mar 2023 03:51:59 +0000 https://courses.ideate.cmu.edu/60-223/s2023/work/?p=17684 An elbow sleeve that vibrates when your elbow is bent past 90 degrees for too long, and when you put excessive pressure on your elbow joint.

 

Fits around halfway up your upper arm, and covers the elbow. Pancake vibration motors are placed over the bicep.

 

Fabric is elastic and bends with the elbow without restriction

 

Flex sensor and pressure sensors located under the elbow joint.

 

Arduino Pro Micro powered by a 9V battery. All connections are soldered.

 

Closeup of electronics and shoddy hot-gluing and sowing.

 

Process

I started by picking out some components and wiring them on a breadboard to get an idea of what I’ll be using and how they work, as well as the code needed to run it.

First attempt getting a pancake vibration motor to respond to a flex sensor and pressure sensor.

 

 

After getting an idea of the components I’ll be using, I taped the flex sensor underneath my elbow as a proof-of-concept prototype. It worked surprisingly well! Next up is trying to fit it all on an elbow sleeve.

 

First attempt at fitting a pancake vibration motor, flex sensor, battery and Arduino Pro Micro on an elbow sleeve.

 

Everything works as expected – the motor vibrates when the elbow is bent past 90 degrees – but its still lacking the pressure sensor. Also, the wiring is very un-robust. The Arduino Pro Micro and pancake vibration motors are connected directly to the 9V battery. From here I worked on the hardware to make sure its safe and reliable.

 

Wiring the battery to a latch switch and voltage regulator.

I used silicon cables to wire up the flex sensor as to not hinder any movement of the joints.

 

Pancake vibration motors and pressure sensors wired up. Pancake vibration motors connected to an N-channel MOSFET.

 

With the hardware working, I started attaching the hardware to the elbow sleeve with Velcro, shoddy sowing and hot-glue. Since the flex sensor itself can’t stretch and compress, it was most difficult trying to fix the flex sensor to the sleeve in a way that allows it to travel when the elbow is bent, but not so fixed that the sensor flexes outwards when the arm is straightened.

 

The flex sensor bends outwards when the arm is straightened, if there isn’t any tension pulling the strip upwards or downwards.

 

Closeup of everything secured to the elbow sleeve.

 

First-person view of elbow sleeve with components attached.

 

Discussion

Looking at the responses from my classmate, it’s clear that my solution would work better if it looked better – and I agree. That would turn the solution from something that works to something someone would actually want to use everyday, as one of my classmates put it. In its current state, all the electronics are exposed and haphazardly attached to the sleeve, which renders the whole thing useless as soon as it gets cold and someone has to put on a jacket, or when it just rains a little. The reason why my final product looks this way is because of timeframe and my inexperience with fabrics. Early on in the project, I was very interested in the types of fabrics I could use, especially ones that were used in existing compression elbow sleeves. They seemed to have a good balance of stiffness and elasticity, such that I could reasonably create a pouch to house the electronics. It was also mentioned that to improve the solution, there should be some indication as to how the sleeve should be worn, which was a very valuable piece of critique I hadn’t addressed. For the flex sensor and pressure sensors to work accurately, the flex sensor has to be centered over the elbow joint, but there aren’t any affordances or even instructions that lead to this consideration. I can think of some ways to implement this off the top of my head – like cutting a hole at the ‘hinge’ of the elbow sleeve so that there’s a more apparent top-side and bottom-side, or using an accelerometer and the pancake vibration motors to help the user position the arm sleeve correctly? I’m not sure what would work to be honest.

All-in-all, I’m still quite happy with how the final product turned out, because it works reliably and consistently (if worn right of course). It’s wearable, it detects when you’ve bent your elbow past 90 degrees for too long, it detects if you’re putting too much pressure on your elbow, and it tells you to stop. It was most fulfilling being able to create something that responds directly to the biomechanics of the human body on a wearable device, which isn’t just pushing buttons and flipping switches. Part of the reason why I think something like this should exist in the world is because elbow braces designed to prevent ulnar entrapment are intentionally stiff, to make it physically harder for you to bend your elbow for prolonged periods of time – but I wanted there to be a sleeve that still allowed freedom of movement, and I think my final product achieved that goal to a degree. I was able to practice some simple soldering and learn about building robust circuits. Though building the circuit was very simple on the breadboard, I spent a lot of time planning out my soldered breadboard, working around including components to improve the robustness of the circuit, and the placement of wires and resistors so I can see what I’m doing myself. I think I want to move towards building circuits that are less stuck to breadboards now, especially if I’m going to work on more wearables.

If I were to build another iteration, I think I would focus all my energy on fabrication to house the electronics. This would also mean making the hardware more low-profile and resistant to deformation – to bring the product to something more close to a ‘living’ piece of fabric? And less of hardware tacked onto fabric. I think this would mean using stiffer and thicker fabrics and foams, and soldering closer to the breadboard so that the overall height of the hardware can be brought down. As for the battery, I’m not sure what I’d do without the 9V battery. It just makes sense to charge and replace the battery. Or I guess coin batteries wired in series could work too.

 

Block Diagram

Circuit Diagram

Code

const int flexPin = A0;
const float VCC = 5;
const float R_DIV = 10000.0;
const float flatResistance = 10000.0;
const float bendResistance = 30000.0;

const int vibrPin = 3;
const int pressurePin = A2;

int bentTimer = 0;

// flex sensor code from: https://www.instructables.com/How-to-use-a-Flex-Sensor-Arduino-Tutorial/

void setup(){
  Serial.begin(9600);
  pinMode(flexPin, INPUT);
  pinMode(vibrPin, OUTPUT);
  pinMode(pressurePin, INPUT);

// turn on sequence

  digitalWrite(vibrPin, HIGH);
  delay(300);
  digitalWrite(vibrPin, LOW);
  delay(300);
  digitalWrite(vibrPin, HIGH);
  delay(300);
  digitalWrite(vibrPin, LOW);
  delay(300);
  digitalWrite(vibrPin, HIGH);
  delay(300);
  digitalWrite(vibrPin, LOW);

}

void loop(){
  int flexNum = analogRead(flexPin);
  int pressureNum = analogRead(pressurePin);

  float Vflex = flexNum * VCC / 1023.0;
  float Rflex = R_DIV * (VCC / Vflex - 1.0);
  Serial.println("Reistance: " + String(Rflex) + " ohms");
  
  float angle = map(Rflex, flatResistance, bendResistance, 0, 180.0);
  Serial.println("Bend: " + String(angle) + " degrees");
  Serial.println("Pressure: " + String(pressureNum) + " units");
  Serial.println();

// if arm is bent past 70 degrees, begin counter.

  if (angle >= 70){
    bentTimer += 1;
  } else{
    bentTimer = 0;
  }

// if counter exceeds 10 seconds, or pressure is detected, activate vibration motors. 

  if (bentTimer > 50 || pressureNum > 850){
    digitalWrite(vibrPin, HIGH);
  } else{
    digitalWrite(vibrPin, LOW);
  }

  Serial.println("Timer: " + String(bentTimer) + " seconds");

  delay(200);
}

 

]]>
Double Transducer – Temperature to Fan Speed. https://courses.ideate.cmu.edu/60-223/s2023/work/project-1-double-transducer-temperature-to-fan-speed/ Tue, 14 Feb 2023 22:19:37 +0000 https://courses.ideate.cmu.edu/60-223/s2023/work/?p=17171 Angie Wang, Stanley Ip, Michelle Liu

Top view of Stanley’s Double Transducer. Thermistor (left), fan (right)

Top view of Michelle’s Double Transducer. Fan (left), thermistor (right).

Top view of Angie’s Double Transducer. Thermistor(Middle-left, labeled as “INPUT”), Fan(Top-right, labeled as “OUTPUT”)

Adafruit color sensor secured directly above color wheel, which is spun by a stepper motor.

Another angle of the Adafruit color sensor secured above the color wheel and overview of some of the wiring we did to connect the steps.

Close up of the fan and LED setup.

 

Final Working Model:

Stanley

Michelle

Narrative Description:

The thermistor picks up on the temperature being emitted, and based on how high or low the temperature is, the Arduino Uno will determine how much to rotate a stepper motor. A color wheel is attached to the stepper motor, and an RGB sensor will detect the hue rotated in front of it. This hue then determines the wind speed.

 

Process:

A hand holding the adafruit sensor next to a color wheel, propped up on a stepper motor.

Testing the Adafruit sensor and stepper motor to make sure these middle step components work reliably. – Stanley

 

Getting the color sensor to pick up hue from the color wheel. As the color wheel spins, the hue value is recorded and is represented by the graph on screen. – Stanley

 

Getting the fan to respond to hue. A higher hue value (blues, purples) corresponds to higher fan speed, and lower hue values (red) corresponds to lower fan speed. – Michelle

 

Putting it all together (stepper motor, color wheel, fan) – Michelle

 

Discussion:

From the beginning of the project, we didn’t expect that our direction of going from a temperature input, to color, and fan speed would be technically challenging to execute. In particular, there were a few external things we had to learn, from learning how to use a MOSFET to control fan speed, to using a driver for the stepper motor, etc. We worked through this by taking the time to understand each component individually first – their capabilities, how the hardware itself works, how to speak to it through code, etc. and that helped us get a holistic idea of how we can get each component to communicate with each other.

Needless to say, there was a lot of debugging in our process. With so many new components and systems, it was difficult to pinpoint exactly which part was going wrong – whether it was the software, weak connections, or just a single wire plugged incorrectly. Oftentimes, we would mess with the code and wiring a lot just to find that the breadboard was faulty. Towards the end, we learned to use software workarounds for hardware problems. For example, with the RGB sensor, it was difficult to figure out a way to physically set the stepper motor such that the low temperature corresponds to the red part (low hue value) of the color wheel. Instead, we wrote a function to rotate and calibrate the color wheel, so that a lower temperature always corresponds to red, and a higher temperature always corresponds to purple. 

In the end, we are glad that we followed through with the idea, using these different dimensions (temperature, hue, fan speed) in a novel yet cohesive way, and it was definitely a very productive experience in learning how to work with hardware and software simultaneously and creatively. 

 

Block Diagram & Schematic:

 

 

Code:

/*
* TEMPERATURE > COLOR > FAN SPEED DOUBLE TRANSDUCER
* Michelle Liu, Stanley Ip, Angie Wang
* 
* The thermistor picks up on the temperature being emitted, and based on how high or 
* low the temperature is, the Arduino Uno will determine how much to rotate a stepper motor.
* A color wheel is attached to the stepper motor, and an RGB sensor will detect the hue
* rotated in front of it. This hue then determines the fan speed.
* 
* Pin Map:
variable name | mode | pin | description
-----------------------------------------------
FAN_PIN | output | 10 | controls fan speed
STEP_PIN | output | 2 | stepper motor: sets stepper position
DIR_PIN | output | 3 | stepper motor: sets stepper direction
THERMO_PIN | output | A0 | thermistor: reads in resistance from the thermistor
* 
* Credits:
* RGB sensor code referenced from the Adafruit TCS34725 breakout library example
* code: https://learn.adafruit.com/adafruit-color-sensors/arduino-code
* 
* Algorithm for converting RGB values to hue values referenced from
* https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/
* 
* Stepper motor code referenced from the course website:
* https://courses.ideate.cmu.edu/60-223/s2023/tutorials/stepper
* 
* LCD display code referenced from the course website: 
* https://courses.ideate.cmu.edu/60-223/s2023/tutorials/I2C-lcd
*/


#include <Wire.h>
#include "Adafruit_TCS34725.h" // ADAFruit TCS34725 driver for ADAFruit TCS34725 RGB Color Sensor by Adafruit
#include <AccelStepper.h> // AccelStepper stepper motor driver by Mike McCauley
#include <LiquidCrystal_I2C.h> // LiquidCrystal I2C LCD display driver by Frank de Brabander

LiquidCrystal_I2C screen(0x27, 16, 2);

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X);

// WIRING
const int FAN_PIN = 3;
const int STEP_PIN = 8;
const int DIR_PIN = 9;
const int THERMO_PIN = A0;
const int FAN_START_SPEED = 0;

AccelStepper myMotor(1, STEP_PIN, DIR_PIN);

int pos = 800; // variable to store motor position instruction
int fanSpeed = 0;
int dispDelay = 0;

void setup(void) {
  screen.init();
  Serial.begin(9600);

// LCD DISPLAY SETUP

  screen.backlight();
  screen.home();

// RGB SENSOR SETUP

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

// STEPPER MOTOR SETUP

  myMotor.setMaxSpeed(1000); // measured in steps per second
  myMotor.setAcceleration(500); // measured in steps per second squared

  initializeMotorPos();

  pinMode(FAN_PIN, OUTPUT);
  pinMode(THERMO_PIN, INPUT);
  analogWrite(FAN_PIN, FAN_START_SPEED);

}

// HUE ALGORITHM
// Algorithm from https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/
// Range: 0 (red) - 360 (violet)
double calculateHue(uint16_t r, uint16_t g, uint16_t b) {
  double rDepth = r / 65535.0;
  double gDepth = g / 65535.0;
  double bDepth = b / 65535.0;

  double rgbMax = max(max(gDepth, bDepth), rDepth); // maximum of r, g, b
  double rgbMin = min(min(gDepth, bDepth), rDepth); // minimum of r, g, b

  double rgbDiff = rgbMax - rgbMin; // diff of cmax and cmin.

  if (rgbDiff == 0) {
    return 0.0;
  } else if (rgbMax == rDepth) {
    return fmod(60.0 * ((gDepth - bDepth) / rgbDiff) + 360.0, 360);
  }
  else if (rgbMax == gDepth) {
    return fmod(60 * ((bDepth - rDepth) / rgbDiff) + 120, 360);
  } else {
    return fmod(60 * ((rDepth - gDepth) / rgbDiff) + 240, 360);
  }
}

// Reads RGB sensor data without introducing a delay like the built-in function does
void getRawData_noDelay(uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c)
{
  *c = tcs.read16(TCS34725_CDATAL);
  *r = tcs.read16(TCS34725_RDATAL);
  *g = tcs.read16(TCS34725_GDATAL);
  *b = tcs.read16(TCS34725_BDATAL);
}

// initialize motor pos to approximately red
void initializeMotorPos() {
  uint16_t r, g, b, c, colorTemp, lux;

  getRawData_noDelay(&r, &g, &b, &c);
  int hue = (int)calculateHue(r, g, b);

// rotate the stepper motor by 1 degree until the hue reaches a value close to red
  while (hue > 15) {
    getRawData_noDelay(&r, &g, &b, &c);
    hue = (int)calculateHue(r, g, b);
    myMotor.move(1);
    myMotor.run();
  }
  Serial.print("RED FOUND: ");
  Serial.println(myMotor.currentPosition());
  myMotor.setCurrentPosition(0);
}

// debugging helper function for printing values with their labels
void p(String label, int val) {
  Serial.print(label + ": "); Serial.print(val, DEC); Serial.print(" ");
  Serial.println(" ");
}

void displayVal(String c, int i) {
  screen.print(c);
  screen.print(":");
  screen.print(i < 10 ? "0" : "");
  screen.print(i < 10 ? "0" : "");
  screen.print(i);
}

/*
   i = input, iMax = input upper range
   m = middle-step actuator value, mMax = middle-step actuator value upper range
   s = middle-step sensor value, mMax = middle-step sensor value upper range
   o = output, oMax = middle-step actuator value upper range
*/
// helper function for displaying values on the LCD screen
void displayLCD(int i, int iMax, int m, int mMax, int s, int sMax, int o, int oMax) {
  int iNorm = map(i, 0, iMax, 0, 99);
  int mNorm = map(m, 0, mMax, 0, 99);
  int sNorm = map(s, 0, sMax, 0, 99);
  int oNorm = map(o, 0, oMax, 0, 99);

  displayVal("i", iNorm);
  screen.print("  ");

  displayVal("  m", mNorm);

  screen.setCursor(1, 6);

  displayVal("s", sNorm);
  screen.print("  ");

  displayVal("  o", oNorm);

   screen.print("      ");

  if (dispDelay > 100) {
    dispDelay = 0;
    screen.clear();
  }
}

void loop() {
  int thermoVal = analogRead(THERMO_PIN);

  pos = map(thermoVal, 450, 600, 0, 200);

  myMotor.moveTo(-pos); // and tell the motor to go there
  myMotor.run(); // call this function as often as possible

  uint16_t r, g, b, c, colorTemp, lux;

  getRawData_noDelay(&r, &g, &b, &c);
  int hue = (int)calculateHue(r, g, b);

  fanSpeed = map(hue, 0, 360, FAN_START_SPEED, 255);

  analogWrite(FAN_PIN, fanSpeed);

  displayLCD(thermoVal, 1023, pos, 400, hue, 360, fanSpeed, 255);

  dispDelay += 1;
}

 

 

]]>