Covid-19 continues to spread rapidly around the world.
Crowded places, shared spaces and high traffic areas are among the higher risk places for contracting the Coronavirus that causes COVID-19.
One of the places most at risk for the transmission of COVID-19 are bathrooms in public places (schools, universities, public libraries, museums, restaurants, coffee shops, gyms, hospitals, public transit, offices and workplaces).
Studies and research have shown that the virus spreads mainly by person-to-person contact, but it may spread also by tiny droplets that waft through the air and accumulate over time in indoor spaces. In addition, experts say that it may be possible to get COVID-19 by touching a contaminated surface or a contaminated object and then touching their own mouth, nose or eyes.
Surfaces frequently touched with hands are most likely to be contaminated. These include, among others: doorknobs, light switches, faucet handles, elevator buttons, handrails, etc.
We don’t know exactly how long the virus causing COVID-19 lives on different surfaces. However, evidence suggests it can live on objects and surfaces from a few hours to days, depending on the type of surface.
To reduce the possible contagion from surfaces and from shared spaces and high traffic areas it would be necessary to sanitize these places (in our case the public bathrooms) after each use. The best method to repeatedly sanitize an environment is by using UV-C rays.
My solutionThe device I have designed and made is based on M5Stack Core2 ESP32 IoT Development Kit for AWS IoT EduKit.
On port A (GPIO 33) of M5Stack Core2 I have connected the M5Stack PIR Sensor Human Body Infrared PIR Motion Sensor (AS312) to detect the presence of people in the bathroom.
On port C on GPIO 13 I have connected the SSR to control the UV-C lamp (to do the tests I have connected a led).
I have used the two SK6812 RGB LED bars on the M5Stack Core2 to visually signal the status of the bathroom: available (green), occupied (red) or in sanitization (blue).
The LED (near to the reset button) lights up when the M5Stack PIR Sensor detects the presence of people.
I have used the accelerometer to detect the opening of the door: in this case the UV-C lamps are turned off immediately.
The display shows each operation that is performed.
The main three states (bathroom available, bathroom occupied and bathroom in sanitization) are transmitted to the AWS cloud via an MQTT message.
I have created a cloud application on AWS IoT Events to turn off the lamps after some time (this time is calculated based on the distance of the lamps from the surface that need to be sanitized) by sending an MQTT message (LampOFF == true). In this way, if you move the lamps and modify the distance from the surface to be sanitized, you simply need to change the time of the timer in the Detector Model of IoT Events, without modifying the firmware inside the M5Stack Core2 ESP32 IoT Development Kit for AWS IoT EduKit
For safety I have set a timeout beyond which the M5Stack Core2 ESP32 IoT Development Kit for AWS IoT EduKit turns off the UV-C lamps (countLamp * 100ms). Furthermore, if someone open the bathroom door or a person is detected inside the bathroom by the PIR sensor, the lamps are immediately turned off and the device goes into the bathroom occupied state.
UV-C lamps and devices are not a new concept. They have been used for decades in healthcare applications. But now they may be a new option and a different solution to fight against COVID-19.
Studies and research have shown that Coronavirus is highly susceptible to ultraviolet light and that UV-C light can quickly inactivate it.
An important consideration concerns the UV-C dose required to inactivate the virus. Inactivation of viruses depends on some main factors related to each other: intensity of radiation, time and distance from the UV-C lamp.
At equal distance, the higher the UV-C intensity, the less time UV-C will take to kill pathogens like viruses and bacteria. At equal distance, lamps with a lower intensity will take a longer time to get the same level of disinfection. A higher-power UV-C unit may enable you sanitize a surface quicker than a low power unit (always at equal distance).
At equal time, surfaces further away from the UV-C lamp will not get the same level of disinfection as surfaces close to the lamp.
For example, in the case of a table (horizontal surface), if the lamp is vertical and is located at the beginning of the table, there will be an inactivation gradient: at equal time, at the edge the required UV-C dose will be lower than in the center or at the opposite end (because at the edge the intensity is greater).
Studies have shown that the required UV-C DOSE to inactivate the virus is 25mJ/cm^2.
For more information see this other project of mine, click here.
My device is equipped with a modular UV-C lamps system consisting of two 12W-UVC (36W) lamps. The module has a UV-C power at one meter of 191.08uW/cm^2. By increasing the number of modules, the UV-C power is increased and the time to reach the required UV-C DOSE is reduced.
If I use only two 12W-UV-C lamps at a distance of 1ft (about 0.3m), the dose of 25mJ/cm^2 is delivered in about 12 seconds; if I use two 12W-UV-C lamps at a distance of 6ft (about 1, 83m), the dose of 25mJ/cm^2 is delivered in about 7 minutes and 18 seconds. The device is modular, so you can add other lamps to halve the exposure times: if I use four 12W-UV-C lamps at a distance of 6ft (about 1, 83m), the dose of 25mJ/cm^2 is delivered in about 3 minutes and 39 seconds.
I have created a modular system in order to increase the UV-C power according to the surfaces that need to be sanitized.
The basic structure consists of two 36W Philips lamps, two 2G11 lamp holders, one ballast and one SSR (Solid State Relay).
The power on/off control is made via an SSR that controls the high voltage.
Lamps, ballast and SSR can work also at 110VAC (it will be necessary to buy them with this voltage).
For more information see this other project of mine, click here.
FirmwareI started learning how the "M5Stack Core2 ESP32 IoT Development Kit for AWS" works with the "AWS IoT EduKit tutorials" (you can find them here).
For my project, I have modified the smart thermostat tutorial. I removed what I didn't need and added the functions to control the M5Stack PIR Sensor and the accelerometer.
In the following figures you can see the flowcharts of all the tasks that I have implemented or modified.
pir_read_task
This function reads the GPIO_PIR pin. According to the values stored in the three variables bathroomAvailable, bathroomOccupied and bathroomSanitization, the function decides whether to indicate the bathroom as free, occupied or in sanitization and activates or deactivates the UV-C lamps via the GPIO_LAMP pin.
The countLamp variable defines the max duration of the sinification.
The delay is 100ms, so the timeout of sanitization will be countLamp * 100ms.
If during the sanification the IR sensor is activated, the sanitization is interrupted and the device goes into occupied bathroom mode.
countON variable is used to avoid false changes to the occupied bathroom state.
countOFF variable is used to avoid false changes to the sanitization phase.
void pir_read_task(){
bool readPIN = 0;
uint8_t countON = COUNTON_VALUE;
uint8_t countOFF = COUNTOFF_VALUE;
for(;;){
readPIN = Core2ForAWS_Port_Read(GPIO_PIR);
if(readPIN == 1)
{
Core2ForAWS_LED_Enable(1);
//ESP_LOGI(TAG, "Sensor plugged in == 1 (yes)");
}
else
{
Core2ForAWS_LED_Enable(0);
//ESP_LOGI(TAG, "Sensor plugged in == 0 (no)");
}
if((bathroomAvailable == true) && (bathroomOccupied == false) && (bathroomSanitization == false))
{
if(readPIN == 1)
{
if(countON == 0)
{
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0xff00000); // Red
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0xff0000);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = false;
bathroomOccupied = true;
bathroomSanitization = false;
countON = COUNTON_VALUE;
ui_textarea_add("\nBathroom Occupied!\n", NULL, 0);
ESP_LOGI(TAG, "bathroom Occupied ------------------------------------------------");
}
else
{
countON--;
//ESP_LOGI(TAG, "countON = %d", countON);
}
}
else countON = COUNTON_VALUE;
}
else if((bathroomAvailable == false) && (bathroomOccupied == true) && (bathroomSanitization == false))
{
if(readPIN == 0)
{
if(countOFF == 0)
{
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0x0000ff); // Blue
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0x0000ff);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = false;
bathroomOccupied = false;
bathroomSanitization = true;
countOFF = COUNTOFF_VALUE;
ui_textarea_add("\nBathroom Sanitization!\n", NULL, 0);
ESP_LOGI(TAG, "bathroom Sanitization ------------------------------------------------");
}
else
{
countOFF--;
//ESP_LOGI(TAG, "countOFF = %d", countOFF);
}
}
else countOFF = COUNTOFF_VALUE;
}
else if((bathroomAvailable == false) && (bathroomOccupied == false) && (bathroomSanitization == true))
{
if(readPIN == 1)
{
if(writePINOK == 1) Core2ForAWS_Port_Write(GPIO_LAMP, 0); // Lamp OFF -----------------------------------------
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0xff00000); // Red
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0xff0000);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = false;
bathroomOccupied = true;
bathroomSanitization = false;
countLamp = COUNTLAMP_VALUE;
ESP_LOGI(TAG, "LAMP OFF ------------------------------------------------");
ui_textarea_add("\nBathroom Occupied!\n", NULL, 0);
ESP_LOGI(TAG, "Bathroom Occupied! ------------------------------------------------");
}
else
{
if(writePINOK == 1) Core2ForAWS_Port_Write(GPIO_LAMP, 1); // Lamp ON -----------------------------------------
if(countLamp == 0)
{ // Timeout Lamp
if(writePINOK == 1) Core2ForAWS_Port_Write(GPIO_LAMP, 0); // Lamp OFF -----------------------------------------
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0x00ff00); // Green
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0x00ff00);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = true;
bathroomOccupied = false;
bathroomSanitization = false;
countLamp = COUNTLAMP_VALUE;
ui_textarea_add("\nBathroom Available!\n", NULL, 0);
ESP_LOGI(TAG, "bathroom Available ------------------------------------------------");
}
else
{
countLamp--;
ESP_LOGI(TAG, "countLamp = %d", countLamp);
}
}
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
accel_task
This function reads the 6-axis Inertial Measurement Unit (IMU). If the acceleration on the y axis is higher than the value stored in the sensibility variable and the sanitization is active, then the sanitization is deactivated via the GPIO_LAMP pin and the device switches to bathroom occupied mode.
static void accel_task(void* pvParameters)
{
float accel_x = 0.00;
float accel_y = 0.00;
float accel_z = 0.00;
float sensibility = 0.15;
for(;;)
{
MPU6886_GetAccelData(&accel_x, &accel_y, &accel_z);
if(accel_z > sensibility)
{
ESP_LOGI(TAG, "Porta aperta! | X: %.2f -- Y; %.2f -- Z: %.2f", accel_x, accel_y, accel_z);
if((bathroomAvailable == false) && (bathroomOccupied == false) && (bathroomSanitization == true))
{
if(writePINOK == 1) Core2ForAWS_Port_Write(GPIO_LAMP, 0); // Lamp OFF -----------------------------------------
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0xff00000); // Red
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0xff0000);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = false;
bathroomOccupied = true;
bathroomSanitization = false;
countLamp = COUNTLAMP_VALUE;
ESP_LOGI(TAG, "LAMP OFF ------------------------------------------------");
ui_textarea_add("\nBathroom: Door is open!\n", NULL, 0);
ui_textarea_add("\nBathroom Occupied!\n", NULL, 0);
}
}
vTaskDelay(pdMS_TO_TICKS(100));
}
vTaskDelete(NULL); /* Should never get to here... */
}
LampOFF_Callback
This callback deactivates the UV-C lamps and brings the device into the bathroom available condition when it receives LampOFF == true and the device is in the sanitization phase.
void LampOFF_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext)
{
IOT_UNUSED(pJsonString);
IOT_UNUSED(JsonStringDataLen);
bool * statusLamp = (bool *) (pContext->pData);
if(pContext != NULL) {
ESP_LOGI(TAG, "Delta - Sanitization state changed to %d ", *statusLamp);
}
if((*statusLamp == 1) && (bathroomAvailable == false) && (bathroomOccupied == false) && (bathroomSanitization == true))
{
if(writePINOK == 1) Core2ForAWS_Port_Write(GPIO_LAMP, 0); // Lamp OFF -----------------------------------------
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_LEFT , 0x00ff00); // Green
Core2ForAWS_Sk6812_SetSideColor ( SK6812_SIDE_RIGHT , 0x00ff00);
Core2ForAWS_Sk6812_Show ();
bathroomAvailable = true;
bathroomOccupied = false;
bathroomSanitization = false;
countLamp = COUNTLAMP_VALUE;
LampOFF = false;
ui_textarea_add("\nBathroom Available!\n", NULL, 0);
ESP_LOGI(TAG, "bathroom Available ------------------------------------------------");
}
}
The values of the variables countON, countOFF, countLamp and the timer of the Detector Model are test values and depend on how the M5Stack Core 2, PIR sensor and UVC lamps are positioned.
I have attached all the code.
AWSIn my AWS cloud application I have used IoT Core and IoT Events.
For the configuration I followed the tutorials that you can find here.
The hardest part is building the cloud application through IoT Events.
First I have created a new Input to IoT Events:
- Go to the IoT Events management console, choose Inputs and then Create Input;
- Give your input a name, for example Sanitizing;
- Now you need to upload a JSON file to define the schema. Click on Upload file and select the input.json file located in your project folder;
{
"current": {
"state": {
"reported": {
"bathroomAvailable": true,
"bathroomOccupied": false,
"bathroomSanitization": false,
"LampOFF": true
}
},
"version": 13
},
"timestamp": 1606282489
}
- Then click on Create.
In IoT Core I have created a new rule that will forward device shadow updates to it and which you will use to create the control application:
In the IoT core menu click on Execution of actions -> Rules -> Create a rule.
Give your rule a name and description, for example SanitizingRule.
Use this statement for your query and be sure to replace CLIENT_ID with your device's client ID / serial number:
SELECT current.state as current.state, current.version as current.version, timestamp FROM '$aws/things/CLIENT_ID/shadow/update/documents'
Choose Add Action.
Select Send a message to an IoT Events Input and then click Configure operation.
For Input Name, find the name of the input resource created in the previous step of the IoT Events console. The name I used is Sanitizing.
For Role, choose Create Role, name your role (example SanitizingRole), then choose Create role to finalize your new IAM role that gives permission to IoT Core to send messages to IoT Events.
Choose Add Action to finalize the new rule action. You should be returned to the rule creation form.
Choose Create rule to finish creating the rule.
The rule is configured to receive the full JSON document whenever the Smart-Sanitizing device shadow is updated, and then to forward it to my IoT Sanitizing Events Input. The Input is configured to parse only a few of the fields from the device shadow document and will discard the unneeded ones.
IoT Events: creating the cloud application
I have created a detector model that processes the device shadow messages forwarded by the SanitizingRule rule and that evaluates whether it should send the MQTT message to deactivate the UV-C lamps.
Here is the preview of the detector model I created:
(Attached is the detector model JSON file)Click on Publish.Give the model a name, for example SanitizationModel and select a role.Click on Save and publish.
For further information, click here.
Future evolutionIf there are more bathrooms, multiple devices can be used to sanitize all them.
It is possible to use other M5Stack Core2 ESP32 IoT Development Kits for AWS IoT EduKit in order to create some devices able to view the status of the toilets from afar: available, occupied or in sanitization.
This device can also be used wherever an automatic periodic sanitization is required.
That's all!If you have any questions or suggestions don't hesitate to leave a comment below.
CreditsI thank my wife Giovanna ♥ for the voiceover narration in all the videos.
Comments