MakeMIT 2021 - Mask Manager
Shinjini Ghosh, Lay Jain, Pawan Goyal
Forgetting your mask :mask: every time you leave your room? Want a reminder if you are not masked? Mask Manager automatically detects if you are wearing your mask when you try to step out of your room, alerting you if not. Additionally, record interesting stats and perform select actions touchlessly if you want to - such as recording the time you spent outside or switching the light on/off by voice (say, if you are returning from outside or have touched the door handle and haven't washed your hands yet).
Hardware
Parts
- Raspberry Pi
- Raspberry Pi Camera & Lens
- Raspberry Pi Camera Board & Cable
- TFT Display
- Microphone
- IMU
- Power Supply
- Audio Module
- 2 LEDs
- Buttons
- Rectangular Wooden Frame
- Breadboard and Jumper Wires
- Screws and Fasteners
Wiring
The following is a schematic of how the different hardware components will be wired together in the final device. The device will then be sitting on a wooden board, which we can tape/hang/attach to the door or wall.
Camera & IMU Workings
The camera will track a person's motion, with help from the IMU, which can detect the door's opening/closing with help of its three sensors. Once the camera can zone in on a person, it will send the image over to a backend script, which uses deep learning and image detection techniques to identify whether or not the person is masked. If not masked, the TFT display will glow red and the audio output module or amplifier will play a warning tone or beep.
Statistics Recording
One can choose to start a timer by a button press or automatically when they are leaving their room, and the device continues until they receive a signal of the door opening again based on the IMU values, upon which the timer will be stopped and the data recorded. All the relevant information (duration, timestamps of leaving and returning, etc.) will be accessible online.
Touchless Actions
A few weekends ago, we created a voice-enabled smart switch Switch-It-Up, which is easily installable as well as portable. This switch can be installed as an extension to Mask Manager to enable turning on/off switches in the room touchlessly, presumably when people do not wish to touch them before they have washed their hands.
Power Module
Instead of being powered to a laptop, the Raspberry Pi can be used connected to a power bank, any USB switch, or any external power module, giving Mask Manager portability and ease of access.
Button Workings
We have two buttons on the device - primarily used as manual timer on/off buttons, but with software expansion, can be used for a combination of things (including brightness toggle if the switch allows for it), privacy mode, etc.
We define a button press as a full cycle of the button going from OPEN - CLOSED - OPEN, i.e., first pressing down and then releasing the button. However, just tracking a 0 to 1 state and vice-versa is not enough due to bouncing issues and hence, we use debouncing (for 10ms) on both ends. We also keep in a mechanism for differentiating short presses ($10$ ms - $1000$ ms) and long presses ($> 1000$ ms).
Here is an FSM denoting this working.
The code below implements this FSM.
#include <SPI.h>
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
const int BUTTON_PIN = 16;
class Button {
public:
uint32_t t_of_state_2;
uint32_t t_of_button_change;
uint32_t debounce_time;
uint32_t long_press_time;
uint8_t pin;
uint8_t flag;
bool button_pressed;
uint8_t state;
Button(int p) {
flag = 0;
state = 0;
pin = p;
t_of_state_2 = millis(); //init
t_of_button_change = millis(); //init
debounce_time = 10;
long_press_time = 1000;
button_pressed = 0;
}
void read() {
uint8_t button_state = digitalRead(pin);
button_pressed = !button_state;
}
int update() {
read();
flag = 0;
if (state == 0) { // Unpressed, rest state
if (button_pressed) {
state = 1;
t_of_button_change = millis();
}
} else if (state == 1) { //Tentative pressed
if (!button_pressed) {
state = 0;
t_of_button_change = millis();
} else if (millis() - t_of_button_change >= debounce_time) {
state = 2;
t_of_state_2 = millis();
}
} else if (state == 2) { // Short press
if (!button_pressed) {
state = 4;
t_of_button_change = millis();
} else if (millis() - t_of_state_2 >= long_press_time) {
state = 3;
}
} else if (state == 3) { //Long press
if (!button_pressed) {
state = 4;
t_of_button_change = millis();
}
} else if (state == 4) { //Tentative unpressed
if (button_pressed && millis() - t_of_state_2 < long_press_time) {
state = 2; // Unpress was temporary, return to short press
t_of_button_change = millis();
} else if (button_pressed && millis() - t_of_state_2 >= long_press_time) {
state = 3; // Unpress was temporary, return to long press
t_of_button_change = millis();
} else if (millis() - t_of_button_change >= debounce_time) { // A full button push is complete
state = 0;
if (millis() - t_of_state_2 < long_press_time) { // It is a short press
flag = 1;
} else { // It is a long press
flag = 2;
}
}
}
return flag;
}
};
Now, we can identify short presses and long presses. Here, button A corresponds to the ON button and button B corresponds to the OFF button. Pressing button A currently prints "Light turns ON" on the TFT display and pressing button B currently prints "Light turns OFF" on the TFT display. Once we have the motor parts, we can simply link the button presses to moving the motor in addition to printing on the TFT display. In the future, when we wish to extend the system to various brightness levels or mode toggling, we can utilize the long press option, which we have already developed and tested.
The code snippet below uses instances of the Button
class to implement our override switches. The current state of the light switch is displayed on the TFT display for want of an actuator in our possession currently.
Button button_1(BUTTON_PIN_1);
Button button_2(BUTTON_PIN_2);
void setup() {
Serial.begin(115200); // Set up serial port
pinMode(BUTTON_PIN_1, INPUT_PULLUP);
pinMode(BUTTON_PIN_2, INPUT_PULLUP);
tft.init();
tft.setRotation(2);
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
tft.setCursor(40, 60, 4);
tft.print("OFF");
}
void loop() {
uint8_t flag_1 = button_1.update();
uint8_t flag_2 = button_2.update();
if (bulb_state == 0 && (flag_2 == 1 || flag_2 == 2)){
tft.fillScreen(TFT_BLACK);
tft.setCursor(40, 60, 4);
tft.print("ON");
bulb_state = 1;
}
else if (bulb_state == 1 && (flag_1 == 1 || flag_1 == 2)){
tft.fillScreen(TFT_BLACK);
tft.setCursor(40, 60, 4);
tft.print("OFF");
bulb_state = 0;
}
}
Log in or sign up for Devpost to join the conversation.