Final Project: Doorbell

Intro:

Demo gif of Doorbell device's lights flashing

This is what the Doorbell device looks like while on, with the lights and buzzer (not audible) both running.


For my final project in HCDE 439, I designed and built an alarm device called Doorbell. This device is designed to recognize movement by using an ultrasonic sensor which can detect changes in distance, and then triggers a toggleable alarm and light response. The alerts can be toggled by pressing the "M" key on the connected computer. I started this project as a basic alarm device, and realized that it could theoretically be used for various applications that need me to be aware of motion or changing distances.


Project video demo


Implementation:

Circuitry:

Circuit diagram for my project This diagram represents the device's circuitry. The ultrasonic sensor is connected to digital pins to send data to the Arduino, which in turn controls the alarm lights and buzzer. The green LEDs are controlled by a digital output which turns on or off, and have an ideal maximum current of ~20 mA. Using Ohm's law to calculate an ideal resistance gets the following value:
5V - 1.8V / 20 mA = 160 Ω
I used a 220 Ω resistor, which had the closest available value to the ideal resistance. A separate digital output is used to activate the active buzzer.

Side view of circuit on a breadboard Top view of circuit on a breadboard
Photos of the actual device internals. I used the small breadboard to hold up the ultrasonic sensor and had the LEDs/buzzer attached to the large breadboard. To connect the device to my computer, I used a 10-foot cable after I realized that I wanted to be able to place it further from me or the computer.

Container design:

3D model of the container from the front 3D model of the container from the back
I used SolidWorks to design the outer container for my device, and 3D printed it from PLA filament. It has holes for the ultrasonic sensor and the LEDs as well as an open back for inserting the components. There is also a small step inside the container for the ultrasonic sensor to be placed on, so that there's some elevation from the floor. The overall footprint of the device is 4.5 in by 2 in by 6.5 in. 3D-printed, rectangular device container This is what the printed device looks like with the components inside.


Code:

Most of the functionality for my device was implemented in code on the Arduino, which is responsible for controlling the sensor and LEDs. I added computer control as a way to control the alarm function. When the device is first set up, it enters a short calibration period where it saves the nearest detected distance and uses that as the reference "alarm distance". Afterwards, if anything comes closer than the alarm distance, the device will trigger its buzzer and LEDs. At the same time, if the mute key on the keyboard is pressed, that will toggle the functioning of the LEDs and buzzer altogether.


Arduino code:
 
    const int xPin = A1; // Identifies the X-axis pin
    const int yPin = A0; // Identifies the Y-axis pin
    const int ledPin = 8; // Identifies the blue LED pin
    const int buzzer = 7; // Identifies the buzzer pin
    const int trig = 2; // Identifies the sensor "trig" pin
    const int echo = 3; // Identifies the sensor "echo" pin
    bool mute = true; // Creates a variable for "muting" the system
    bool manualMute = false; // Creates a variable for manual mute control
    int triggerDist = 0; // Initialize the trigger distance at 0 in

    void setup() { // Setup
        Serial.begin(9600); // Sets baud rate for serial usage
        pinMode(ledPin, OUTPUT); // Sets the blue LED pin for output
        pinMode(buzzer, OUTPUT); // Sets the buzzer pin for output
        pinMode(trig, OUTPUT); // Sets the trig pin for output (sending signals)
        pinMode(echo, INPUT); // Sets the echo pin for input (receiving signals)
        delay(200); // Wait 200ms for sensors
        while(millis() < 3000) { // 3-second calibration period
            digitalWrite(trig, LOW); // Deactivate the sensor
            delay(5); // Wait 5 ms
            digitalWrite(trig, HIGH); // Send a ping
            delay(5); // Wait 5 ms
            digitalWrite(trig, LOW); // Deactivate the sensor
            long calibDuration, calibInches; // Set up calibration measurement units
            calibDuration = pulseIn(echo, HIGH); // Measure the sensor ping
            calibInches = calibDuration / 74 / 2; // Convert the duration to a distance in inches
            if (calibInches > triggerDist) { // If current distance is larger than trigger distance
                triggerDist = calibInches; // Set trigger distance to current
            }
        }
        triggerDist--; // Set trigger distance to measured - 1 inch
    }

    void loop() { // Main loop
        if (Serial.available() > 0) { // If there is serial output
            int inByte = Serial.read(); // Reads from the port
            if (inByte == 1) { // If "1" is received
                manualMute = !manualMute; // Toggle manual mute control
            }
        }
        if (mute) { // If mute is true
            digitalWrite(buzzer, LOW); // Set buzzer to low
            digitalWrite(ledPin, LOW); // Set LED to high
            digitalWrite(trig, LOW); // Deactivate the sensor
            delay(5); // Wait 5 ms
            digitalWrite(trig, HIGH); // Send a ping
            delay(5); // Wait 5 ms
            digitalWrite(trig, LOW); // Deactivate the sensor
            long duration, inches; // Setup distance measurements
            duration = pulseIn(echo, HIGH); // Measure the sensor ping
            inches = duration / 74 / 2; // Convert the duration to a distance in inches
            if (!manualMute) { // If manual mute control is off
                if (inches < triggerDist) { // If the distance is less than the trigger range
                    mute = false; // Sets mute to false
                } else { // If the distance is outside the trigger range
                    mute = true; // Sets mute to true
                }
            }
            Serial.println(1); // Prints to serial
            delay(50); // Wait 50 ms
        } else { // If mute is false
            Serial.println(2); // Prints new output to serial
            digitalWrite(buzzer, LOW); // Sets buzzer to low
            digitalWrite(ledPin, LOW); // Sets LEDs to low
            delay(100); // Waits 100 ms
            digitalWrite(ledPin, HIGH); // Sets LEDs to high
            delay(100); // Waits 100 ms
            digitalWrite(buzzer, HIGH); // Sets buzzer to high
            digitalWrite(ledPin, LOW); // Sets LEDs to low
            delay(100); // Waits 100 ms
            digitalWrite(ledPin, HIGH); // Sets LEDs to high
            delay(100); // Waits 100 ms
            if (manualMute) { // If manualMute is true
                mute = true; // Set mute to true
            } else { // If manualMute is not true
                mute = false; // Set mute to false
            }
        }
    }
            

