The cushion with all components embeded inside
All components laid out on the cushion with labels.
The Sitting Reminder Cushion is a honeycomb gel seat cushion with embedded electronics that reminds the user to take a break after sitting for 40 minutes. Three QRD1114 infrared proximity sensors detect when someone is sitting. After the time limit is reached, a piezoelectric buzzer beeps and a pancake vibration motor vibrates to alert the user. Standing up automatically resets the timer. A manual reset button is also included.
The cushion placed on a chair, ready for use
A QRD1114 infrared proximity sensor embedded in a honeycomb cell.
Perfboard with soldered buzzer and transistor circuit.
Interior of the cushion with the cover unzipped, showing the embedded electronics.
Demonstration of the sitting reminder in action. The timer threshold is set to 10 seconds for demonstration purposes (40 minutes in normal use). When the user sits down, the sensors detect their presence and start the timer. After 10 seconds, the piezoelectric buzzer beeps and the vibration motor activates as a reminder to stand up.
Early breadboard prototype with all components wired up before soldering.
Debugging soldered sensor boards using a multimeter, with sensors laid out across the cushion surface.
Testing sensor placement on the honeycomb cushion before final assembly.
Breadboard prototype placed on the cushion during early testing to verify sensor threshold values.
The most time-consuming part of the process was soldering the sensors into the cushion. Each QRD1114 has four pins, and because the sensors had to be threaded through the honeycomb cells from below and soldered in reverse orientation, the process was slow and difficult to get right. In the early stages, I also repeatedly mixed up the A and C pins on the sensors, which caused incorrect readings and required resoldering several connections.
Another recurring problem was accidental solder bridges — nearby pins getting connected together unintentionally. This was especially problematic on the underside of the cushion, where the sensor leads are close together. When any two pins touched, the sensor readings became completely unstable. I had to use tape ties to physically separate all the wires and pin leads to prevent contact, as shown in the process photo. The challenge was that the honeycomb silicone material is very difficult to bond anything to — hot glue, which would normally be the easiest fix, does not stick well to silicone, so keeping everything in place required more creative solutions.
During the in-class critique, two comments stood out to me. One reviewer suggested "perhaps a way to receive feedback from the device based on completion," which I agree with — having some kind of confirmation signal when the timer resets or when the user successfully stands up would make the interaction feel more complete. Another reviewer noted "I would worry about damage to the circuitry under weight or being able to feel the electronics when you sit on them. You could move the electronics to an outside box and just run one wire to the actual seat." I also agree with this — it was something I had already considered during the build process, and in hindsight, separating the control electronics from the cushion entirely would have been a cleaner and more durable solution.
Overall, I am happy with how the project turned out. It achieved my original goal of creating a passive sitting reminder that requires no active input from the user. That said, I am not fully satisfied with the wiring. The cables are messy and not well organized, and given more time I would have found a more rational way to route and manage them.
What this project trained most was my soldering skills. The code logic was not particularly complex — the harder challenge was almost entirely on the hardware side. Getting the device to run was easier than I expected, but making the soldered version perform as reliably as the breadboard prototype was much more difficult and time-consuming than I had anticipated.
If I were to build another iteration, I would start by planning the wire routing more carefully before soldering anything, rather than figuring it out as I went.
/*
* Project Title: Sitting Reminder Cushion
*
* Description:
* This program monitors how long a person has been sitting using three
* QRD1114 infrared proximity sensors embedded in a honeycomb gel cushion.
* When the sensors detect a person sitting (reflected IR light drops below
* a threshold), a timer starts. After 40 minutes of continuous sitting,
* a piezoelectric buzzer beeps and a pancake vibration motor activates
* to remind the user to stand up and take a break.
* When the user stands up, the timer resets automatically.
* A manual reset button is also provided.
*
* Pin Mapping:
* A0 - QRD1114 sensor 1, phototransistor output (C pin)
* A1 - QRD1114 sensor 2, phototransistor output (C pin)
* A2 - QRD1114 sensor 3, phototransistor output (C pin)
* D3 - Piezoelectric buzzer (positive leg)
* D5 - Pancake vibration motor, via 2N2222 NPN transistor
* D7 - Push button for manual timer reset (uses INPUT_PULLUP)
* D13 - LED status indicator (on = sitting detected)
*
* Each QRD1114 is wired as follows:
* LED anode (long left pin) → 220Ω resistor → 5V
* LED cathode (short left pin) → GND
* Collector (long right pin) → 10kΩ pull-up to 5V, also → analog pin
* Emitter (short right pin) → GND
*
* The vibration motor is driven through a 2N2222 NPN transistor:
* D5 → 1kΩ → Base
* Collector → motor negative lead
* Emitter → GND
* Motor positive lead → 5V
* Flyback diode across motor terminals (cathode to 5V)
*
* Credits:
* Circuit design and code developed with assistance from Claude (Anthropic).
* Sensor wiring reference: 60-223 Intro to Physical Computing course materials,
* Carnegie Mellon University.
*
* License: CC BY-NC-SA 4.0
*/
// ===== Pin definitions =====
const int SENSOR_PINS[3] = {A0, A1, A2};
const int BUZZER_PIN = 3;
const int MOTOR_PIN = 5;
const int BUTTON_PIN = 7;
const int LED_PIN = 13;
// ===== Configuration =====
// Sensor reading threshold: sitting causes IR reflection to increase,
// raising the analog value. Values above threshold = person detected.
const int SENSOR_THRESHOLD = 500;
// Number of sensors that must trigger to count as "sitting"
// (2 out of 3 prevents false positives from shifting weight)
const int SENSORS_NEEDED = 2;
// How long before the reminder triggers (40 minutes)
const long SIT_LIMIT_MS = 40UL * 60 * 1000;
// How long the buzzer and motor run when alert triggers
const long ALERT_DURATION_MS = 5000;
// Buzzer tone frequency in Hz
const int BUZZER_FREQ = 2000;
// ===== State variables =====
bool isSitting = false;
bool isAlerting = false;
long sitStartTime = 0;
long alertStartTime = 0;
// ===== checkSitting() =====
// Reads all three sensors and returns true if enough sensors
// detect a person sitting (value above threshold).
bool checkSitting() {
int triggered = 0;
for (int i = 0; i < 3; i++) {
int val = analogRead(SENSOR_PINS[i]);
Serial.print("S"); Serial.print(i);
Serial.print(":"); Serial.print(val);
Serial.print(" ");
if (val > SENSOR_THRESHOLD) triggered++;
}
Serial.println();
return (triggered >= SENSORS_NEEDED);
}
// ===== resetAll() =====
// Resets all state and turns off buzzer, motor, and LED.
void resetAll() {
isSitting = false;
isAlerting = false;
noTone(BUZZER_PIN);
digitalWrite(MOTOR_PIN, LOW);
digitalWrite(LED_PIN, LOW);
}
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(MOTOR_PIN, OUTPUT);
digitalWrite(MOTOR_PIN, LOW); // ensure motor is off on startup
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP); // button uses internal pull-up, no external resistor needed
Serial.begin(9600);
Serial.println("Sitting Reminder started.");
}
void loop() {
bool sittingNow = checkSitting();
long currentTime = millis();
// Detect sit-down event
if (sittingNow && !isSitting) {
isSitting = true;
sitStartTime = currentTime;
Serial.println("Sitting detected. Timer started.");
}
// Detect stand-up event: auto reset
else if (!sittingNow && isSitting) {
resetAll();
Serial.println("User stood up. Timer reset.");
}
// Manual reset via button
if (digitalRead(BUTTON_PIN) == LOW) {
resetAll();
Serial.println("Manual reset.");
delay(300); // simple debounce
}
// Check if sitting time limit has been reached
if (isSitting && !isAlerting) {
long elapsed = currentTime - sitStartTime;
if (elapsed >= SIT_LIMIT_MS) {
isAlerting = true;
alertStartTime = currentTime;
Serial.println("40 minutes reached. Alerting user.");
}
}
// Run alert: buzzer + motor for ALERT_DURATION_MS
if (isAlerting) {
tone(BUZZER_PIN, BUZZER_FREQ);
digitalWrite(MOTOR_PIN, HIGH);
if (currentTime - alertStartTime >= ALERT_DURATION_MS) {
noTone(BUZZER_PIN);
digitalWrite(MOTOR_PIN, LOW);
isAlerting = false;
alertStartTime = currentTime;
}
}
// LED on when sitting is detected
digitalWrite(LED_PIN, isSitting ? HIGH : LOW);
delay(100);
}