The SCavenger

As my senior capstone project, The SCavenger represents the culmination of my four-year undergraduate education. Our challenge was to design an embedded system that addressed a problem within one of three themes: hiking/camping, health and wellness, or gardening.
I collaborated with two fellow Viterbi engineers and two designers from Otis College of Art and Design to create The SCavenger: a device designed to encourage physical movement among traditionally sedentary gamers.
The SCavenger is an interactive adventure journal – an “escape room in a box” – that challenges players to solve puzzles by engaging with their physical environment. Some puzzles prompt players to toggle lights, change elevation, or travel to specific GPS coordinates. Guiding the experience is a narrator who introduces each puzzle and shares stories from her past life, weaving a narrative through the gameplay.

At a system level, The SCavenger consists of a central microcontroller coordinating sensors, audio and display output, and persistent game state across a linear progression.
The project was intentionally built from first principles, starting with an ATmega328p microcontroller and a wire-wrapped protoboard. Initial development focused on establishing reliable power delivery, generating a stable clock signal via an external oscillator, and configuring a programming interface for firmware deployment. Each subsystem was validated through targeted testing before moving on to sensor integration.

Once the core system was operational, we integrated sensors and peripheral devices using a consistent workflow:
- Research. Review datasheets and relevant documentation
- Test. Build a minimal hardware setup and write a focused software unit test
- Integrate. Abstract low-level register access into helper functions and incorporate the device into the broader system

By the end of the semester, this approach resulted in a fully integrated system composed of the following hardware components:

Significant Efforts I Contributed
Debugging the GPS Module
While most puzzles were tested at a desk, the GPS challenge demanded real-world conditions, and quickly became the most technically unpredictable part of the system.
- GPS data doesn’t wait for you
Early implementations relied on blocking serial reads to capture a full NMEA sentence (e.g., “$GPRMC,…\r\n”). This led to severe bugs: the system would freeze waiting for a message, or display corrupted/incomplete coordinate data if a partial sentence was parsed.
To fix this, I rewrote the serial handling using an interrupt-driven ring buffer. Each character from the GPS module was read non-blocking via UART and stored until a full sentence was detected (\r\n). A gps_ready flag marked when a complete message was available. This allowed the game loop to remain responsive while waiting for valid GPS data in the background.
- You only get one sentence at a time
Parsing the GPS stream was also trickier than expected. Multiple sentence types ($GPGGA, $GPRMC) arrived in unpredictable order. Fields could shift depending on the sentence type, and many fields (like coordinates) would be left blank until a satellite fix was acquired.
To handle this, I wrote a robust parser that accepted both sentence types, extracted latitude and longitude fields based on sentence format, and ignored invalid or zeroed-out coordinates until a fix was confirmed.
- Testing required… elevation
Because GPS reception was unreliable indoors, I often had to test outside. The most reliable results came from standing on top of a parking garage, holding the project box like a handheld scanner while watching the LCD slowly update with raw GPS data.

Managing Flash and EEPROM Memory
Flash and EEPROM memory were used to address two distinct constraints: narrative storage and persistent game state.
To support a story-driven experience, the project required storing a large amount of dialogue text. The ATmega328P provides only 2 KB of SRAM, which is quickly exhausted by runtime variables and buffers. Early attempts to store dialogue in RAM proved infeasible as additional puzzles were added. To solve this, all dialogue text was moved into flash memory (PROGMEM), which offers 32 KB total (approximately 30 KB usable). This approach comfortably accommodated the full narrative while preserving SRAM for critical state and runtime logic.

Separately, the device needed to retain progress across power cycles for both testing and user convenience. This was achieved using the ATmega328P’s EEPROM, where a single persistent value - the player’s current puzzle index - was stored. The value was updated whenever the player progressed, allowing the game to resume from the correct point on the next power-up.
Designing UI/UX Under Embedded Constraints
Unlike many capstone projects with a single measurable target, The SCavenger was ultimately judged by an experiential metric: fun. Early design work focused on what makes an “escape room in a box” engaging. I distilled our approach into three principles, each presenting unique challenges.
- The player should feel appropriately challenged. Players bring different levels of puzzle-solving experience, so the system needed adjustable difficulty without changing core gameplay. We implemented an on-demand clue system that lets players request progressively stronger hints when they get stuck.

- The interface should be intuitive. We evaluated several input schemes (including a joystick) and ultimately chose a four-button model: Power, Next, Back, and Clue. Supporting dialogue, prompts, clue confirmation, and multi-step clue navigation within this constraint required a carefully designed finite state machine to keep behavior predictable and prevent dead ends.

- The player should feel emotionally connected to the story. Delivering narrative on a microcontroller with only a 64-character LCD and a buzzer required deliberate pacing and expressive feedback. I wrote a short narrative and implemented delivery using character-by-character text rendering paired with a narrator-associated tonal “voice signature” synchronized with on-screen text.
Implementing this cleanly was more complex than it sounds: the LCD’s low-level command interface, unconventional memory addressing (rows mapped non-sequentially), and text-layout edge cases (word wrapping, line breaks, and responsive button handling during rendering) all had to be managed without breaking pacing or usability.