WEEK 13

Written by:

BloomSense

Final Project: Documentation

Interactive single‐player reaction game built around two flexible paper-flower petals 3D-printed in TPU and driven by SG90 servos via wire linkages. Each “flower” opens and closes in a staggered sequence; the player must wave their hand within 15 cm (detected by VL53L0X ToF sensors) to “catch” each bloom. Complete three rounds of increasing speed and difficulty, scoring five successful reactions out of six chances to win—and enjoy a 5 s cheer of rapid petal dances upon victory. A full-screen p5.js UI provides a branded gradient background, countdown, in-game progress bar, and confetti/rain effects, all tied to the Arduino’s serial messages.

Final Video

Hardware demo

UI

Circuit & Code

Code

Arduino: the runEvent() loop that drives each bloom

// Runs one "open → close → react → pause" cycle on a flower.
// reverse=true flips the servo direction; cfg holds the timings for the current round.
bool runEvent(Servo &srv,
              Adafruit_VL53L0X &lox,
              bool reverse,
              Round &cfg)
{
  uint8_t openA  = reverse ? 0   : 100;
  uint8_t closeA = reverse ? 100 :   0;

  // 1) OPEN petal
  srv.write(openA);
  delay(cfg.openT);

  // 2) CLOSE petal
  srv.write(closeA);

  // 3) REACT window: watch for your hand
  uint32_t t0 = millis();
  bool reacted = false;
  while (millis() - t0 < cfg.reactT) {
    if (lox.rangingTest(&m, false), m.RangeStatus == 0 
        && m.RangeMilliMeter < THRESH_MM) {
      reacted = true;
      break;
    }
  }
  if (reacted) {
    Serial.println("REACT");
    // reward: re-open briefly
    srv.write(openA);
  }

  // 4) PAUSE before next event
  delay(cfg.pauseT);

  return reacted;
}

p5.js: handling serial messages

// 1. Trigger the port chooser and open the serial port
select('#connect').mousePressed(async () => {
  if (port) return;  
  port = await navigator.serial.requestPort();
  await port.open({ baudRate: 115200 });

  // 2. Pipe the readable stream through a TextDecoder
  decoder = new TextDecoderStream();
  port.readable.pipeTo(decoder.writable);

  // 3. Get a reader and start the read loop
  reader = decoder.readable.getReader();
  readLoop();
});

// 4. Continuously read chunks, split on newline, and dispatch
async function readLoop() {
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += value;
    let parts = buffer.split(/\r?\n/);
    buffer = parts.pop();          // leftover partial line
    for (let line of parts) {
      handleLine(line.trim());
    }
  }
}

Hardware List

3D-Printed Flower Petals

  • 2× custom petal mounts printed in soft TPU (for flexibility and grip)

Linkage Wiring

  • Steel or nylon wire rods connecting each SG90 horn to its TPU petal, transmitting servo motion

Remaining Components

  • Arduino Uno
  • 2 × Adafruit VL53L0X Time-of-Flight breakouts
  • 2 × SG90 micro-servos
  • 1 × tactile pushbutton (D4 + GND)
  • External 5 V rail + 470 µF decoupling cap
  • Jumper wires, common ground bus

Happy Moments / Challenges

  • Happy: watching the first soft-TPU petal flex open under servo motion—felt delightfully organic.
  • Challenge: managing dual VL53L0X modules on I²C; solved by toggling XSHUT lines and reassigning one to 0x30.
  • Happy: getting the 5 s servo “cheer” dance synced with UI confetti—extremely gratifying!
  • Challenge: refining round timing so it wasn’t too rushed or too slow—iterated over several user tests.

Future Improvements

Mechanical: refine petal shape for even smoother blooms; integrate flex joints directly in print.

UI Calibration: add on-screen sliders to adjust reaction distance threshold and round speeds.

Credits

My contributions: mechanical design (TPU petals + wire linkages), full Arduino firmware, p5.js UI, sound & graphics integration.

Libraries & Resources:

AI Assistance: Used Chatgpt to Debug.

Office Hours: Thanks to Prof. David Rios for my idea brainstorm.

VL53L0X Soldering Help: Proud Aiemruksa (GA)

Leave a comment

Latest Articles

Previous: