Sarah: The system begins with a potentiometer, which acts as the user input by varying resistance as it is rotated. This signal is read by the Arduino and mapped to control the angle of a servo motor. The servo’s lever is extended with a popsicle stick, which pulls on a rubber band as it rotates. The stretching of the rubber band creates tension, which is measured by a load cell. The load cell converts this mechanical force into an electrical signal that the Arduino processes. The output of the load cell is then used to control a set of incandescent light bulbs, which illuminate at different brightness levels depending on the measured tension.
Helios: The system starts with a potentiometer to detect rotational movement from the user and transform the signal into rotational movement for the servo. This rational movement is utilized as a force, and pulls on the rubber band attached to the load cell to detect the amount of force outputted by the servo and rubber band. This signal is finally read by the load cell, and coverted into temperature via the lights to the right of the entire project.
Sarah: The load cell mechanism works by translating the servo motor’s rotation into measurable tension. The servo’s lever is reinforced with electrical tape and attached to a popsicle stick with a hole cut in the middle. At the far end of the stick, another hole is cut to tie a rubber band, which is then knotted to the outermost hole of the load cell. As the servo rotates, it pulls the stick and stretches the rubber band, creating tension that the load cell converts into an electrical signal. Positioning the rubber band at the end of the stick increases the lever arm length, making the system more sensitive to changes in rotation.
Helios: The temperature emitter is just three incandescent light bulbs powered by 5V and turned on and off by a MOSFET connected to the data pins in the Arduino. The interesting and most complicated part of the code was mapping the [0, 1023] range of the potentiometer into the [0, 255] range of each of the lights to allow for a smooth flow from light to light as the potentiometer signal, (or load sensor signal) increased. This was done by multiplying the potentiometer value by 3/4, then setting each light to the range associated with their spot in the sequence.
Sarah: This video demonstrates how one form of energy (rotation) can be transduced step by step into another, represented as temperature through the dimming of incandescent bulbs. The intended system connects a potentiometer to a servo motor, which applies tension to a load cell. The load cell’s readings are then used to control the brightness of the bulbs.
During testing, I encountered significant troubleshooting challenges. A faulty Arduino produced unexpected errors, and the load cell failed to provide usable readings. As a result, the lightbulbs could not dim or brighten as planned. However, the mechanical chain from the potentiometer to the servo motor and into the load cell functioned correctly, showing that the core transduction pathway was working, even if the final output stage was incomplete.
Helios: This video shows the input (trying) to go through the double transducer to output a final signal by the 3 lights, as well as the skip the middle button working as intended and skipping the middle transducer for a direct input-output.
Sarah: Potentiometer to Servo Motor
This is a breadboard and Arduino wired together, with the potentiometer controlling the servo motor’s lever arm. From this step, we learned how a simple change in resistance could be translated into controlled rotation, giving us a clear understanding of how input values map to physical movement.
Sarah: Incandescent Bulb Wiring
Here, an incandescent bulb is wired into the breadboard and powered through the Arduino using a transistor and resistor. The bulb glows when current passes through, acting as the system’s heat and light output. Through this stage, we learned how to regulate higher-power components safely and how transistors can be used to control brightness effectively.
Sarah: Soldering the Load Cell
This image shows wires being soldered to the load cell while it is secured in a vise, with the vacuum on to remove fumes. Preparing the sensor taught us how to make clean, reliable solder joints and gave us insight into how careful wiring impacts sensor accuracy. We also learned that precise handling during soldering is essential for achieving consistent readings.
Helios: Setting the Lights
This image shows a progress step where the LCD was powered and displaying information while the three incandescent lights were able to be powered and manipulated by the Arduino and potentiometer. It also marked the point in which we knew how we would wire all three lights to the Arduino using the MOSFETs;
Helios: Setting the Light
This image shows another progress step where we figured out how to not only power one lightbulb using the MOSFET and Arduino, but also how to vary and manipulate the output of the lightbulb.
The system begins with a potentiometer, a knob that changes electrical resistance as it turns. This resistance is read by the Arduino and mapped to control a servo motor, a small motor designed to rotate to specific angles rather than spin continuously. The servo’s lever arm is connected to a popsicle stick and rubber band mechanism, which stretches when the servo rotates. This stretch is measured by a load cell, a sensor that converts mechanical tension into an electrical signal.
The data from the load cell is then used to adjust the brightness of three 5V incandescent bulbs. These bulbs convert electrical energy into both heat and light, making them perfect for simulating a temperature-like output. Depending on the input, the bulbs illuminate at different intensities, creating a stepped visualization of “heat.” For example, if the potentiometer is turned halfway, one bulb will be fully on, one will glow dimly, and the third will remain off. Finally, the system displays real-time feedback on an LCD screen, which shows both the tension measured by the load cell and the relative brightness levels of the bulbs. This allows users to see not just the physical light output but also the underlying numerical data driving it.
At the beginning, we found the brainstorming process to be a challenging step. With so many unfamiliar electrical components, it was difficult to decide on the parts for a chain that was both feasible and creative. Once we settled on our middle load cell step, the initial wiring between the potentiometer and servo motor was straightforward. Learning how to solder and desolder was a rewarding part of the project. We also learned how to dim incandescent bulbs using transistors.
The load cell, in particular, required multiple attempts before we achieved reliable connections. Even so, achieving consistent readings from the load cell remained an issue. Its sensitivity to small variations in tension and alignment made it difficult to stabilize. If we were to restart the project, we would simplify the design by choosing a different middle component than the load cell. While it was exciting in concept, it introduced a level of fragility and unreliability that slowed down our progress. Something more consistent would have allowed us to focus on refining the system’s clarity rather than troubleshooting inconsistent inputs. Similarly, we later realized we could have avoided using three separate barrel jacks, which would have drastically simplified wiring and debugging.
Despite the challenges, this project pushed us to make our designs both functional and resilient. We learned that keeping mechanisms simple often leads to more robust results. Looking ahead, we want to continue exploring ways to integrate multiple transducers while maintaining system stability. This project has shown us how iteration, problem-solving, and collaborative testing can transform uncertainty into growth.
/*
Project 1: Double Transducer - Rotation to Temperature
60-223 Intro to Physical Computing
A potentiometer detects rotational movement from the user and transforms the signal
into rotational movement for the servo. This rotation pulls a rubber to create a
force measured in a range of 18000 to 27000 by the load sensor, and coverted into
a range from 0-255*3, where every 255 represents a different light.
Pin mapping:
Arduino pin | role | description
-------------------------------------
A0 input potentiometer
A3 output servo
3 output light1
5 output light2
6 output light3
8 loadcell data
9 loadcell clock
10 input skip-the-middle button
Sarah Fernandes and Helios Gayibor
September 2025
*/
#include <Servo.h>
#include <LiquidCrystal_I2C.h>
#include "HX711.h"
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
HX711 scale;
Servo motor;
const int SERVOPIN = A3;
const int POTPIN = A0;
//make sure these are pwm pins
const int LIGHTPIN1 = 3;
const int LIGHTPIN2 = 5;
const int LIGHTPIN3 = 6;
//loadcell stuff
const int DATAPIN = 9;
const int CLOCKPIN = 8;
//Skip the middle button
const int BUTTONPIN = 10;
void setup() {
motor.attach(SERVOPIN);
pinMode(POTPIN, INPUT);
pinMode(LIGHTPIN1, OUTPUT);
pinMode(LIGHTPIN2, OUTPUT);
pinMode(LIGHTPIN3, OUTPUT);
pinMode(BUTTONPIN, INPUT_PULLUP);
Serial.begin(115200);
lcdSetup();
loadcellSetup();
}
/*Setting the prevTime for the display*/
int prevTime = 0;
void loop() {
/*True when pressed, False when released*/
bool button = !digitalRead(BUTTONPIN);
/*ngl, the loadcell is mad inconsistent. you're just gonna have to work with me
on this one*/
int loadCellRaw = scale.read_median(3);
int loadCellVal = map(loadCellRaw, 27000, 18000, 0, 255);
loadCellVal = max(min(255, loadCellVal), 0);
/*Value from potentiometer, ranging from 0-1024*/
int potVal = analogRead(POTPIN);
/*The value assigned to the servo. Depends on the value from the potentiometer*/
int servoVal = map(potVal, 0, 1024, 100, 60);
delay(50); //delay pot to servo
motor.write(servoVal);
/*The value assigned to the lights, ranging from 0-(1024 * 3/4), where each light is
represented by their respective 255 range.*/
int lightVal;
if (button) {
//if skip-the-middle button is pressed
lightVal = potVal * 3 / 4;
} else {
//regular function
lightVal = loadCellVal * 3; //TODO: figure out loadcell math/mapping
}
/*Each light waits for light value to reach their respective range:
[0, 255] for light1,
[255, 510] for light2,
[510, 765] for light3
*/
int light1 = min(lightVal, 255);
int light2 = min(max(0, lightVal-255), 255);
int light3 = min(max(0, lightVal-255* 2), 255);
analogWrite(LIGHTPIN1, light1);
analogWrite(LIGHTPIN2, light2);
analogWrite(LIGHTPIN3, light3);
// LCD printing
if (millis() - prevTime > 250) {
prevTime = millis();
lcd.clear();
if (button) {
lcd.setCursor(0, 0);
lcd.print(loadCellRaw);
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("i:" + String(map(potVal, 0, 1024, 0, 99)));
lcd.setCursor(6, 0);
lcd.print("s:" + String(map(servoVal, 100, 60, 0, 99)));
lcd.setCursor(8, 1);
lcd.print(String(map(loadCellVal, 0, 255, 0, 99)));
lcd.setCursor(12, 1);
lcd.print("o:" + String(map(lightVal, 0, 255 * 3, 0, 99)));
Serial.println("raw: " + String(loadCellRaw));
}
}
}
/*Sets up the LCD*/
void lcdSetup() {
lcd.init();
lcd.backlight();
}
/*Sets up the loadcell*/
void loadcellSetup() {
scale.begin(DATAPIN, CLOCKPIN);
scale.set_scale(127.15);
scale.tare();
}