Files
lab-sage/Arduino/labsage_v1.1_9_1.ino

595 lines
21 KiB
Arduino
Raw Normal View History

2023-04-12 09:02:38 +00:00
/******************************************************************************************************************************
Date: 29/03/2023
Filename: LabSage_v1.1.9.1
Description: updated 1.1.9
Update - Increased threshold counting values. False alarm aversion.
*****************************************************************************************************************************/
// INCLUDE LIBRARIES
#include <TaskScheduler.h> // Install TaskScheduler Arduino Library
#include <WiFiManager.h> // Install WiFiManager Library
#include <FirebaseESP8266.h> // Install Firebase ESP8266 library (Mobitz?)
#include <MB_NTP.h>
#include <ESP8266WiFi.h>
#include <DHT.h> // Install DHT11 Library and Adafruit Unified Sensor Library
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h> // 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();
}
}