/*
FIRE DETECTOR 6000
FOR IHU
*/
// Firmware Definitions
#define SERIAL_SUPPORT // Compile with Serial Support
#define RELEASE_BUILD // Funky welcome effects
//#define BLYNK_INTEGRATION // Blynk Support
#define SERVO_SUPPORT // Fire Preventor Servo
#define EASTER_EGG // Team Easter Egg :b
// Pins
#define I2C_LCD_ADDRESS 0x27
#define DHT22_PIN 19
#define LDR_RECEPTOR_PIN 32
#define FIRE_ALERT_LED_PIN 26
#ifdef SERVO_SUPPORT
#define SERVO_PIN 14
#define SERVO_ECO_PREVENTION_SWITCH_PIN 4
#define SERVO_NON_ECO_POTENTIOMETER_PIN 2
#endif
// Libraries
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#ifdef BLYNK_INTEGRATION /* Blynk build prerequisites */
/* Those are the Blynk templates */
#define BLYNK_TEMPLATE_ID "TMPL4r0D6XLFF"
#define BLYNK_TEMPLATE_NAME "Fire Detector 6000"
#define AUTHENTICATION_TOKEN_BLYNK "aLyqq8oXoc2541MS__LK8oi6p_V-nO5O"
/* Normally we need a Wi-Fi handler but we hardcode */
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASS ""
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#endif
#ifdef SERVO_SUPPORT /* Servo */
#include <ESP32Servo.h>
#endif
/* Firmware code starts here */
///////////////////////////////////////////////////////////
// DEVICES //
///////////////////////////////////////////////////////////
// LCD Display
LiquidCrystal_I2C lcd(I2C_LCD_ADDRESS, 20, 4);
// DHT Humidity & Temperature Sensor
DHT dht(DHT22_PIN, DHT22);
// Servo
#ifdef SERVO_SUPPORT
Servo servo;
#endif
// On these temperatures we trigger a fire warning
#define FIRE_WARN 75
#define HUMID_WARN 50
#define LUMEN_WARN 1500
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
// CURRENT HANDLER //
///////////////////////////////////////////////////////////
// The Handler itself
int (*cid)();
// Current Handler
void pass_cid(int (*new_cid)(), bool clear_lcd) {
if(clear_lcd) {
lcd.clear();
lcd.setCursor(0, 0);
}
cid = new_cid;
}
// Panic safe empty function for placeholders :)
int nothing() {
return 0;
}
void panic(int ret) { /* NOTHING */
lcd.clear();
lcd.setCursor(0,0);
lcd.printstr("Execution Error: ");
lcd.print(ret);
int memory_position = (int)&cid;
lcd.setCursor(0,1);
lcd.printstr("CID: 0x");
lcd.print(memory_position, HEX);
#ifdef SERIAL_SUPPORT
Serial.print("PANIC!!\n");
Serial.print("Error Code: ");
Serial.print(ret);
Serial.print("\n");
Serial.print("CID Address: 0x");
Serial.print(memory_position, HEX);
#endif
while(true) //Pemantely stay there
delay(10000); // 10sec delay for clock sanity
}
// ESP32's built-in loop functionality
void loop() {
int ret;
ret = cid();
if(ret) // A CID Handler didn't return 0? Panic!
panic(ret);
// Once execution loop finishes. Delay it for a second.
delay(1000);
}
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
// DRIVERS & MODULES //
///////////////////////////////////////////////////////////
#ifdef SERIAL_SUPPORT
// Serial Module
void serial_module() {
Serial.begin(115200);
Serial.println("Serial: Serial Driver was initated");
}
// We may disable Serial support. Keep the code coherent at least.
void print_serial(const char* log_output) {
Serial.println(log_output);
}
#else
void print_serial(const char* log_output) {
/* NOTHING */
}
#endif
// Lumen Module
float get_lumen() {
/* This Lumen conversion algorithm had no copyright header */
float value = analogRead(LDR_RECEPTOR_PIN);
float voltage = value / 1024. * 5;
float resistance = 2000 * voltage / (1 - voltage / 5);
float lux = pow(50 * 1e3 * pow(10, 0.7) / resistance, (1 / 0.7));
return (value < 1000) ? lux : 0;
}
// LCD Kernel module
void kernel_lcd_module() {
print_serial("LCD: LCD initiated");
lcd.init();
print_serial("LCD: Testing Backlight");
lcd.backlight();
print_serial("LCD: Returning execution to kernel");
}
// Blynk module
#ifdef BLYNK_INTEGRATION
void blynk_module() {
print_serial("Blynk: Authenticating!!!");
#ifdef RELEASE_BUILD
lcd.setCursor(0, 1);
lcd.print(" BLYNK DRIVER ");
lcd.setCursor(0, 2);
lcd.print(" AUTHENTICATING ");
#endif
/* No sanity handler here. We may NOT authenticate! */
Blynk.begin(AUTHENTICATION_TOKEN_BLYNK, WIFI_SSID, WIFI_PASS);
print_serial("Blynk: Authenticated!!!");
#ifdef RELEASE_BUILD
lcd.setCursor(0, 2);
lcd.print(" AUTHENTICATED! ");
delay(2000);
#endif
lcd.clear(); // We ain't an individual process. Clear LCD manually
}
#endif
#ifdef SERVO_SUPPORT
void servo_module() {
servo.attach(SERVO_PIN, 500, 2400);
servo.write(0);
print_serial("Servo: Servo was attached");
}
#endif
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
// CUSTOM HANDLERS //
///////////////////////////////////////////////////////////
// Main Handler header for safe return
extern int main_handler();
// Eco Prevention Values for Blynk Builds
#ifdef SERVO_SUPPORT
#ifdef BLYNK_INTEGRATION
bool eco_prevention = 1;
int valve_pressure;
BLYNK_WRITE(V4) { // Eco Prevention Mode Handler
eco_prevention = (bool)param.asInt();
}
BLYNK_WRITE(V3) { // Non-eco Valve Pressure Handler
valve_pressure = param.asInt();
}
#endif
#endif
// Common Fire Prevention Handler
int fire_prevention_handler() {
digitalWrite(26, LOW); // Shut the LED off
if(((int)dht.readTemperature() > FIRE_WARN) && ((int)dht.readHumidity() < HUMID_WARN) && ((int)(get_lumen() >= LUMEN_WARN))) {
digitalWrite(26, HIGH); // Raise the LED high
#ifdef SERVO_SUPPORT // The Preventor.... If enabled to begin with
#ifndef BLYNK_INTEGRATION
servo.write(digitalRead(SERVO_ECO_PREVENTION_SWITCH_PIN) ? \
((int)(3.6 * (50 - (int)dht.readHumidity()))) : \
((int)(0.036 * (int)(analogRead(SERVO_NON_ECO_POTENTIOMETER_PIN)))));
#else
servo.write(eco_prevention ? \
((int)(3.6 * (50 - (int)dht.readHumidity()))) : \
valve_pressure);
#endif
#endif
}
#ifdef SERVO_SUPPORT // An else statement to close the Servo one the fire was taken care of
else servo.write(0);
#endif
pass_cid(main_handler, 0); // Pass control back to the main handler
return 0;
}
// Small Team Easter Egg
#ifdef EASTER_EGG
int easter_egg() {
lcd.setCursor(0, 0);
lcd.blink_on();
delay(3000);
char initial_text[] = "THE FOUR PERISHED";
char initial_text_2[] = "SOULS OF TEAM 9:";
char aem0[] = "5178";
char aem1[] = "5135";
char aem2[] = "5036";
char aem3[] = "5098";
char initial_text_3[] = "LET'S CRASH";
for(int i = 0; i < 17; i++) {
lcd.print(initial_text[i]);
delay(50);
}
lcd.setCursor(0, 1);
for(int i = 0; i < 16; i++) {
lcd.print(initial_text_2[i]);
delay(50);
}
lcd.setCursor(0, 2);
delay(3000);
for(int i = 0; i < 4; i++) {
lcd.print(aem0[i]);
delay(50);
}
delay(1500);
lcd.setCursor(16, 2);
for(int i = 0; i < 4; i++) {
lcd.print(aem1[i]);
delay(50);
}
lcd.setCursor(0, 3);
delay(1500);
for(int i = 0; i < 4; i++) {
lcd.print(aem2[i]);
delay(50);
}
delay(1500);
lcd.setCursor(16, 3);
for(int i = 0; i < 4; i++) {
lcd.print(aem3[i]);
delay(50);
}
delay(3000);
lcd.clear();
delay(1000);
lcd.setCursor(0, 0);
for(int i = 0; i < 11; i++) {
lcd.print(initial_text_3[i]);
delay(50);
}
delay(4000);
lcd.blink_off();
return 9; //Return Team (9) to trigger a panic
}
#endif
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
// MAIN HANDLERS //
///////////////////////////////////////////////////////////
// Main Handler
#ifdef BLYNK_INTEGRATION
int main_handler() { // Blynk
lcd.setCursor(0, 1);
lcd.printstr(" WE ARE ");
lcd.setCursor(0, 2);
lcd.printstr(" ON BLYNK ");
Blynk.run();
// Report the Temperature
Blynk.virtualWrite(V0, (int)dht.readTemperature());
// Report the Humidity
Blynk.virtualWrite(V1, (int)dht.readHumidity());
// Report Lumen concentration
Blynk.virtualWrite(V2, (int)get_lumen());
#ifdef EASTER_EGG
if(((int)dht.readTemperature() == 78) && ((int)dht.readHumidity() == 35) && (valve_pressure == 36))
pass_cid(easter_egg, 1);
else
pass_cid(fire_prevention_handler, 0);
#else
pass_cid(fire_prevention_handler, 0);
#endif
return 0;
}
#else
int main_handler() { // Standard
lcd.setCursor(0, 0);
lcd.printstr("Temperature: ");
lcd.print(dht.readTemperature());
lcd.printstr(" ");
lcd.setCursor(0, 1);
lcd.printstr("Humidity: ");
lcd.print(dht.readHumidity());
lcd.printstr(" ");
lcd.setCursor(0, 2);
lcd.printstr("Lumen: ");
lcd.print(get_lumen());
lcd.printstr(" ");
#ifdef EASTER_EGG
if(((int)dht.readTemperature() == 78) && ((int)dht.readHumidity() == 35) && (get_lumen() == 0))
pass_cid(easter_egg, 1);
else
pass_cid(fire_prevention_handler, 0);
#else
pass_cid(fire_prevention_handler, 0);
#endif
return 0;
}
#endif
// Initializer
int kernel_init() {
print_serial("Kernel: BOOT!");
print_serial("Kernel: Getting Modules");
serial_module();
kernel_lcd_module();
#ifdef SERVO_SUPPORT // Initiate the Servo module if a fire mechanism is in place
servo_module();
#ifndef BLYNK_INTEGRATION
pinMode(SERVO_ECO_PREVENTION_SWITCH_PIN, INPUT_PULLUP);
#endif
#endif
pinMode(FIRE_ALERT_LED_PIN, OUTPUT); // Fire alert pin
#ifdef BLYNK_INTEGRATION // If it's a Blynk build, authenticate with the device headers
print_serial("Kernel: It's a Blynk build! Starting Blynk");
blynk_module();
#endif
// Welcome!
print_serial("OS: Welcome to FIRE DETECTOR 6000!");
#ifdef RELEASE_BUILD
lcd.setCursor(1, 1);
char device[] = "FIRE DETECTOR 6000";
for(int i = 0; i < 18; i++) {
lcd.print(device[i]);
delay(50);
}
#ifdef BLYNK_INTEGRATION
lcd.setCursor(1, 2);
lcd.print("BLYNK BUILD");
#endif
delay(1000);
#endif
print_serial("OS: Getting into the Handler now");
pass_cid(main_handler, 1);
print_serial("Kernel: CID passed to the main hander");
print_serial("Kernel: Kernel initiated successfully!");
return 0;
}
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
/* Firmware code ends here */
///////////////////////////////////////////////////////////
// ESP32 BUILT-IN HANDLERS. DO NOT TOUCH! //
///////////////////////////////////////////////////////////
void setup() {
pass_cid(kernel_init, 0); // Jump straight onto the firmware
}
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////