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

  1. Raspberry Pi
  2. Raspberry Pi Camera & Lens
  3. Raspberry Pi Camera Board & Cable
  4. TFT Display
  5. Microphone
  6. IMU
  7. Power Supply
  8. Audio Module
  9. 2 LEDs
  10. Buttons
  11. Rectangular Wooden Frame
  12. Breadboard and Jumper Wires
  13. 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.

Button Press FSM

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;
  }
}

Built With

Share this project:

Updates