Final Images
Image of team member 1's full double transducer circuit.
Image of Member 2's full double transducer circuit
Detail Photos
Final Project Movies
Student 1 Video
Student 2 Video
Narrative Description
A flat board sits on a table. One part of the board can see color from a light. The board changes that color into a brightness number. That number makes a small light shine brighter or dimmer. Another part of the board reads how bright the light is. Then the board moves a small arm to a position that matches that brightness. When the color or light changes, the arm moves to a new spot.
Progress Photos
Using RGB LED at first complicated the process and caused a lot of trouble.
RGB LED causes trouble, the cathode and anode are reversed and the LED does not light up at all.
Switching to normal LED, it lit up successfully but the process of brightness gradient still needs to be perfected.
When making another new circuit, found that the light didn't light up again.
Discussion
One thing we learned is that keeping the circuit simple makes troubleshooting much easier. We originally tried using an RGB LED, but it required extra components and more complex mapping, which complicated both hardware and software. Simplifying to a standard LED made the system more reliable and easier to test.
The hardest part was mapping the LED brightness from the color sensor. This taught us how important it is for components to “speak the same language” and how careful scaling is needed to get the system working properly.
In retrospect, starting with a simpler LED and planning mapping ranges ahead of time would have saved time and made testing smoother. Overall, this project helped us understand how sensors, outputs, and code interact, and showed the value of keeping circuits manageable while still exploring creative behaviors.
Schematic
Code
/*
Project Title: Double Transducer: Colored Light to Rotation
Team Members:
- Jack
- Coral
Description:
This project uses a color sensor (APDS9960) and a light-dependent resistor (LDR)
as inputs. The sensed data is evaluated and mapped to control an LED brightness
and a servo motor. A button allows bypassing the LDR and directly mapping color
hue to servo motion. System status is displayed on a 16x2 I2C LCD.
Pin Mapping:
LED_PIN -> D3
SERVO_PIN -> D6
BUTTON_PIN -> D2
LDR_PIN -> A0
APDS9960 -> I2C (SDA/SCL)
LCD (I2C) -> Address 0x27
Credits:
Uses Adafruit_APDS9960 library and Arduino Servo library.
*/
#include <Wire.h>
#include <Adafruit_APDS9960.h>
#include <Servo.h>
#include <LiquidCrystal_I2C.h>
// -------------------- PIN DEFINITIONS --------------------
#define LED_PIN 3
#define LDR_PIN A0
#define SERVO_PIN 6
#define BUTTON_PIN 2
// -------------------- OBJECTS --------------------
Adafruit_APDS9960 apds;
Servo myServo;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// -------------------- GLOBAL VARIABLES --------------------
uint16_t r, g, b, c;
int LDR_MIN = 1023;
int LDR_MAX = 0;
float smoothedValue = 0;
// -------------------- HELPER FUNCTIONS --------------------
// Convert RGB to Hue (0–360)
float rgbToHue(float r, float g, float b) {
float maxVal = max(r, max(g, b));
float minVal = min(r, min(g, b));
float delta = maxVal - minVal;
if (delta == 0) return 0;
float hue;
if (maxVal == r)
hue = 60 * fmod(((g - b) / delta), 6);
else if (maxVal == g)
hue = 60 * (((b - r) / delta) + 2);
else
hue = 60 * (((r - g) / delta) + 4);
if (hue < 0) hue += 360;
return hue;
}
// -------------------- SETUP --------------------
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(9600);
myServo.attach(SERVO_PIN);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.print("Initializing...");
if (!apds.begin()) {
Serial.println("APDS9960 init failed!");
while (1);
}
apds.enableColor(true);
// Initial LDR calibration
Serial.println("Calibrating LDR...");
for (int i = 0; i < 100; i++) {
int val = analogRead(LDR_PIN);
LDR_MIN = min(LDR_MIN, val);
LDR_MAX = max(LDR_MAX, val);
delay(20);
}
smoothedValue = map(analogRead(LDR_PIN), LDR_MIN, LDR_MAX, 0, 180);
lcd.clear();
}
// -------------------- LOOP --------------------
void loop() {
// Read
if (apds.colorDataReady()) {
// ---------------- READ ----------------
apds.getColorData(&r, &g, &b, &c);
bool bypass = (digitalRead(BUTTON_PIN) == LOW);
long sum = 0;
for (int i = 0; i < 5; i++) {
sum += analogRead(LDR_PIN);
}
int rawLDR = sum / 5;
// ---------------- EVALUATE ----------------
LDR_MIN = min(LDR_MIN, rawLDR);
LDR_MAX = max(LDR_MAX, rawLDR);
float hue = rgbToHue(r, g, b);
float radians = hue * DEG_TO_RAD;
float wave = (cos(radians) + 1.0) * 0.5;
wave = pow(wave, 1.6);
int ledBrightness = wave * 255;
float targetValue;
if (bypass) {
targetValue = map(hue, 0, 360, 0, 180);
} else {
targetValue = map(rawLDR, LDR_MIN, LDR_MAX, 0, 180);
targetValue = constrain(targetValue, 0, 180);
}
float alpha = 0.08; // Smaller alpha produces smoother but slower servo response
smoothedValue = smoothedValue * (1 - alpha) + targetValue * alpha;
int servoPos = constrain(smoothedValue, 0, 180);
// ---------------- ACTUATE ----------------
analogWrite(LED_PIN, ledBrightness);
myServo.write(servoPos);
// ---------------- REPORT ----------------
int inputMapped, ldrMapped;
if (bypass) {
inputMapped = map(hue, 0, 360, 0, 99);
ldrMapped = 0;
} else {
int temp = map(rawLDR, LDR_MIN, LDR_MAX, 0, 99);
inputMapped = temp;
ldrMapped = temp;
}
int ledMapped = map(ledBrightness, 0, 255, 0, 99);
int servoMapped = map(servoPos, 0, 180, 0, 99);
inputMapped = constrain(inputMapped, 0, 99);
ldrMapped = constrain(ldrMapped, 0, 99);
ledMapped = constrain(ledMapped, 0, 99);
servoMapped = constrain(servoMapped, 0, 99);
lcd.setCursor(0, 0);
lcd.print("i:");
if (inputMapped < 10) lcd.print("0");
lcd.print(inputMapped);
lcd.setCursor(7, 0);
lcd.print("m:");
if (ledMapped < 10) lcd.print("0");
lcd.print(ledMapped);
lcd.setCursor(6, 1);
if (ldrMapped < 10) lcd.print("0");
lcd.print(ldrMapped);
lcd.setCursor(11, 1);
lcd.print("o:");
if (servoMapped < 10) lcd.print("0");
lcd.print(servoMapped);
Serial.print("Input: "); Serial.print(inputMapped);
Serial.print(" | LED: "); Serial.print(ledMapped);
Serial.print(" | LDR: "); Serial.print(ldrMapped);
Serial.print(" | Servo: "); Serial.println(servoMapped);
}
delay(40);
}