Overview
For our final project, we collaborated with CMU's Facilities Management Services to create a device that would help them with their work. We created a device for our client, Mark Jones, who works as a plumber in FMS. After working closely with Mark and learning about his day-to-day routines at work, we decided to create an enhanced barrier to support him and his team when they are working on maintenance and repairs.
What We Built
Our group built an enhanced barrier to prevent entry into bathrooms when the plumbing staff is working. We designed a two-part system that collapses down to a small size for transport. Sensors on the outside of the door trigger when someone approaches the door, and it plays a sound saying "bathroom closed, do not enter". A flashing LED on the end of the barrier also flashes. When someone approaches the barrier from the inside, the physical barrier opens, and the alarm sensing is deactivated. If someone needs to enter the front of the barrier without the alarm going off, they can scan their ID on a reader, and the barrier will open, and the alarm will be deactivated.
Our final collapsible barriers in use blocking a door
Video shows the barrier in use under all use cases
Mark the plumber needs to replace a pipe in the women's bathroom. He is nervous about doing so because people often ignore the bathroom is closed sign he puts up outside. The team is short-staffed, so he cannot bring another person to stand at the door while he does his work. Instead, he brings his smart barrier system, which has a physical barrier and plays sound to get people's attention and make sure they do not enter the bathroom. Mark is able to get his work done without worrying that people will enter the bathroom.
Close-up image on our 3D printed collapsible structure on one of the stools with a green speaker cap.
Detailed image of our white 3D printed collapsible structure being fully stretched out.
The sensor being depicted as being installed in our collapsible structure inside a slot.
Close-up of our green collapsible structure mounted on top of our commercially purchased stool.
How We Got Here
Prototype
To begin, we created two prototypes to answer two questions we had about the product. First, we made a prototype to look at the form and collapsibility of the nodes. We focused on curating a 3D print that captured the ease of usage we wanted for the device, and we tested that out by printing the collapsible device on the left. After the print came out and we played around with it, we decided to move forward with a structure that contained the L-shaped slots. The second prototype we made was to answer the question of the features that would be useful in the design. For instance, some of the considerations were the intensity of the light & radio modules, the implementation of the barriers, and an RFID tag for the plumbers to deactivate the system as the go in and out of the bathroom.
Collapsed 3D printed telescopic stick for a compact design.
3D printed telescopic stick (stretched out) that we wanted to act as physical barriers on each side of the device.
3D printed collapsible structure for testing with size and form.
The image above shows an initial prototype of one of the nodes of the barrier with the RFID reader, 2 IR sensors, and a LED
Video shows the system entering the different states of the system to demonstrate functionality
To start the project, I was originally planning to use break beam sensors. This would not have actually worked for our project because there was no way to detect someone approaching the barrier only if they crossed it.
We were inspired by some collaspible stool models on the internet to create this L-shaped interlocking mechanism. After dragging one layer up and turning leftwards, the pin locks the layer in place. Creating this SolidWorks model was especially tricky because we had to cut out the L shape repetitively but making sure the pins align.
After creating our prototype, we were able to give Mark a better idea of the device we were making. He was very happy with both of our prototypes, and thought that the collapsible design was something he thought could be very useful for transporting the device. For the functionality, he was happy with everything we were doing and could not think of anything else that he wanted. He was happy that we had thought through all of the use cases, such as him leaving the bathroom and coming back in. At this point in the project, we had been following our original schedule to a pretty decent extent and have kept up with the building process.
Most of the feedback we received were clarifications about the size of the design now that Mark, our client, had more of an idea of what it was going to look like. He asked that we make it taller than we were planning because he wanted the physical barrier to be more at eye level. Although we did make it taller than we originally planned, we did not make it all the way to eye level because the barrier bar would not go straight horizontally, so it gave height to the system itself.
Process
Image shows inital testing of the mp3 player with the speaker on the breadboard
Collapsable housing with the IR sensors mounted inside
Reprinted the top of the mount to provide more room for electronics. Debugged sets of electronics and ready for mounting.
Video shows the system going through all of the states on each node while they are connected with the radio module
Video shows the RFID sensor working through the side of the housing
Video shows the system working through all of the states in each housing
We realized that the stool requires us to have access to both sides' holes to pull it out. Therefore, we had to make our 3D print part detachable. This ring was used to connect the 3D print parts with the existing stool. Magenets are meant to be put inside the four holes.
To make it easiser to pull out the 3D print part, we added little handles on both sides so that there's something to grab on.
In the previous model, due to miscommunication, we only cut out one hole for the sensor. This updated model has two neighboring slots for the sensor, and a rectangular slot for the servo motor. In addition to that, we enlarged the hexagon's surface area so that there's enough space to mount everything on.
Holes were cut on top of the hexagon to allow speaker output.
Conclusions & Lessons Learned
In essence, we learned a lot about designing features and housing catered to a client's wants and needs. Our final crit gave us a lot of insight into the work we had done, especially with the feedback we had gotten. Though our device wasn't fully done, Mark seemed quite happy with the results and he looks forward to when we can hand off the device to him. Other reviewers commented a lot on how the housing's collapsible structure was "interesting and unique", and they enjoyed playing around with the collapsible stools. Moreover, one of the reviewers mentioned how our device "could be used anywhere when maintenance needs an area to be blocked off, not just the bathroom". We appreciated getting feedback that spoke on the versatility and consideration that went into our design. From our final crit, we learned that the construction and integration of the design is more complicated than it seems. Since we had issues with our 3D printer and parts were not done when expected a limited amount of time leads to things not working and no time to debug. We will move forward by reprinting a couple of the components and making sure that integration goes smoothly before the final hand off.
In addition, this project really emphasized the importance of iterative prototyping and testing for us. Through our two prototype stages, we were able to better understand the physical structure and functional features that would make the device effective for our client. Our final collapsible structures design was a direct result of multiple rounds of testing and interacting with the printed model. Along the way, we also learned about the integration of electronics with the housing and ensuring all components are accounted for in the design. Similarly, experimenting with sensors, alarms, and RFID integration allowed us to think more critically about real-world use cases, such as authorized entry and conditioned alarm triggers.
Working with Mark also gave us a lot of insight as to how we approach thinking about the device versus how our client would think about it. His input on the functionality and appearance of the barriers helped us refine our design to better match his practical needs in a real work environment. In particular, he focused more on the ease of use and reliability of the system, pushing us to think about how intuitive the device would be for someone using it in a work environment that is usually fast-paced and urgent. Overall, we all enjoyed working with Mark; he was very clear with his wants and needs regarding the project and always supported our process.
Block Diagram & Schematic
Code
Node 1 Code:
/*
Final Project: Enhanced Barrier
60-223 Intro to Physical Computing
PROJECT SUMMARY: This device is an enhanced barrier that would prevent entry into bathrooms while the maintenance
staff is working. There are servos triggering two physical barriers, LEDs, and a speaker. There is also an RFID tag reader to allow staff to enter without triggering the alarm.
Pin mapping:
Arduino pin | role | description
-------------------------------------
A0 in/out alarm sensor
A1 in/out disable sensor
5 output servo pin
6 output TX DFPlayer
7 output RX DFPlayer
8 output LED pin
9 output CE radio module
10 output CSN radio module
11 in/out MOSI radio module
12 in/out MISO rado module
13 output SCK radio module
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Servo.h>
// ---------------- RF ----------------
RF24 radio(9, 10);
const byte txAddress[6] = "00001";
const byte rxAddress[6] = "00002";
// ---------------- DFPLAYER ----------------
SoftwareSerial mp3(6, 7);
DFRobotDFPlayerMini player;
// ---------------- PINS ----------------
const int alarmSensor = A0;
const int disableSensor = A1;
const int ledPin = 8;
const int servoPin = 5;
Servo myServo;
// ---------------- SETTINGS ----------------
const int threshold = 120;
const unsigned long lockTime = 5000;
const unsigned long windowTime = 100;
// ---------------- STATE ----------------
enum Mode { NONE, ALARM, DISABLE };
Mode activeMode = NONE;
unsigned long modeStart = 0;
// ---------------- TRIGGER TRACKING ----------------
int alarmHits = 0;
int disableHits = 0;
unsigned long firstAlarmHit = 0;
unsigned long firstDisableHit = 0;
// ---------------- SEND STATE ----------------
void sendState(char s) {
radio.stopListening();
radio.writeFast(&s, sizeof(s));
radio.txStandBy();
radio.startListening();
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
myServo.attach(servoPin);
myServo.write(45);
mp3.begin(9600);
delay(1500);
if (!player.begin(mp3)) {
Serial.println("DFPlayer FAIL");
while (true);
}
player.volume(25);
radio.begin();
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_250KBPS);
radio.setAutoAck(false);
radio.setRetries(0, 0);
radio.openWritingPipe(txAddress);
radio.openReadingPipe(1, rxAddress);
radio.startListening();
Serial.println(radio.isChipConnected() ? "Radio connected" : "Radio NOT connected");
Serial.println("NODE 1 READY");
}
// =====================================================
// LOOP
// =====================================================
void loop() {
int alarmValue = analogRead(alarmSensor);
int disableValue = analogRead(disableSensor);
Serial.print("Alarm: ");
Serial.print(alarmValue);
Serial.print(" | Disable: ");
Serial.println(disableValue);
bool alarmState = alarmValue > threshold;
bool disableState = disableValue > threshold;
unsigned long now = millis();
// =====================================================
// IDLE — WATCH FOR TRIGGERS
// =====================================================
if (activeMode == NONE) {
// ---------- ALARM ----------
if (alarmState) {
if (alarmHits == 0) {
alarmHits = 1;
firstAlarmHit = now;
Serial.println("Alarm hit 1");
} else if (alarmHits == 1 && now - firstAlarmHit <= windowTime) {
alarmHits = 0;
activeMode = ALARM;
modeStart = now;
disableHits = 0;
Serial.println("LOCAL ALARM");
player.play(1);
sendState('A');
} else if (alarmHits == 1 && now - firstAlarmHit > windowTime) {
alarmHits = 1;
firstAlarmHit = now;
Serial.println("Alarm hit reset");
}
} else {
alarmHits = 0;
}
// ---------- DISABLE ----------
if (disableState) {
if (disableHits == 0) {
disableHits = 1;
firstDisableHit = now;
Serial.println("Disable hit 1");
} else if (disableHits == 1 && now - firstDisableHit <= windowTime) {
disableHits = 0;
activeMode = DISABLE;
modeStart = now;
alarmHits = 0;
Serial.println("LOCAL DISABLE");
myServo.write(90);
sendState('D');
} else if (disableHits == 1 && now - firstDisableHit > windowTime) {
disableHits = 1;
firstDisableHit = now;
Serial.println("Disable hit reset");
}
} else {
disableHits = 0;
}
// ---------- RF receive ----------
if (radio.available()) {
char s;
radio.read(&s, sizeof(s));
if (s == 'A' || s == 'D' || s == 'N') {
Serial.print("Received: "); Serial.println(s);
if (s == 'A') { activeMode = ALARM; modeStart = now; player.play(1); }
if (s == 'D') { activeMode = DISABLE; modeStart = now; myServo.write(90); }
}
}
}
// =====================================================
// ALARM MODE
// =====================================================
if (activeMode == ALARM) {
digitalWrite(ledPin, (millis() / 250) % 2);
if (millis() - modeStart >= lockTime) {
activeMode = NONE;
digitalWrite(ledPin, LOW);
player.stop();
myServo.write(45);
sendState('N');
Serial.println("ALARM END");
}
}
// =====================================================
// DISABLE MODE
// =====================================================
else if (activeMode == DISABLE) {
if (millis() - modeStart >= lockTime) {
activeMode = NONE;
myServo.write(45);
sendState('N');
Serial.println("DISABLE END");
}
}
delay(50);
}
Node 2 Code:
/*
Final Project: Enhanced Barrier
60-223 Intro to Physical Computing
PROJECT SUMMARY: This device is an enhanced barrier that would prevent entry into bathrooms while the maintenance
staff is working. There are servos triggering two physical barriers, LEDs, and a speaker. There is also an RFID tag
reader to allow staff to enter without triggering the alarm.
Pin mapping:
Arduino pin | role | description
-------------------------------------
A0 in/out alarm sensor
A1 in/out disable sensor
5 output servo pin
6 output CE radio module
7 output CSN radio module
8 output LED pin
9 output RST RFID
10 in/out SDA RFID
11 in/out MOSI radio module and RFID
12 in/out MISO rado module and RFID
13 output SCK radio module and RFID
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <MFRC522.h>
#include <Servo.h>
// ---------------- RF ----------------
RF24 radio(6, 7);
const byte txAddress[6] = "00002";
const byte rxAddress[6] = "00001";
// ---------------- RFID ----------------
#define SS_PIN 10
#define RST_PIN 9
MFRC522 rfid(SS_PIN, RST_PIN);
// ---------------- PINS ----------------
const int alarmSensor = A0;
const int disableSensor = A1;
const int ledPin = 8;
const int servoPin = 5;
Servo myServo;
// ---------------- SERVO POSITIONS ----------------
const int SERVO_OPEN = 45; // barrier open
const int SERVO_CLOSED = 90; // barrier closed
// ---------------- SETTINGS ----------------
const int threshold = 120;
const unsigned long lockTime = 5000;
const unsigned long windowTime = 500;
// ---------------- STATE ----------------
enum Mode { NONE, ALARM, DISABLE };
Mode activeMode = NONE;
unsigned long modeStart = 0;
// ---------------- TRIGGER TRACKING ----------------
int alarmHits = 0;
int disableHits = 0;
unsigned long firstAlarmHit = 0;
unsigned long firstDisableHit = 0;
// ---------------- SEND STATE ----------------
void sendState(char s) {
radio.stopListening();
radio.writeFast(&s, sizeof(s));
radio.txStandBy();
radio.startListening();
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
myServo.attach(servoPin);
myServo.write(SERVO_CLOSED); // start closed
SPI.begin();
rfid.PCD_Init();
radio.begin();
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_250KBPS);
radio.setAutoAck(false);
radio.setRetries(0, 0);
radio.openWritingPipe(txAddress);
radio.openReadingPipe(1, rxAddress);
radio.startListening();
Serial.println("NODE 2 READY");
}
// =====================================================
// LOOP
// =====================================================
void loop() {
int alarmValue = analogRead(alarmSensor);
int disableValue = analogRead(disableSensor);
Serial.print("Alarm: ");
Serial.print(alarmValue);
Serial.print(" | Disable: ");
Serial.println(disableValue);
bool alarmState = alarmValue > threshold;
bool disableState = disableValue > threshold;
unsigned long now = millis();
// =====================================================
// IDLE — WATCH FOR TRIGGERS
// =====================================================
if (activeMode == NONE) {
// ---------- ALARM ----------
if (alarmState) {
if (alarmHits == 0) {
alarmHits = 1;
firstAlarmHit = now;
Serial.println("Alarm hit 1");
} else if (alarmHits == 1 && now - firstAlarmHit <= windowTime) {
alarmHits = 0;
activeMode = ALARM;
modeStart = now;
disableHits = 0;
Serial.println("LOCAL ALARM");
myServo.write(SERVO_CLOSED);
sendState('A');
}
} else {
if (alarmHits == 1 && now - firstAlarmHit > windowTime) {
alarmHits = 0;
Serial.println("Alarm hit expired");
}
}
// ---------- DISABLE ----------
if (disableState) {
if (disableHits == 0) {
disableHits = 1;
firstDisableHit = now;
Serial.println("Disable hit 1");
} else if (disableHits == 1 && now - firstDisableHit <= windowTime) {
disableHits = 0;
activeMode = DISABLE;
modeStart = now;
alarmHits = 0;
Serial.println("LOCAL DISABLE");
myServo.write(SERVO_OPEN);
sendState('D');
}
} else {
if (disableHits == 1 && now - firstDisableHit > windowTime) {
disableHits = 0;
Serial.println("Disable hit expired");
}
}
// ---------- RFID ----------
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
Serial.println("RFID DISABLE");
activeMode = DISABLE;
modeStart = now;
alarmHits = 0;
disableHits = 0;
myServo.write(SERVO_OPEN);
sendState('D');
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
// ---------- RF receive ----------
if (radio.available()) {
char s;
radio.read(&s, sizeof(s));
Serial.print("Received: "); Serial.println(s);
if (s == 'A') {
activeMode = ALARM;
modeStart = now;
myServo.write(SERVO_CLOSED);
}
if (s == 'D') {
activeMode = DISABLE;
modeStart = now;
myServo.write(SERVO_OPEN);
}
if (s == 'N') {
activeMode = NONE;
myServo.write(SERVO_CLOSED);
}
}
}
// =====================================================
// ALARM MODE
// =====================================================
if (activeMode == ALARM) {
digitalWrite(ledPin, (millis() / 250) % 2);
myServo.write(SERVO_CLOSED); // enforce closed
if (millis() - modeStart >= lockTime) {
activeMode = NONE;
digitalWrite(ledPin, LOW);
myServo.write(SERVO_CLOSED);
sendState('N');
Serial.println("ALARM END");
}
}
// =====================================================
// DISABLE MODE
// =====================================================
else if (activeMode == DISABLE) {
myServo.write(SERVO_OPEN); // enforce open
if (millis() - modeStart >= lockTime) {
activeMode = NONE;
myServo.write(SERVO_CLOSED); // FIXED
sendState('N');
Serial.println("DISABLE END");
}
}
delay(50);
}