Zarmond Goodman's double transducer mounted on chip board.
Hua Tong's double transducer mounted on chip board.
Close up of NeoPixel ring (light color output). (Zarmond Goodman)
Close up of the breadboard making up the double transducer. (Zarmond Goodman)
Close up of NeoPixel ring (light color output). (Hua Tong)
Close up of the soldering part on the photo resistor (middle step). (Hua Tong)
Zarmond's documentation video showing force changing to light color.
Hua's documentation video showing force changing to light color with skipping the middle button.
When the force-sensitive resistor (FSR) senses an input from someone pressing on it, that will cause the hobby servo motor to move proportional to the amount of force being input (more force, closer to 180; less force, closer to 0). The motor moves a transparent sheet with a black gradient printed onto it. Based on the degree that the motor turns, the photo resistor will pick up more or less light because it will be covered by different levels of gradient on the transparent sheet. Lastly, the amount of light detected will affect the hue the NeoPixel ring displays.
Initial prototype of the photo resistor step of the transducer. The issue with this iteration was the wires obstructed the transparent sheet on the servo motor from adequately changing the light conditions.
The "skip-the-middle-button" was initially wired with much shorter wires. This eventually was switched out for some longer leads so that it could be mounted nicely on the chip board.
Initial prototype of the double transducer without middle button and LCD. Glad to get the functional steps working.
The working printed part of the light gradient lid above the photo resistor. We printed it on transparent sheet with a linear gradient vector file.
An accidental artistic documentation image taken due to bad explosure in phone.
The process is fruitful for us. We have learned a lot from the beginning to the end. In the ideation stage, we initially wanted to use sound output and input as our middle step. However, we are suggested that this could be instead a rather unstable middle step. Subsequently, we shifted to a plan that we thought were a little bit outrageous at first: putting the photoresistor underneath a lid that allows linear gradient amount of light to come through to control the light input it receives. To our surprise, this turns out to be a lot easier and also more sensitive in later fabrication. From this experience, we learned that there is a discrepancy between perception and actual practice.
Beside this, the process of conduction is also full of resourceful exploration. For example, we both agree that the most difficult part of it all happened at the last step in the double transducer: when the light input from photo resistor is transformed into hue and then into rgb value for the neoPixel to shine. We have looked up for a lot of codes, a lot of libraries, several blogs, and even a math equation. The solution we found out at last is even in Javascript but not C. But beyond all of that, we still managed to work it out. The joyness from this success is unparalleled. It's a unique happiness from creation, turning 0 to 1 and seeing it working on its own.
We are very glad that we have this opportunity to complete this project, because it's so nice to see the world around us from an engineering perspective of how everything is created. In that sense, we are also very looking forward to develop a further step in our journey of physical computating.
/*
Project 1: Double Transducer
60-223 Intro to Physical Computing
Zarmond and Hua
When the force-sensitive resistor (FSR) senses an input from someone pressing
on it, that will cause the hobby servo motor to move proportional to the amount
of force being input (more force, closer to 180; less force, closer to 0). The
motor moves a transparent sheet with a black gradient printed onto it. Based on
the degree that the motor turns, the photo resistor will pick up more or less
light because it will be covered by different levels of gradient on the
transparent sheet. Lastly, the amount of light detected will affect the hue the
NeoPixel ring displays.
Pin mapping:
Arduino pin | role | description
-------------------------------------
A3 input FSR
A2 output servo motor
A0 input photoresistor
8 output neo pixel ring
11 input button
*/
//library for servo motor
#include <Servo.h>
//library for neopixel ring
#include <PololuLedStrip.h>
//library for LCD
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
//library for mapping RGB to HSV
#include <math.h>
//pin assignments
const int FORCEPIN = A3;
const int SERVOPIN = A2;
const int PHOTOPIN = A0;
const int LEDPIN = 8;
const int LCDSDA = A4;
const int LCDSCL = A5;
const int BUTTONPIN = 11;
// number of LEDs in the neopixel
const int NUMLEDS = 12;
//creates Servo object to control a servo
Servo MOTORCONTROL;
//creates ledStrip object and specify the pin it will use.
PololuLedStrip<LEDPIN> ledStrip;
//creates an array for holding the color values for each LED
rgb_color colors[NUMLEDS];
//creates LCD object
LiquidCrystal_I2C screen(0x27, 16, 2);
//creates a hsl to rgb function
void hslToRgb(float h, uint8_t &r, uint8_t &g, uint8_t &b) {
float c = 1.0f; // chroma = (1 - |2l-1|) * s = 1
float h_ = fmod(h, 360.0f) / 60.0f;
float x = c * (1 - fabs(fmod(h_, 2) - 1));
float r1, g1, b1;
//match the condition selection of the function
if (h_ < 1) {
r1 = c;
g1 = x;
b1 = 0;
} else if (h_ < 2) {
r1 = x;
g1 = c;
b1 = 0;
} else if (h_ < 3) {
r1 = 0;
g1 = c;
b1 = x;
} else if (h_ < 4) {
r1 = 0;
g1 = x;
b1 = c;
} else if (h_ < 5) {
r1 = x;
g1 = 0;
b1 = c;
} else {
r1 = c;
g1 = 0;
b1 = x;
}
//give out final value
r = (uint8_t)round(r1 * 255.0f);
g = (uint8_t)round(g1 * 255.0f);
b = (uint8_t)round(b1 * 255.0f);
}
void setup() {
//FSR setup
pinMode(FORCEPIN, INPUT);
//servo setup
pinMode(SERVOPIN, OUTPUT);
MOTORCONTROL.attach(SERVOPIN);
//photoresistor setup
pinMode(PHOTOPIN, INPUT);
//neopixel LED setup
pinMode(LEDPIN, OUTPUT);
//button setup
pinMode(BUTTONPIN, INPUT_PULLUP);
//serial port to send the data back to the computer (also for LCD use)
Serial.begin(9600);
//LCD setup
// initialize the screen
screen.init();
// turn on the backlight to start
screen.backlight();
}
void loop() {
//skip the middle button code
if (digitalRead(BUTTONPIN) == LOW) {
//reads value from FSR input
int forceVal = analogRead(FORCEPIN);
//maps FSR inputs to hue
int colorVal = map(forceVal, 0, 1023, 0, 360);
int h = colorVal;
uint8_t r8, g8, b8;
hslToRgb(h, r8, g8, b8);
for (int i = 0; i < NUMLEDS; i++) {
colors[i] = rgb_color(r8, g8, b8);
}
ledStrip.write(colors, NUMLEDS);
delay(10);
//LCD code
//sets cursor to top left corner
screen.home();
//maps FSR value to be 0-99
forceVal = map(forceVal, 0, 1023, 0, 99);
//input sensor's value
screen.print("i:");
screen.print(forceVal);
delay(100);
//set cursor to middle of screen for middle values
screen.setCursor(6, 0);
//maps servo motor position from 0-180 to 0-99
int motorPos = map(motorPos, 0, 170, 0, 99);
//servo motor/middle actuator value
screen.print("m:");
screen.print(motorPos);
delay(100);
//set cursor to middle of screen for middle value on second row
screen.setCursor(6, 1);
//maps photoresistor values to 0-99
int lightVal = map(lightVal, 0, 1023, 0, 99);
//photoresistor/middle sensor value
screen.print("m:");
screen.print(lightVal);
delay(100);
//set cursor to bottom corner of screen for output value
screen.setCursor(12, 1);
//maps neopixel values to 0-99
colorVal = map(colorVal, 0, 360, 0, 99);
//display neopixel color value
screen.print("o:");
screen.print(colorVal);
delay(100);
//if the button isn't pressed:
} else {
// step 1: read all of the sensors
//reads value from FSR input
int forceVal = analogRead(FORCEPIN);
//create var to store light value, set light value from the photoresistor input data
int lightVal = analogRead(PHOTOPIN);
// step 2: make decisions about what to do
//maps possible FSR inputs to servo motor range of 180deg.
int motorPos = map(forceVal, 0, 1023, 0, 170);
//maps photoresistor input data to hue
int colorVal = map(lightVal, 300, 800, 0, 360);
// step 3: drive all of the outputs
//move the servo to that proportional position
MOTORCONTROL.write(motorPos);
delay(10);
//make the NeoPixel emit said color
int h = colorVal;
uint8_t r8, g8, b8;
hslToRgb(h, r8, g8, b8);
for (int i = 0; i < NUMLEDS; i++) {
colors[i] = rgb_color(r8, g8, b8);
}
ledStrip.write(colors, NUMLEDS);
delay(10);
// step 4: report data back to the user
//LCD code
//sets cursor to top left corner
screen.home();
//maps FSR value to be 0-99
forceVal = map(forceVal, 0, 1023, 0, 99);
//input sensor's value
screen.print("i:");
screen.print(forceVal);
delay(100);
//set cursor to middle of screen for middle values
screen.setCursor(6, 0);
//maps servo motor position from 0-180 to 0-99
motorPos = map(motorPos, 0, 170, 0, 99);
//servo motor/middle actuator value
screen.print("m:");
screen.print(motorPos);
delay(100);
//set cursor to middle of screen for middle value on second row
screen.setCursor(6, 1);
//maps photoresistor values to 0-99
lightVal = map(lightVal, 0, 1023, 0, 99);
//photoresistor/middle sensor value
screen.print("m:");
screen.print(lightVal);
delay(100);
//set cursor to bottom corner of screen for output value
screen.setCursor(12, 1);
//maps neopixel values to 0-99
colorVal = map(colorVal, 0, 360, 0, 99);
//display neopixel color value
screen.print("o:");
screen.print(colorVal);
delay(100);
}
}
// source for hue to rgb code: https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion