Problem
With quarantine, I have not been leaving the house all that much, so I seem to have lost my intuition about temperature and my habit of always checking the weather in the morning. This has led me to be surprised by how hot/cold it gets if I’ve gone on a long walk or gone grocery shopping as well as when it starts raining before I’ve gotten home. I’ve found myself really cold in just a sweatshirt in the evening, hot when I wore thick sweatpants on a day that became unusually warm, and caught in the rain unprepared in the infrequent times I’ve been outside for an hour or two this semester. This led me to the idea of a system that when I leave the house, would let me know about weather changes in the 2 following hours without bothering me.
Proposed Solution
To solve this problem, I propose a small device that can be placed on the wall near the front door that will detect when you leave the house and give notifications appropriate to the current weather and the changes. The opening of the door is detected with a magnetic reed switch. Whether you are entering or exiting the house is decided based on if the PIR motion sensor detects motion right before the door opens. If you are entering the house, motion will be detected. Notifications will only be given when you are leaving the house, so when no motion is detected. This device uses an esp32 board (I chose the Adafruit Huzzah32) to get the current and hourly weather from the OpenWeather API over WiFi. The current temperature and weather condition as well as that of the next 2 hours are taken from the fetched information. If it will rain or snow, and it isn’t already raining or snowing, the buzzer will play a specific tone sequence 3 times as the door is opening. I chose a sequence that sounds to me a bit like “oh hooray” when it will snow because I love snow. If it will rain, I chose a more warning sound alert because it’s usually not fun to get caught in the rain. The main reason I chose to not give an alert if it will snow/rain when it already is is because you can clearly see what’s happening and should be prepared for it to keep raining/snowing. The buzzer was chosen rather than a speaker because it was loud enough to catch my attention easily, but unlike the speaker it isn’t loud enough to be annoying. I chose to have the LED strip light up to let you know it will be colder or hotter by 5 or more degrees Fahrenheit as that’s when the temperature change bothers me. Red corresponds to hotter and blue to colder. The LED strip will flash for 2 seconds once the door closes to get your attention and then for another 5 seconds it will stay that color so that you can still see the alert but not get annoyed by it as you are leaving. In the next section, you can see demo videos of the cases for this device.
Proof of Concept
Outside videos showing operation when set up:
Demo while leaving the house showing the snow and colder notifications.
Demo while entering the house, showing how there’s no notifications even though the temperature gets colder enough and it will snow.
Videos taken inside to show the different cases:
Demo showing how there are no notifications when temperature doesn’t change enough and it won’t snow or rain.
Demo showing when it just gets colder.
Demo showing when it just gets hotter.
Demo showing when it just will rain.
Demo showing when it just will snow.
Demo showing when it just will rain and is currently raining.
Changes for real implementation
Currently, my device is easily seen and looks bad especially because I had to use so much duct tape for things to stick. To fix this, I would use command strips instead of duct tape to attach everything to the door and wall. The wires for the magnetic reed switch would be all white to blend more. The main board part would be soldered together and put into a box then place under the decorative wreath.
In the final version, a rechargeable 9V battery would be stepped down to 5V would power the LED strip and PIR motion sensor rather than my uno plugged into my laptop which I used because my 9V battery was drained. Using a usb wall plug, I would power the esp32 board rather than my laptop’s port. The outlet my laptop is plugged into would be used for this.
I would run the LED strip wires across the roof of the porch and put the LED strip where the green circle is. This way, it would be where you face while leaving the house and is in the line of sight for going down the porch stairs.
Schematic:
Code:
Note: You will have to enter in your own WiFi information as well as API key- I don’t want to just broadcast that.
//used this tutorial for getting weather: https://techtutorialsx.com/2018/03/17/esp32-arduino-getting-weather-data-from-api/ #include <WiFi.h> #include <HTTPClient.h> const char* ssid = "YOUR WIFI NAME"; const char* password = "YOUR WIFI PASSWORD"; const String endpoint = "https://api.openweathermap.org/data/2.5/onecall?lat=40.445227&lon=-79.934243&exclude=minutely,daily,alerts&units=imperial&appid=";//CHANGE TO LOCATION YOU WANT DATA FROM const String key = "YOUR UNIQUE KEY"; const int doorSwitchPin = 14; int justOpened = 0; int foundWeather = 0; //const int LEDpin=12; #include <NeoPixelBus.h> const int LEDstripPin = 12; const int numLEDs = 10; NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(numLEDs, LEDstripPin); long startTime; int hotter = 0; int colder = 0; int currentlyRaining = 0; int currentlySnowing = 0; int willRain = 0; int willSnow = 0; int rainWarning = 0; int snowWarning = 0; int maskWarning = 0; int haveMask = 0; String content; #include <Tone32.h> #define BUZZER_PIN A0 #define BUZZER_CHANNEL 0 const int PIRpin = 27; int pirVal; void pirISR() { pirVal = digitalRead(PIRpin); } //RFID stuff mainly from this tutorial https://randomnerdtutorials.com/security-access-using-mfrc522-rfid-reader-with-arduino/ //#include <SPI.h> //#include <MFRC522.h> // //#define SS_PIN 23 //#define RST_PIN 15 //MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. void setup() { Serial.begin(115200); // SPI.begin(); // Initiate SPI bus // mfrc522.PCD_Init(); // Initiate MFRC522 pinMode(doorSwitchPin, INPUT); pinMode(PIRpin, INPUT); attachInterrupt(digitalPinToInterrupt(PIRpin), pirISR, CHANGE); //pinMode(LEDpin, OUTPUT); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi.."); } Serial.println("Connected to the WiFi network"); // this resets all the neopixels to an off state strip.Begin(); strip.Show(); } void loop() { if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status HTTPClient http; if (digitalRead(doorSwitchPin) == 0) {//door opened //added measure so lights off when the door is opened, will immediately turn of lights if you open it while they are on for (int i = 0; i < numLEDs; i = i + 1) { strip.SetPixelColor(i, RgbColor(0, 0, 0)); } strip.Show(); if (pirVal == 0) { //no motion detected at door near knob outside, so ur opening from inside // if (haveMask == 0) { // Look for new cards // if ( ! mfrc522.PICC_IsNewCardPresent()) // { // //return; // } // // Select one of the cards // if ( ! mfrc522.PICC_ReadCardSerial()) // { // //return; // } // } // //Show UID on serial monitor // // Serial.print("UID tag :"); // content = ""; // byte letter; // for (byte i = 0; i < mfrc522.uid.size; i++) // { // // Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); // // Serial.print(mfrc522.uid.uidByte[i], HEX); // content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ")); // content.concat(String(mfrc522.uid.uidByte[i], HEX)); // } // content.toUpperCase(); // // if (content.substring(1) == "ED E4 86 B9" || content.substring(1) == "9D 65 7D B9" ) {//change here the UID of the card/cards that you want to give access // int haveMask = 1; // } // else { //dont have mask // if (maskWarning == 0) { // for (int count = 0; count < 4; count++) { // tone(BUZZER_PIN, NOTE_D4, 500, BUZZER_CHANNEL); // noTone(BUZZER_PIN, BUZZER_CHANNEL); // tone(BUZZER_PIN, NOTE_C4, 500, BUZZER_CHANNEL); // noTone(BUZZER_PIN, BUZZER_CHANNEL); // } // maskWarning = 1; //been warned // } // } if (foundWeather == 0) {//havent calculated the weather for this opening yet // Serial.println("calculating"); http.begin(endpoint + key); //Specify the URL int httpCode = http.GET(); //Make the request if (httpCode > 0) { //Check for the returning code String payload = http.getString(); int first1 = payload.indexOf("temp");//gives index of the t int start1 = first1 + 6;//add indexso that I'm at the number int end1 = payload.indexOf(',', start1); Serial.println(payload); //Serial.println(start1); float currentTemp = payload.substring(start1, end1).toFloat();//making string into float so that I can do math with it int first2 = payload.indexOf("temp", end1); int start2 = first2 + 6; int end2 = payload.indexOf(',', start2); float hrOneTemp = payload.substring(start2, end2).toFloat(); int first3 = payload.indexOf("temp", end2); int start3 = first3 + 6; int end3 = payload.indexOf(',', start3); float hrTwoTemp = payload.substring(start3, end3).toFloat(); Serial.print("Current temp: "); Serial.print(currentTemp); Serial.print("\t"); Serial.print("Temp in 1 hr: "); Serial.print(hrOneTemp); Serial.print("\t"); Serial.print("Temp in 2 hrs: "); Serial.println(hrTwoTemp); if (((hrOneTemp - currentTemp) > 5) || ((hrTwoTemp - currentTemp) > 5)) { //would've been 5 but not fluctuating much at all recently hotter = 1; } else { hotter = 0; } if (((currentTemp - hrOneTemp) > 5) || ((currentTemp - hrTwoTemp) > 5)) { //would've been 5 but not fluctuating much at all recently colder = 1; } else { colder = 0; } int first4 = payload.indexOf("temp", end3);//just using to know around where hour 3 is int checkRain = payload.indexOf("Rain"); if (checkRain != -1) { //means doesn't rain at all in huge data set if (checkRain < first2) { currentlyRaining = 1; willRain = 0; //dont care if it rains later as can already see it's raining }//don't bother finding index of another rain bc if it's already raining don't need to see if it'll rain in next 2 hours else if (checkRain < first3) { willRain = 1; //will rain in an hr } else if (checkRain < first4) { willRain = 1; //will rain in 2 hours } } else { willRain = 0; } int checkSnow = payload.indexOf("Snow"); if (checkSnow != -1) { if (checkSnow < first2) { currentlySnowing = 1; willSnow = 0; } else if (checkSnow < first3) { willSnow = 1; } else if (checkSnow < first4) { willSnow = 1; } } else { willSnow = 0; } willRain = 1; Serial.print("Currently raining: "); Serial.print(currentlyRaining); Serial.print("\t"); Serial.print("Will rain: "); Serial.println(willRain); Serial.print("Currently snowing: "); Serial.print(currentlySnowing); Serial.print("\t"); Serial.print("Will snow: "); Serial.println(willSnow); if (willRain) { if (rainWarning == 0) { for (int count = 0; count < 4; count++) {//play sequence 3x while door is being closed/your just stepping outside tone(BUZZER_PIN, NOTE_C4, 100, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); tone(BUZZER_PIN, NOTE_A4, 200, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); tone(BUZZER_PIN, NOTE_B4, 200, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); } rainWarning = 1;//so warning doesn't just keep playing if door is open } } if (willSnow) { if (snowWarning == 0) { for (int count = 0; count < 4; count++) { tone(BUZZER_PIN, NOTE_D4, 100, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); tone(BUZZER_PIN, NOTE_E4, 500, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); tone(BUZZER_PIN, NOTE_C4, 300, BUZZER_CHANNEL); noTone(BUZZER_PIN, BUZZER_CHANNEL); } snowWarning = 1; } } } else { Serial.println("Error on HTTP request"); } http.end(); //Free the resources foundWeather = 1; } } } //} else {//door closed again if (foundWeather == 1) { startTime = millis(); //resetting the variables above rainWarning = 0; snowWarning = 0; maskWarning = 0; haveMask = 0; content = ""; } foundWeather = 0; if (hotter) { if (millis() - startTime < 2000) { //flash to get attention static int blinkStart = millis(); for (uint16_t i = 0; i < numLEDs; i++) {//this rotates through each LED on the strip so that each one will be red byte x = millis() - 20 * i; strip.SetPixelColor(i, RgbColor(255 - x, 0, 0)); } strip.Show(); if (millis() - blinkStart >= 10) { for (uint16_t i = 0; i < numLEDs; i++) { strip.SetPixelColor(i, RgbColor(0, 0, 0)); } strip.Show(); blinkStart = millis(); } } else if (millis() - startTime < 7000) {//stays on just lights as attention probably already caught and dont want to annoy for (uint16_t i = 0; i < numLEDs; i++) { byte x = millis() - 20 * i; strip.SetPixelColor(i, RgbColor(255 - x, 0, 0)); } } else { for (int i = 0; i < numLEDs; i = i + 1) { strip.SetPixelColor(i, RgbColor(0, 0, 0)); } strip.Show(); } } else if (colder) { if (millis() - startTime < 2000) { static int blinkStart = millis(); for (uint16_t i = 0; i < numLEDs; i++) { byte x = millis() - 20 * i; strip.SetPixelColor(i, RgbColor(0, 0, 255 - x)); } strip.Show(); if (millis() - blinkStart >= 10) { for (uint16_t i = 0; i < numLEDs; i++) { strip.SetPixelColor(i, RgbColor(0, 0, 0)); } strip.Show(); blinkStart = millis(); } } else if (millis() - startTime < 7000) { for (uint16_t i = 0; i < numLEDs; i++) { byte x = millis() - 20 * i; strip.SetPixelColor(i, RgbColor(0, 0, 255 - x)); } } else { for (int i = 0; i < numLEDs; i = i + 1) { strip.SetPixelColor(i, RgbColor(0, 0, 0)); } strip.Show(); } } } } }
Notes on taking project further and functioning that was decided against
- The idea of having an RFID scanner so that notifications are only given when you aren’t prepared is really interesting and would add great functionality, but doesn’t work well with this project. The RFID scanner I have took too long to read the card and held up the entire system; it took long enough that you could already make it to the stairs before a notification was given. Additionally, the card/tag had to be touching the scanner which isn’t realistic for someone leaving the house to do. Finally, it can’t read multiple cards right next to each other. The first of these problems could be fixed with better equipment, but I don’t think the other two would get solved. If someone fixed these issues and wanted to make my project, especially more commercial, the add-ons would be that the clothes and items that pertain to certain type of weather (ie sweatshirt, raincoat, boots, gloves, etc) could be scanned on the way out. Multiple tags can be used for the same category and the tags could be small and machine washable. Similar to this, in the age of covid, masks could be checked for and if you don’t have one, be warned with an alarm sound. Moreover, our school cards are RFID cards, so I could have set it to react according to who is leaving the house or even only just for me. To really expand, a To-do list could be linked via the internet and if you leave close to the time of grocery shopping, you could get a notification if you don’t have reusable bags.
- Instead of specific sound sequences, I tried to use the TTS esp library with a speaker and amplifier circuit. This is loud enough with the amplifier circuit, however, it isn’t very understandable. I wanted it to say, rain, snow, and mask (with the RFID implemented but again it didn’t work fast enough). Even changing the text given to try and make it sound better by spelling more like pronunciation didn’t help much.
- Something relatively easy to do with IFTTT is to send a text message when certain criteria are met. I could have had a text message to me if it was going to be colder, hotter, rain, or snow. I decided against this because it would only really make sense if who was leaving was detected and free accounts only have 3 applets included so I didn’t want to use another on this especially since for me I probably wouldn’t even look at or notice that notification until I’m pretty far away.
Notes about using the esp32 board
In general, it takes much longer to compile and upload to the board than it did for the uno.
Certain libraries that work with the uno don’t work with this and you need to download different ones in general or the same ones modified to work with esp32s.
- I needed to use the Tone32 library instead of regular one.
- The PololuLedStrip library is incompatible and so I instead used NeoPixelBus.
- Not for this project, but for the other one I’m working on, I needed to use download an esp servo library and still included Servo.h like usual.