Front view of the final product, 'screaming phone jail'. LCD displays time, up and down arrows to increase and decrease time in 30-minute increments, and a 'set time' button to start the countdown.
Have you ever caught yourself doom-scrolling when you have a million things to do, only to realize hours have slipped by?
The Screaming Phone Jail is an Arduino-based device designed to curb excessive phone use by temporarily locking your smartphone away, blending behavioral intervention with playful design. Users set a timer and place their phone inside the ‘jail.’ If they try to remove it before time is up, a loud buzzer alarm—hence, the “screaming”—goes off, making sure they stay accountable.
Person placing their phone in the 'Screaming phone jail'
Angled view of the jail to understand it's scale
Detail of hands holding onto the rod of the jail, attempting to break out (inspired from an illustation)
Detail of the project tile on top of the jal and the opening mechanism of the top.
In this video, I show how the screaming phone jail works. The user places the phone into the jail and sets the time using the button. The LCD displays the time and counts down. On removing the phone, the jail makes a loud, obnoxious noise - 'screaming. The user can stop this when they really need to by pressing 3 times on the set time button.
Incrementally adding componenets.
Soldering components & wires.
Circuit placed inside the enclosure
Attempting to cover whole surface with force sensors.
Testing distance sensor as an alternative to detect phone placement.
Trial laser cut with acrylic revealed it's limitations.
Switching to wood and incrementally sticking pieces together.
Once I finalized the idea, I outlined the exact functionality I wanted. I created a block diagram schematic to map out the logic and ensure a structured approach to development.
Process Overview
Planning execution with block diagram, schematic, and writing down code logic
Building & coding incrementally on a breadboard
Designing the phone jail laser-cut file
Creating a rough laser-cut prototype
Testing and selecting the best sensor for phone detection
Soldering components onto a protoboard
Adjusting the laser-cut design
Assembling the enclosure incrementally
Placing the computational components
Choosing the Right Sensor
A key challenge was selecting the most effective sensor to detect phone presence. I experimented with both distance sensors and force sensors, ultimately finding that force sensors were significantly more reliable and consistent.
Initially, I included multiple force sensors for increased coverage, but this introduced errors and inefficiencies in both hardware and code. To simplify and improve reliability, I redesigned the jail structure to accommodate a single, well-placed force sensor instead.
Prototyping & Material Challenges
For the enclosure, I initially laser-cut the phone jail in acrylic to test the sensor’s effectiveness. However, this surfaced several limitations:
Engravings on acrylic were unclear, making labels and markings hard to read.
The material was thinner than expected, which led to finger joint assembly issues. Since I wasn’t familiar with 3D construction, I had overlooked this during the design phase.
To address these issues, I switched to wood, which provided better structural stability and engraving visibility.
Iterative Refinements & Soldering
Much of my design evolved through making, a concept known as “reflection-in-action.” Many unforeseen challenges surfaced only during hands-on construction, requiring multiple iterations to refine the balance between functionality, aesthetics, and ergonomics.
To ensure a compact and secure electronic setup, I soldered all components onto a single protoboard. Given the limited space and number of connections, this required precise soldering techniques to avoid shorts or poor connections.
Fabrication & Code Development
For laser cutting the jail enclosure and housing the electronics, I used Adobe Illustrator to refine the design. I based my structure on an open-source finger-joint box template from Cuttle.
On the coding front, the logic was relatively straightforward, but I used AI assistance (Perplexity) with ‘chain of thought’ prompting to debug and optimize my code efficiently.
“I think this idea is so funny, but as far as temptations to open early, I think using different motives like solving puzzles or cooldowns after opening, even a different sound effect for customization or a rewards (carrot and stick) system, could make the urge to open the box more difficult.”
I really like this suggestion to make it harder to break into. As of now, I kept it multiple button pushes to stop the alarm for ease of test. During & after building this, I realized how each aspect of the device could be ideated on more. This aspect is surely one of them.
"SO FUNNY I love how much personality is in the box. Good job thinking of the scientifically basked pomodoro technique"
I believe that as a designer, having an element of play or fun in my design is important to me. I'm glad it came through!
“There could be different variations of the sound to induce different reactions.”
While approaching this, to me the role of the sound was to cause disturbance to not just you but others as well, having a ‘public embarrassment’ aspect. I’m not sure if it should have a role other than being loud and obnoxious but it’s interesting to consider!
In all, I’m quite happy with how the project came out. I learned A LOT! While I the circuit was simple, the fabrication took way longer than I anticipated. Shifting to think in ‘3D’ and not just ‘2D’ for fabrication was certainly a challenge: it took a lot of trial and error. I would have liked to have finessed the usability more for instance, make it harder to stop the alarm and think about the LCD display for more states.
I learned that I’m somewhat organized in how I approach things and if/when I don’t think things through or be organized, I have had to redo them. I’m also surprisingly good at thinking about the logic of how the code should work. Laser cutting surprisingly took me the most time. I believe I laser cut about 4 times! In one, the incorrect ‘drag and drop’ of the design onto Corel Draw changed its scale, making it unusable. I realized I’m not particularly good at working with 3d forms & material.
As next steps, I would definitely want to add more difficulty to stop the alarm of the jail, giving them 1 ‘get out of jail free’ card a week and otherwise having to solve a math problem on the LCD screen.
/*
Screaming Phone Jail
Author: Shivani Kannan
Description:
This Arduino project implements a 'screaming phone jail' designed to help control phone usage.
The device allows users to set a timer, place their phone in the 'jail', and prevents access
until the set time has elapsed. If the phone is removed prematurely, an alarm sounds.
Key features:
- Timer setting with 30-minute increments/decrements
- LCD display for timer and status information
- Multiple force sensors to detect phone presence
- Buzzer alarm for unauthorized phone removal
- Triple-press reset functionality
Pin Mapping:
Arduino pin | Component | Role
---------------------------------------------------------------------------
2 | Pushbutton | Increase timer by 30-minute increments
4 | Pushbutton | Decrease timer by 30-minute increments
6 | Pushbutton | Set time / Start countdown / Reset (triple press)
12 | Passive Buzzer | Sound alarm when phone is removed during countdown
A0 | Force Sensor 1 | Detect phone presence
A1 | Force Sensor 2 | Detect phone presence
SDA | LCD Display | I2C data line for display
SCL | LCD Display | I2C clock line for display
Libraries used:
- Wire.h
- LiquidCrystal_I2C.h
Credits:
- LCD I2C library by Frank de Brabander
- Inspired by various Arduino timer projects and phone usage control apps
Last updated: 14th March 2025
*/
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
// Pin definitions
const int INCREASE_PIN = 2;
const int DECREASE_PIN = 4;
const int START_RESET_PIN = 6;
const int FORCE_SENSOR_PINS[] = {A0, A1}; // Two force sensor pins
const int NUM_FORCE_SENSORS = sizeof(FORCE_SENSOR_PINS) / sizeof(FORCE_SENSOR_PINS[0]);
const int BUZZER_PIN = 12;
// LCD setup (address, columns, rows)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Time and countdown variables
int timeInSeconds = 0;
bool isCountingDown = false;
bool isPaused = false;
unsigned long lastCountdownUpdate = 0;
// Button state variables
int lastIncreaseButtonState = LOW;
int lastDecreaseButtonState = LOW;
int lastStartResetButtonState = LOW;
// Triple-press reset variables
int buttonPressCount = 0;
unsigned long lastButtonPressTime = 0;
const unsigned long TRIPLE_PRESS_WINDOW = 1000; // 1 second window for triple press
// Time constants
const int THIRTY_MINUTES = 1800; // 30 minutes in seconds
const int ONE_SECOND = 1000; // 1 second in milliseconds
// Force sensor
const int FORCE_THRESHOLD = 15; // Adjust based on sensor and phone weight
bool phoneDetected = false;
unsigned long lastPhoneCheckTime = 0;
const unsigned long PHONE_CHECK_INTERVAL = 1000; // Check every 1000 ms
// Buzzer variables
unsigned long lastToneChange = 0;
int currentTone = 1000;
int toneStep = 100;
const int MIN_TONE = 500;
const int MAX_TONE = 2000;
const int TONE_CHANGE_INTERVAL = 100; // Change tone every 100 ms
void setup() {
Serial.begin(115200);
// Initialize button pins
pinMode(INCREASE_PIN, INPUT);
pinMode(DECREASE_PIN, INPUT);
pinMode(START_RESET_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Initialize force sensor pins
for (int i = 0; i < NUM_FORCE_SENSORS; i++) {
pinMode(FORCE_SENSOR_PINS[i], INPUT);
}
// Initialize LCD
initializeLCD();
}
void loop() {
// Read current button states
int currentIncreaseButtonState = digitalRead(INCREASE_PIN);
int currentDecreaseButtonState = digitalRead(DECREASE_PIN);
int currentStartResetButtonState = digitalRead(START_RESET_PIN);
// Check for phone presence
checkPhonePresence();
// Handle button presses and timer logic only if phone is detected or timer is counting down
if (phoneDetected || isCountingDown) {
handleButtons(currentIncreaseButtonState, currentDecreaseButtonState, currentStartResetButtonState);
}
// Update countdown if active and phone is detected
if (isCountingDown && phoneDetected) {
updateCountdown();
}
// Control buzzer
controlBuzzer();
// Update button states
updateButtonStates(currentIncreaseButtonState, currentDecreaseButtonState, currentStartResetButtonState);
// Display "Place Phone" if timer is not counting down and no force detected
if (!isCountingDown && !phoneDetected) {
lcd.setCursor(0, 0);
lcd.print("Place Phone ");
lcd.setCursor(0, 1);
lcd.print(" ");
}
delay(50); // Small delay to avoid reading buttons too fast
}
// Initialize LCD display
void initializeLCD() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Place Phone ");
}
// Handle button presses and timer logic
void handleButtons(int currentIncreaseState, int currentDecreaseState, int currentStartResetState) {
handleTriplePressReset(currentStartResetState);
if (!isCountingDown) {
handleTimeAdjustment(currentIncreaseState, currentDecreaseState);
handleCountdownStart(currentStartResetState);
}
}
// Handle triple-press reset functionality
void handleTriplePressReset(int currentStartResetState) {
if (currentStartResetState == HIGH && lastStartResetButtonState == LOW) {
unsigned long currentTime = millis();
if (currentTime - lastButtonPressTime < TRIPLE_PRESS_WINDOW) {
buttonPressCount++;
if (buttonPressCount == 3) {
resetTimer();
}
} else {
buttonPressCount = 1;
}
lastButtonPressTime = currentTime;
}
}
// Handle time increase and decrease
void handleTimeAdjustment(int currentIncreaseState, int currentDecreaseState) {
if (currentIncreaseState == HIGH && lastIncreaseButtonState == LOW) {
timeInSeconds += THIRTY_MINUTES;
updateLCD();
}
if (currentDecreaseState == HIGH && lastDecreaseButtonState == LOW) {
if (timeInSeconds >= THIRTY_MINUTES) {
timeInSeconds -= THIRTY_MINUTES;
updateLCD();
}
}
}
// Handle countdown start
void handleCountdownStart(int currentStartResetState) {
if (currentStartResetState == HIGH && lastStartResetButtonState == LOW) {
if (timeInSeconds >= THIRTY_MINUTES && buttonPressCount == 1) {
startCountdown();
}
}
}
// Reset timer to 0 and update display
void resetTimer() {
timeInSeconds = 0;
isCountingDown = false;
updateLCD();
buttonPressCount = 0;
lcd.setCursor(0, 0);
lcd.print("Time Reset ");
delay(1000);
lcd.setCursor(0, 0);
lcd.print("Time: ");
}
// Start the countdown
void startCountdown() {
isCountingDown = true;
lastCountdownUpdate = millis();
}
// Update the countdown timer
void updateCountdown() {
unsigned long currentMillis = millis();
if (currentMillis - lastCountdownUpdate >= ONE_SECOND) {
timeInSeconds--;
updateLCD();
lastCountdownUpdate = currentMillis;
if (timeInSeconds <= 0) {
isCountingDown = false;
lcd.setCursor(0, 0);
lcd.print("Time's up!");
}
}
}
// Update the LCD display with current time
void updateLCD() {
int hours = timeInSeconds / 3600;
int minutes = (timeInSeconds % 3600) / 60;
int seconds = timeInSeconds % 60;
char timeString[9]; // HH:MM:SS format
sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds);
lcd.setCursor(0, 0);
lcd.print("Time: ");
lcd.setCursor(0, 1);
lcd.print(timeString);
}
// Check for phone presence using two force sensors
void checkPhonePresence() {
unsigned long currentMillis = millis();
if (currentMillis - lastPhoneCheckTime >= PHONE_CHECK_INTERVAL) {
lastPhoneCheckTime = currentMillis;
bool newPhoneDetected = false;
int totalForceValue = 0;
// Read both force sensors and sum their values
for (int i = 0; i < NUM_FORCE_SENSORS; i++) {
int forceValue = analogRead(FORCE_SENSOR_PINS[i]);
totalForceValue += forceValue;
Serial.print("Sensor ");
Serial.print(i);
Serial.print(": ");
Serial.println(forceValue);
}
// Determine if phone is detected based on total force
newPhoneDetected = (totalForceValue > (FORCE_THRESHOLD * NUM_FORCE_SENSORS));
if (newPhoneDetected != phoneDetected) {
phoneDetected = newPhoneDetected;
if (phoneDetected) {
Serial.println("Phone detected");
if (isCountingDown) {
lcd.setCursor(0, 0);
lcd.print("Time: ");
}
} else {
Serial.println("Phone not there!");
if (isCountingDown) {
lcd.setCursor(0, 0);
lcd.print("Jailbreak! ");
}
}
updateLCD(); // Update LCD to reflect the new state
}
Serial.print("Total Force: ");
Serial.println(totalForceValue);
}
}
// Control buzzer with varying frequency
void controlBuzzer() {
if (!phoneDetected && isCountingDown) {
unsigned long currentMillis = millis();
if (currentMillis - lastToneChange >= TONE_CHANGE_INTERVAL) {
lastToneChange = currentMillis;
currentTone += toneStep;
if (currentTone > MAX_TONE || currentTone < MIN_TONE) {
toneStep = -toneStep; // Reverse direction of frequency change
}
tone(BUZZER_PIN, currentTone);
}
} else {
noTone(BUZZER_PIN);
}
}
// Update button states for next iteration
void updateButtonStates(int currentIncreaseState, int currentDecreaseState, int currentStartResetState) {
lastIncreaseButtonState = currentIncreaseState;
lastDecreaseButtonState = currentDecreaseState;
lastStartResetButtonState = currentStartResetState;
}