#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// WiFi credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// MQTT broker
const char* mqtt_server = "broker.hivemq.com";
// MQTT topics
const char* tempHumTopic = "iot/sensor/temperature_humidity";
const char* weatherTopic = "iot/weather";
const char* fanControlTopic = "iot/fan/control";
const char* alertTopic = "iot/alert";
// DHT22 sensor
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Pins
const int thermistorPin = 34;
const int buzzerPin = 26;
const int fanPin = 2;
// Temperature threshold
float tempThreshold = 20;
WiFiClient espClient;
PubSubClient client(espClient);
// Timers
unsigned long lastSensorReadMillis = 0;
const long sensorReadInterval = 10000;
unsigned long lastWeatherFetchMillis = 0;
const long weatherFetchInterval = 10000;
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
dht.begin();
pinMode(thermistorPin, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(fanPin, OUTPUT);
}
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "ESP32Client-" + String(WiFi.macAddress());
if (client.connect(clientId.c_str())) {
Serial.println("connected");
client.subscribe(fanControlTopic);
Serial.print("Subscribed to: ");
Serial.println(fanControlTopic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" trying again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
Serial.println(message);
if (String(topic) == fanControlTopic) {
int fanState = message.toInt();
digitalWrite(fanPin, fanState);
Serial.print("Fan state set to: ");
Serial.println(fanState == HIGH ? "ON" : "OFF");
}
}
void sendWhatsAppAlert(String message) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = "https://api.callmebot.com/whatsapp.php?phone=27664601947&text=" +
urlencode(message) +
"&apikey=6684276";
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
Serial.println("WhatsApp sent: " + message);
} else {
Serial.println("Failed to send WhatsApp. Error: " + http.errorToString(httpCode));
}
http.end();
}
}
String urlencode(String str) {
String encoded = "";
char c;
char code0, code1;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (isalnum(c)) {
encoded += c;
} else {
code0 = (c >> 4) & 0xF;
code1 = c & 0xF;
encoded += '%';
encoded += char(code0 > 9 ? code0 - 10 + 'A' : code0 + '0');
encoded += char(code1 > 9 ? code1 - 10 + 'A' : code1 + '0');
}
}
return encoded;
}
void readSensors() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
int analogValue = analogRead(thermistorPin);
float voltage = analogValue * 3.3 / 4095.0;
float resistance = (3.3 - voltage) * 10000.0 / voltage;
const float B_PARAM = 3950.0;
const float R0 = 10000.0;
const float T0_KELVIN = 25.0 + 273.15;
float tempC = 1 / (log(resistance / R0) / B_PARAM + 1 / T0_KELVIN) - 273.15;
// Send sensor readings to MQTT
StaticJsonDocument<200> doc;
doc["dht_temp_c"] = t;
doc["dht_humidity"] = h;
doc["thermistor_temp_c"] = tempC;
String jsonPayload;
serializeJson(doc, jsonPayload);
client.publish(tempHumTopic, jsonPayload.c_str());
Serial.println("Published Sensor Data: " + jsonPayload);
// Alert or OK logic
if (t > tempThreshold || tempC > tempThreshold) {
digitalWrite(buzzerPin, HIGH);
digitalWrite(fanPin, HIGH);
client.publish(alertTopic, "Temperature threshold exceeded!");
Serial.println("Alert: Temperature threshold exceeded!");
String alertMsg = "⚠️ Temperature Alert!\nDHT: " + String(t, 1) + "°C\nThermistor: " + String(tempC, 1) + "°C";
sendWhatsAppAlert(alertMsg);
} else {
digitalWrite(buzzerPin, LOW);
digitalWrite(fanPin, LOW);
String okMessage = "✅ Temperature is normal.\nDHT: " + String(t, 1) + "°C\nThermistor: " + String(tempC, 1) + "°C";
client.publish(alertTopic, okMessage.c_str());
Serial.println("Status: " + okMessage);
sendWhatsAppAlert(okMessage);
}
}
void fetchWeatherData() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin("https://api.openweathermap.org/data/2.5/weather?q=Cape%20Town&appid=f63003e055f21f1953b1186b7c7fc142&units=metric");
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
StaticJsonDocument<256> weatherDoc;
weatherDoc["city"] = doc["name"];
weatherDoc["temp"] = doc["main"]["temp"];
weatherDoc["humidity"] = doc["main"]["humidity"];
weatherDoc["wind_speed"] = doc["wind"]["speed"];
weatherDoc["clouds"] = doc["clouds"]["all"];
weatherDoc["weather"] = doc["weather"][0]["main"];
String weatherJson;
serializeJson(weatherDoc, weatherJson);
client.publish(weatherTopic, weatherJson.c_str());
Serial.println("Published Weather Data: " + weatherJson);
} else {
Serial.println("Failed to parse weather JSON");
}
} else {
Serial.print("Error on HTTP request: ");
Serial.println(httpCode);
}
http.end();
} else {
Serial.println("WiFi not connected for weather data fetch.");
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long currentMillis = millis();
if (currentMillis - lastSensorReadMillis >= sensorReadInterval) {
lastSensorReadMillis = currentMillis;
readSensors();
}
if (currentMillis - lastWeatherFetchMillis >= weatherFetchInterval) {
lastWeatherFetchMillis = currentMillis;
fetchWeatherData();
}
}