From 2c2b686e1c1a1d419aa9888d0082d2f6a99bb226 Mon Sep 17 00:00:00 2001 From: tp2043 Date: Wed, 12 Apr 2023 09:07:00 +0000 Subject: [PATCH] Final Code as of 12/4/23 --- Arduino/labsage_v1.1_9_1.ino | 594 +++++++++++++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 Arduino/labsage_v1.1_9_1.ino diff --git a/Arduino/labsage_v1.1_9_1.ino b/Arduino/labsage_v1.1_9_1.ino new file mode 100644 index 0000000..30edcd9 --- /dev/null +++ b/Arduino/labsage_v1.1_9_1.ino @@ -0,0 +1,594 @@ +/****************************************************************************************************************************** + Date: 12/04/2023 + Filename: LabSage_v1.1.9.1 + Description: updated 1.1.9 + Update - Increased threshold counting values. False alarm aversion. + *****************************************************************************************************************************/ +// INCLUDE LIBRARIES +#include // Install TaskScheduler Arduino Library +#include // Install WiFiManager Library +#include // Install Firebase ESP8266 library (Mobitz?) +#include +#include +#include // Install DHT11 Library and Adafruit Unified Sensor Library +#include +#include +#include // Install Adafruit ADXL345 accelerometer library + +// FIREBASE RTDB PATH AND KEY +#define FIREBASE_HOST "https://smartlab-9b83e-default-rtdb.firebaseio.com/" +#define FIREBASE_AUTH "AIzaSyCiO5owTmUgosDyjL0_SqpxRoAwgKtn5-o" + +// SETTING PINS FOR SENSORS +#define LED D1 +#define DHTPIN D2 // Connect Data pin of DHT to D2 +#define GASPIN A0 // Gas sensor analog pin +#define BUZZERPIN D7 // Connect LED to D7 +#define SDA D5 // Accelerometer Serial Data +#define SCL D6 // Accelerometer Serial Clock + +// DHT VARIABLE SET +#define DHTTYPE DHT11 + +// OBJECT DECLARATIONS +Scheduler runner; // Multi tasking object +WiFiManager wm; // Wi-Fi Manager Object +DHT dht(DHTPIN, DHTTYPE); // DHT Object +Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345); // Accelerometer Object + +//MQ2 parameter for CO Gas sensing +float gasSensor_volt; +float RS; +const float R0 = 2.12; +const float b = 1.5120222; +const float m = -0.33975668; +float RS_R0_ratio; +float ppm_log; +float ppm; + +// VARIABLE DEFINITION +bool autoUpdateThresh = true; +long timer = 0L; +float t, h, g; // Temp, Humidity, gas value (ppm) +bool buzz = false; // Smoke alarm +bool coll = false; // Collision Alarm +float x, y, z; // Acceleration in x, y, z directions, storage variables for previous state +float sum = 0; // checksum for smoke detection - false alarm avoidance +unsigned int k = 0; // smoke alarm duration counter +float sThreshSum = 0; // Sum used for threshold updating +unsigned int j = 0; // Counter used for threshold updating +String PATH = "Devices/"; // Base path on RTDB +String UID = ""; // ESP Unique ID - Device ID necessary for dynamic path allocation + +// THRESHOLD VALUES // Not constant because it needs to be updated every min +float COLLISION_THRESHOLD = 12.0; +float SMOKE_THRESHOLD = 2000; + +// DEFINE FIREBASE DATA OBJECTs +FirebaseData firebaseData; +FirebaseData buzzerData; +FirebaseData collData; +FirebaseJson json; + +// FUNCTION DECLARATION +void dhtSetup(void); +void gasSetup(void); +void ambiRead(void); +void accelSetup(void); +void accelRead(void); +void sensorUpdate(void); +void upload(void); +void uploadAlert(void); +void sensorReset(void); +void reconnection(void); + +// TASK SCHEDULE FUNCTIONS +void task1Callback() { // Sensor update + // code for task 1 + sensorUpdate(); +} + +void task2Callback() { // Sensor value upload + // code for task 2 + if (WiFi.status() == WL_CONNECTED) { + upload(); + } + else { + Serial.println("Connection Lost!"); + digitalWrite(LED, !digitalRead(LED)); + Serial.println("Please Reconnect to a different WiFi.\nStarting Soft API..."); + reconnection(); + } +} + +void task3Callback() { // Reset receive and execute + // Check if there is a fire/smoke + if (WiFi.status() == WL_CONNECTED) { + if (Firebase.getBool(buzzerData, PATH + "/reset") && buzzerData.boolData() == true) { //Read from firebase for application + + Serial.println("Call to reset"); + sensorReset(); + + if (Firebase.setBool(firebaseData, PATH + "/reset", false)) // Reset the 'reset' value on the database + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + + uploadAlert(); // Resetting buzzer and collision values on rtdb + } + if (Firebase.getFloat(collData, PATH + "/collThresh") && collData.floatData() != COLLISION_THRESHOLD) { //Read from firebase for application + COLLISION_THRESHOLD = collData.floatData(); + Serial.print("New Collision Threshold = "); + Serial.println(COLLISION_THRESHOLD); + } + if (Firebase.getFloat(buzzerData, PATH + "/smokeThresh") && buzzerData.floatData() != SMOKE_THRESHOLD) { //Read from firebase for application + SMOKE_THRESHOLD = buzzerData.floatData(); + Serial.print("New Smoke Threshold = "); + Serial.println(SMOKE_THRESHOLD); + } + if (buzz || coll) { + uploadAlert(); // Setting the buzzer and collision values on rtdb + } + } + else { + Serial.println("Connection Lost!"); + digitalWrite(LED, !digitalRead(LED)); + Serial.println("Please Reconnect to a different WiFi.\nStarting Soft API..."); + reconnection(); + } +} + +void task4Callback() { // Update Smoke sensor threshold + // code for task 4 + if(autoUpdateThresh){ + if (sThreshSum != 0) { // To avoid accidental wrongful updates + if (j == 0) { // To avoid divide-by-zero error + j = 1; + } + SMOKE_THRESHOLD = sThreshSum / j + 2000; // Taking an average value of gas sensor readings because depending on use, the threshold changes greatly + Serial.print("Smoke threshold auto updated to "); + Serial.println(SMOKE_THRESHOLD); + sThreshSum = 0; // Reset these values for counting again + j = 0; + } + } + if (WiFi.status() == WL_CONNECTED) { + if (Firebase.setFloat(firebaseData, PATH + "/smokeThresh", SMOKE_THRESHOLD)) // Update Smoke Threshold + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + } +} + +Task t1(1000, TASK_FOREVER, &task1Callback); // Task rate: Once every second +Task t2(60000, TASK_FOREVER, &task2Callback); // Task rate: Once every minute +Task t3(3000, TASK_FOREVER, &task3Callback); // Task rate: Once every 3 seconds +Task t4(30000, TASK_FOREVER, &task4Callback); // Task rate: Once every 30 seconds + +// SETUP +void setup() { +// Set Pin Modes + pinMode(LED, OUTPUT); + digitalWrite(LED, LOW); + pinMode(BUZZERPIN, OUTPUT); + Serial.begin(9600); + + dhtSetup(); + accelSetup(); + gasSetup(); + + UID = ESP.getChipId(); // Extract Unique Chip ID for authentication and identity purposes + Serial.printf("ESP8266 Unique Chip id = %s\n", UID); // For hex representation, use %08X + Serial.println(); + Serial.println("Opening WiFi AP"); +// wm.resetSettings(); // Comment out before deployment + + bool res; + wm.setConnectTimeout(60); + wm.setConfigPortalTimeoutCallback(reconnection); + wm.setWiFiAutoReconnect(true); + res = wm.autoConnect("SMI_LBS_ESP"); // auto generated AP name from chipid + wm.setShowInfoUpdate(false); + wm.setShowInfoErase(false); + if (!res) { + Serial.println("Failed to connect"); + } + else { + //if you get here you have connected to the WiFi + Serial.println("Connected"); + } + Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH); // Begin Firebase Server + Firebase.reconnectWiFi(true); + + Serial.println(PATH.concat(UID)); // Dynamic database path allocation + if (Firebase.setFloat(firebaseData, PATH + "/smokeThresh", SMOKE_THRESHOLD)) //Uploading the default smoke threshold for the first time + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + if (Firebase.setFloat(firebaseData, PATH + "/collThresh", COLLISION_THRESHOLD)) // Uploading the default collision threshold for the first time + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + digitalWrite(LED, HIGH); + runner.init(); // Initialise Scheduler, add tasks to it, and then enable the tasks for multitasking + runner.addTask(t1); + runner.addTask(t2); + runner.addTask(t3); + runner.addTask(t4); + t1.enable(); + t2.enable(); + t3.enable(); + t4.enable(); + timer = millis(); +} + +// MAIN LOOP CODE +void loop() { + runner.execute(); // Run the tasks simultaneously +} + +void reconnection(void) { // Re-establishing network with new Wi-Fi using Soft AP + bool res = wm.autoConnect(); + if (!res) { + Serial.println("Failed to connect"); + } + else { + Serial.println("Connected"); + } +} + +void accelSetup(void) { // Setup Accelerometer + Serial.println("Accelerometer Test"); Serial.println(""); + + // Initialise I2C communication as MASTER + Wire.begin(SDA, SCL); + + if (!accel.begin()) { + Serial.println("Failed to initialize ADXL345 sensor."); + while (1); + } + accel.setRange(ADXL345_RANGE_16_G); // Setting Max Range + + // Get accelerometer data + sensors_event_t event; + accel.getEvent(&event); + + // Assign current values to states + x = event.acceleration.x; + y = event.acceleration.y; + z = event.acceleration.z; + Serial.println("Accelerometer Ready!"); Serial.println(""); +} + +void accelRead(void) { // Read Accelerometer + // Get accelerometer data + sensors_event_t event; + accel.getEvent(&event); + + // Print the accelerometer data to serial monitor + Serial.print("Acceleration: "); + Serial.print(event.acceleration.x); + Serial.print(" m/s^2 "); + Serial.print(event.acceleration.y); + Serial.print(" m/s^2 "); + Serial.print(event.acceleration.z); + Serial.println(" m/s^2 "); + if (isnan(event.acceleration.x) || isnan(event.acceleration.y) || isnan(event.acceleration.z)) { + Serial.println(F("Failed to read from Accelerometer!")); + return; + } + + // Check if there is a collision + if ((abs(event.acceleration.x - x)) > COLLISION_THRESHOLD || + (abs(event.acceleration.y - y)) > COLLISION_THRESHOLD || + (abs(event.acceleration.z - z)) > COLLISION_THRESHOLD) { + Serial.println("Collision detected!"); + coll = true; + tone(BUZZERPIN, 100); + } + + // Assign current values to states + x = event.acceleration.x; + y = event.acceleration.y; + z = event.acceleration.z; +} + +void dhtSetup(void) { // Setup DHT11 + Serial.println(F("DHT11 setting up...")); + dht.begin(); + Serial.println("DHT11 ready!"); +} + +void gasSetup(void) { // Setup MQ-2 Smoke Sensor + Serial.println("Gas Sensor setting up..."); + pinMode(GASPIN, INPUT_PULLUP); + delay(10000); // 10 second delay for some level of preheating + int maxG = 0; + // Smoke Threshold established by taking average of the first five readings from the sensor + int cnt = 100; + float temp = 0; + for (int i = 0; i < cnt; i++) { + float gasSensorValue = analogRead(GASPIN); + gasSensor_volt = gasSensorValue / 1024 * 5.0; + RS = ((5 - gasSensor_volt) / gasSensor_volt); + RS_R0_ratio = RS / R0; + ppm_log = ((log (RS_R0_ratio) - b) / m); + ppm = pow (10, ppm_log); + // Check if any reads failed and exit early (to try again). + if (isnan(ppm)) { + Serial.println(F("Failed to read from Gas sensor!")); + return; + } + if(ppm>maxG) maxG = ppm; + temp = temp + ppm; + delay(500); + } + + SMOKE_THRESHOLD = SMOKE_THRESHOLD + maxG;//(temp / cnt); // Taking an average value of gas sensor readings because depending on use, the threshold changes greatly + Serial.println("Gas Sensor ready!"); + Serial.printf("\nSmoke Threshold: %f ppm\n", SMOKE_THRESHOLD); + Serial.println(""); +} + +void ambiRead(void) { // Temp, Humidity, Gas reading + // Read relative humidity in % (the default) + h = dht.readHumidity(); + + // Read temperature as Celsius (the default) + t = dht.readTemperature(); + + // Read gas sensor analog data + float gasSensorValue = analogRead(GASPIN); + gasSensor_volt = gasSensorValue / 1024 * 5.0; + RS = ((5 - gasSensor_volt) / gasSensor_volt); + RS_R0_ratio = RS / R0; + ppm_log = ((log (RS_R0_ratio) - b) / m); + ppm = pow (10, ppm_log); + g = ppm; +/* + IF SMOKE THRESHOLD SHOULD BE UPDATED EVEN WITH THE SMOKE VALUES, UNCOMMENT THE FOLLOWING + sThreshSum += g; + j++; + AND COMMENT IT IN LINES 412-413 (SUBJECT TO CHANGE) + */ + // Check if any reads failed and exit early (to try again). + if (isnan(h) || isnan(t)) { + Serial.println(F("Failed to read from DHT sensor!")); + return; + } + if (isnan(g)) { + Serial.println(F("Failed to read from Gas sensor!")); + return; + } + if(t>50){ + Serial.println("Fire Detected!"); + buzz = true; + tone(BUZZERPIN, 400); + } + // Smoke Detection + if (g > SMOKE_THRESHOLD && ((millis()-timer) > 180000)) { // excluding the first 3 minutes of the program running to ensure no false alarms + sum += g; + k++; + Serial.println("Smoke detected!"); + buzz = true; + tone(BUZZERPIN, 400); + } + else { // If smoke is not detected, continue to updating the smoke threshold + // because smoke threshold ideally should not be updated for beyond limit values + sThreshSum += g; + j++; +// if fewer than 5 values are detecting smoke and it comes back to regular measurement, it is detected as a false alarm and the alarm auto turns off. + if (k <= 5) { // Temporary tolerance count is 5 readings. Could change it depending on the tolerance desired. + buzz = false; + if (coll == false) { + noTone(BUZZERPIN); + } + } + } + Serial.print(F("Humidity: ")); + Serial.print(h); + Serial.print(F("% Temperature: ")); + Serial.print(t); + Serial.print(F("C PPM:")); + Serial.print(g); + Serial.println(); + + +} + +void sensorUpdate(void) { + ambiRead(); + accelRead(); +} + +void sensorReset(void) { + coll = false; + buzz = false; + noTone(BUZZERPIN); + sum = 0; + k = 0; +} + +void upload(void) { // Uploading Sensor values + if (Firebase.setFloat(firebaseData, PATH + "/temperature", t)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + + if (Firebase.setFloat(firebaseData, PATH + "/humidity", h)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + if (Firebase.setFloat(firebaseData, PATH + "/ppm", g)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + + if (Firebase.setFloat(firebaseData, PATH + "/x", x)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + if (Firebase.setFloat(firebaseData, PATH + "/y", y)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + if (Firebase.setFloat(firebaseData, PATH + "/z", z)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } +} + +void uploadAlert(void) { // Uploading Alarm status + if (Firebase.setBool(firebaseData, PATH + "/buzzer", buzz)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } + if (Firebase.setBool(firebaseData, PATH + "/collision", coll)) + { + Serial.println("PASSED"); + Serial.println("PATH: " + firebaseData.dataPath()); + Serial.println("TYPE: " + firebaseData.dataType()); + Serial.println("ETag: " + firebaseData.ETag()); + Serial.println("------------------------------------"); + Serial.println(); + } + else + { + Serial.println("FAILED"); + Serial.println("REASON: " + firebaseData.errorReason()); + Serial.println("------------------------------------"); + Serial.println(); + } +}