#include <RTClib.h>
#include "my_lcd.h"
#include "my_wifi.h"
#include "PsychicMqttClient.h"
#include <DHT.h>
// --- GPIO Pin ---
#define DHTPIN 32
#define DHTTYPE DHT22
#define RED_LED_PIN 23
#define ORANGE_LED_PIN 19
#define PUSH_BUTTON_PIN 34
#define OVERHEAT_LED_PIN 27
// --- MQTT Configuration ---
const char MQTTserver[] = "mqtts://34373d9086c442d3bc260aca9b1928bb.s1.eu.hivemq.cloud:8883";
const int mqtt_port = 8883;
const char MQTT_CLIENT_ID[] = "MengMilestone2";
const char MQTT_USERNAME[] = "IOT_Milestone";
const char MQTT_PASSWORD[] = "Admin@12345";
const char* LWT_TOPIC = "Icecreampolur5/Status/Device";
const char* LWT_MESSAGE_OFFLINE = "Offline";
const char* LWT_MESSAGE_ONLINE = "Online";
const int LWT_QOS = 1;
const bool LWT_RETAIN = true;
// --- MQTT Status Topic ---
const char* MQTT_TEMP_TOPIC = "Icecreampolur5/Status/Temp";
const char* MQTT_RH_TOPIC = "Icecreampolur5/Status/RH";
const char* MQTT_LIGHT_TOPIC = "Icecreampolur5/Status/Light";
const char* MQTT_COMMAND_TOPIC = "Icecreampolur5/Command/Light";
// --- MQTT Alarm Topic ---
const char* MQTT_ALARM_FIRE_TOPIC = "Icecreampolur5/Alarm/Fire";
const char* MQTT_ALARM_OVERHEAT_TOPIC = "Icecreampolur5/Alarm/Overheat";
const char* MQTT_STATE_ON = "Alarm ON";
const char* MQTT_STATE_OFF = "Alarm OFF";
const char* MQTT_STATELIGHT_ON = "Light ON";
const char* MQTT_STATELIGHT_OFF = "Light OFF";
// --- MQTT Command Topic for Alarm Control ---
const char* MQTT_COMMAND_ON = "ON";
const char* MQTT_COMMAND_OFF = "OFF";
const char* PAYLOAD_ON = "ON";
const char* PAYLOAD_OFF = "OFF";
// --- Global Object ---
MyLCD lcd;
RTC_DS1307 rtc;
MyWiFi wifi;
PsychicMqttClient mqttClient;
DHT dht(DHTPIN, DHTTYPE);
// --- Thresholds ---
const float OVERHEAT_THRESHOLD = 40.0;
const float HUMIDITY_THRESHOLD = 50.0;
// --- Global Alarm State Variables ---
bool fireAlarmActive = false;
bool overheatAlarmActive = false;
bool LightActive = false;
bool previousLightState = false;
bool topicsSubscribed = false;
// --- Millis() variables for non-blocking operations ---
unsigned long previousLcdDisplayMillis = 0;
const long lcdDisplayInterval = 2000;
unsigned long previousMqttPublishMillis = 0;
const long mqttPublishInterval = 5000;
// --- Button Debounce Variables ---
unsigned long lastDebounceTime = 0;
const long debounceDelay = 100;
// --- Variables to hold the current and previous reading from the button pin ---
int buttonReading;
int buttonState = LOW;
int lastButtonState = LOW;
// --- Keep track of LCD state for cycling through different displays ---
enum LcdState {
STATE_WELCOME_MESSAGE,
STATE_DATETIME_ALARM
};
LcdState currentLcdState = STATE_WELCOME_MESSAGE;
// --- Variables to store sensor readings ---
float h = 0.0;
float t = 0.0;
// --- Callback function for MQTT Alarm Command topic ---
void mqttLightCommand(
const char *topic,
const char *payload,
int retain,
int qos,
bool dup
) {
Serial.printf("Received Light Command on topic: %s, payload: %s\r\n", topic, payload);
if (strcmp(payload, MQTT_COMMAND_ON) == 0) {
LightActive = true;
Serial.println("Light commanded ON via MQTT.");
} else if (strcmp(payload, MQTT_COMMAND_OFF) == 0) {
LightActive = false;
Serial.println("Light commanded OFF via MQTT.");
} else {
Serial.println("Unknown Light Command payload. Use 'ON' or 'OFF'.");
}
}
void subscribe_mqtt_topics() {
Serial.println("Attempting to subscribe to MQTT topics...");
mqttClient.onTopic(MQTT_COMMAND_TOPIC, 1, mqttLightCommand);
Serial.println("Subscription calls made for topics.");
}
void setup() {
Serial.begin(115200);
lcd.begin();
// --- Set up GPIO pins ---
pinMode(RED_LED_PIN, OUTPUT);
digitalWrite(RED_LED_PIN, LOW);
pinMode(PUSH_BUTTON_PIN, INPUT_PULLDOWN);
pinMode(ORANGE_LED_PIN, OUTPUT);
digitalWrite(ORANGE_LED_PIN, LOW);
pinMode(OVERHEAT_LED_PIN, OUTPUT);
digitalWrite(OVERHEAT_LED_PIN, LOW);
// --- Initialize RTC (Real-Time Clock) ---
if (!rtc.begin()) {
Serial.println(F("Error: RTC not found!"));
lcd.print("RTC not found!");
while (1);
}
wifi.begin(&rtc);
// --- Initialize DHT sensor ---
dht.begin();
Serial.println("DHT sensor initialized.");
// --- MQTT Client Configuration ---
mqttClient.setServer(MQTTserver);
mqttClient.setClientId(MQTT_CLIENT_ID);
mqttClient.setCredentials(MQTT_USERNAME, MQTT_PASSWORD);
Serial.printf("Set MQTT Client ID: %s, Username: %s\r\n", MQTT_CLIENT_ID, MQTT_USERNAME);
mqttClient.setKeepAlive(30);
Serial.println("Set MQTT Keep Alive to 30 seconds.");
mqttClient.attachArduinoCACertBundle();
Serial.println("Attached Arduino CA Certificate Bundle for TLS/SSL.");
// --- Set Last Will and Testament BEFORE connecting ---
mqttClient.setWill(LWT_TOPIC, 1, true, LWT_MESSAGE_OFFLINE);
Serial.printf("Set Last Will (Topic: %s, Message: %s)\r\n", LWT_TOPIC, LWT_MESSAGE_OFFLINE);
// --- Attach MQTT Event Handlers ---
mqttClient.onConnect([&](bool sessionPresent) {
Serial.printf("MQTT Client Connected! Session Present: %d\r\n", sessionPresent);
if (!sessionPresent || !topicsSubscribed) {
subscribe_mqtt_topics();
topicsSubscribed = true;
}
mqttClient.publish(LWT_TOPIC, 1, true, LWT_MESSAGE_ONLINE);
Serial.printf("Published Online status: %s\r\n", LWT_MESSAGE_ONLINE);
});
mqttClient.onDisconnect([&](bool sessionPresent) {
Serial.printf("MQTT Client Disconnected! Session Present: %d\r\n", sessionPresent);
topicsSubscribed = false;
});
mqttClient.onError([](esp_mqtt_error_codes_t error) {
Serial.printf("MQTT Error occurred: %d\r\n", error);
});
mqttClient.onSubscribe([](int msgId) {
Serial.printf("MQTT Subscribed (Msg ID: %d)\r\n", msgId);
});
mqttClient.onPublish([](int msgId) {
Serial.printf("MQTT Published (Msg ID: %d)\r\n", msgId);
});
// --- Publish initial Light state ---
mqttClient.publish(MQTT_LIGHT_TOPIC, 1, true, LightActive ? MQTT_STATELIGHT_ON : MQTT_STATELIGHT_OFF);
Serial.printf("Published Initial Light State: %s\r\n", LightActive ? MQTT_STATELIGHT_ON : MQTT_STATELIGHT_OFF);
previousLightState = LightActive;
}
void loop() {
// --- WiFi & MQTT Connection Management ---
if (wifi.isTimeSynched()) {
if (!mqttClient.connected()) {
Serial.println("MQTT Client not connected. Attempting to connect...");
mqttClient.connect();
}
} else {
Serial.println("WiFi not connected or time not synchronized.");
topicsSubscribed = false;
previousMqttPublishMillis = millis() - mqttPublishInterval;
}
// --- Non-blocking Button Read and Debounce Logic ---
unsigned long currentMillis = millis();
buttonReading = digitalRead(PUSH_BUTTON_PIN);
if (buttonReading != lastButtonState) {
lastDebounceTime = currentMillis;
}
if ((currentMillis - lastDebounceTime) > debounceDelay) {
if (buttonReading != buttonState) {
buttonState = buttonReading;
if (buttonState == HIGH) {
Serial.println("Button Pressed!");
fireAlarmActive = !fireAlarmActive;
Serial.printf("Alarm state toggled to: %s\r\n", fireAlarmActive ? "ON" : "OFF");
if (wifi.isTimeSynched() && mqttClient.connected()) {
if (fireAlarmActive) {
mqttClient.publish(MQTT_ALARM_FIRE_TOPIC, 1, false, MQTT_STATE_ON);
Serial.println("MQTT Alarm Sent: Button Pressed (Alarm ON)");
} else {
mqttClient.publish(MQTT_ALARM_FIRE_TOPIC, 1, false, MQTT_STATE_OFF);
Serial.println("MQTT Alarm Sent: Button Released (Alarm OFF)");
}
}
}
}
}
lastButtonState = buttonReading;
// --- Centralized LED Control ---
digitalWrite(ORANGE_LED_PIN, LightActive ? HIGH : LOW);
digitalWrite(RED_LED_PIN, fireAlarmActive ? HIGH : LOW);
digitalWrite(OVERHEAT_LED_PIN, overheatAlarmActive ? HIGH : LOW);
// --- Check and publish Light state only if it changes ---
if (LightActive != previousLightState) {
if (wifi.isTimeSynched() && mqttClient.connected()) {
mqttClient.publish(MQTT_LIGHT_TOPIC, 1, true, LightActive ? MQTT_STATELIGHT_ON : MQTT_STATELIGHT_OFF);
Serial.printf("Light State Changed and Published: %s\r\n", LightActive ? MQTT_STATELIGHT_ON : MQTT_STATELIGHT_OFF);
}
previousLightState = LightActive;
}
// --- Non-blocking LCD Display Logic ---
if (currentMillis - previousLcdDisplayMillis >= lcdDisplayInterval) {
previousLcdDisplayMillis = currentMillis;
lcd.clear();
switch (currentLcdState) {
case STATE_WELCOME_MESSAGE:
lcd.display("ESP32-DevKitC-v4", "Class DEE1", "Hui Meng");
currentLcdState = STATE_DATETIME_ALARM;
break;
case STATE_DATETIME_ALARM:
DateTime now = rtc.now();
char dtbuf[21] = "YYYY-MM-DD hh:mm:ss";
now.toString(dtbuf);
lcd.datetime(dtbuf);
if (wifi.isTimeSynched()) {
char temp_str[10];
char rh_str[10];
dtostrf(t, 4, 1, temp_str);
dtostrf(h, 4, 1, rh_str);
lcd.alarm(1, "Temp xxxx:", temp_str, 0);
lcd.alarm(2, "RH xxxx:", rh_str, "%");
const char* lightStatus = LightActive ? "ON" : "OFF";
lcd.alarm(3, "Light :", lightStatus, "");
const char* fireAlarmStatus = fireAlarmActive ? "ON" : "OFF";
lcd.alarm(4, "Fire Alarm:", fireAlarmStatus, "");
}
currentLcdState = STATE_WELCOME_MESSAGE;
break;
}
}
// --- Non-blocking MQTT Publishing & DHT Reading Logic ---
if (wifi.isTimeSynched() && mqttClient.connected()) {
if (currentMillis - previousMqttPublishMillis >= mqttPublishInterval) {
previousMqttPublishMillis = currentMillis;
h = dht.readHumidity();
t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println(F("Failed to read from DHT sensor! Retrying next interval."));
return;
}
bool previousOverheatAlarmState = overheatAlarmActive;
if (t > OVERHEAT_THRESHOLD || h > HUMIDITY_THRESHOLD) {
overheatAlarmActive = true;
} else {
overheatAlarmActive = false;
}
if (overheatAlarmActive != previousOverheatAlarmState) {
const char* payload = overheatAlarmActive ? MQTT_STATE_ON : MQTT_STATE_OFF;
mqttClient.publish(MQTT_ALARM_OVERHEAT_TOPIC, 1, false, payload);
Serial.printf("Overheat Alarm State Changed and Published: %s\r\n", payload);
}
Serial.printf("Publishing MQTT data: Temp=%.1f°C, Humidity=%.1f%%\r\n", t, h);
char temp_payload[10];
char rh_payload[10];
dtostrf(t, 4, 1, temp_payload);
dtostrf(h, 4, 1, rh_payload);
mqttClient.publish(MQTT_TEMP_TOPIC, 2, true, temp_payload);
mqttClient.publish(MQTT_RH_TOPIC, 1, true, rh_payload);
}
}
}