p5.js code:
 
    // Final project for HCDE 439 AUT 25
    // This code is partly based on the in-class p5.js demos
    const BAUD_RATE = 9600; // Sets up baud rate for serial usage
    let port, connectBtn, elementSize; // Declare global variables

    // Sets up the canvas
    function setup() {
        setupSerial(); // Runs the serial setup function
        createCanvas(windowWidth, windowHeight); // Creates a canvas sized to the browser window
        textFont("system-ui", 50); // Sets the font (https://p5js.org/reference/)
        textStyle(BOLD); // Sets the font style to bold
        textAlign(CENTER, CENTER); // Sets text center alignment
        blendMode(ADD); // Sets canvas blend mode (https://p5js.org/reference/#/p5/blendMode)
    }

    // Creates the visuals
    function draw() {
        const portIsOpen = checkPort(); // Check whether the port is open (see checkPort function below)
        if (!portIsOpen) return; // Return (exit the loop) if the port isn't open
        let str = port.readUntil("\n"); // Read from the port until reaching a new line
        if (str.length == 0) return; // Return if nothing is read
        let inputStr = str.trim();
        elementSize = min(windowWidth, windowHeight) * 0.3; // Limit size of the main elements

        clear(); // Clear the canvas
        if (inputStr == "1") { // Input is 1 if sensor is not triggered
            background(0); // Black background
        } else { // If the sensor is triggered
            background("red"); // Red background
        }
        translate(windowWidth / 2, windowHeight / 2); // Move the origin to the center
        fill("white"); // Sets color for text/objects
        text("Press M key to toggle mute!", 0, 0); // Text for 
    }

    function keyTyped() { // If a character key is pressed
        if (key === "m") { // If the M key is pressed
            port.write(1); // Write "1" to the port
        }
    }

    // Helper function to set up the serial
    function setupSerial() {
        port = createSerial(); // Sets up the port
        let usedPorts = usedSerialPorts(); // Getting the used ports
        if (usedPorts.length > 0) { // Checks if there are any used ports
            port.open(usedPorts[0], BAUD_RATE); // Opens the first used port
        }
        connectBtn = createButton("Connect to Arduino"); // Creates a connect button
        connectBtn.position(5, 5); // Positions the button in the top left of the screen
        connectBtn.mouseClicked(onConnectButtonClicked); // On click, runs onConnectButtonClicked
    }

    // Changes the button appearance and returns a value based on the status of the port
    function checkPort() {
        if (!port.opened()) { // If the port is not opened
            connectBtn.html("Connect to Arduino"); // Modifies the button text
            background("gray"); // Sets the background color to gray
            return false; // Returns false
        } else { // If the port is opened
            connectBtn.html("Disconnect"); // Modifies the button text
            return true; // Returns true
        }
    }

    // Toggles opening the port when the connect button is clicked
    function onConnectButtonClicked() {
        if (!port.opened()) { // If the port is not opened
            port.open(BAUD_RATE); // Opens the port
        } else { // If the port is opened
            port.close(); // Closes the port
        }
    }
            

Closing thoughts:

Looking ahead to the future, I identified a few areas where I felt there was room for improvement or flaws that needed fixing. While I'm happy with how the functionality turned out, something that I'm interested in changing is fully removing the need for a computer. For power, this would mean adding a battery or a separate power cable. Adding an actual mute button would also be needed, since I wouldn't have access to a keyboard. Additionally, I'd like to continue iterating on the design of the container. For instance, I found that in the 3D-printed version there wasn't much need for a cable notch and some of the parts were more cramped that I expected them to be. I hope to use this device practically around my house soon.

Click to go back to homepage