#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <DHT.h>
//Encodes a message string into URL format
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 {
encoded += '%';
code0 = (c >> 4) & 0xF;
code1 = c & 0xF;
encoded += String("0123456789ABCDEF")[code0];
encoded += String("0123456789ABCDEF")[code1];
}
}
return encoded;
}
void sendWhatsAppAlert(String msg) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String phoneNumber = "27674609058";
String apikey = "2427754";
String url = "https://api.callmebot.com/whatsapp.php?phone=" + phoneNumber +
"&text=" + urlencode(msg) + "&apikey=" + apikey;
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
Serial.println("✅ WhatsApp alert sent!");
} else {
Serial.print("❌ WhatsApp send failed. HTTP error: ");
Serial.println(httpCode);
}
http.end();
}
}
// Sensors
#define DHTPIN 4
#define DHTTYPE DHT11
#define TMP36PIN 34
#define FAN_PIN 12 // PWM control pin
#define BUZZER_PIN 12
// Wi-Fi credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// OpenWeather API
const char* apiKey = "f4cb054e3018c9b03ab395588ca02f22";
const char* city = "Cape%20Town";
// MQTT
const char* mqtt_server = "broker.emqx.io";
const char* mqtt_topic = "/weather/data";
const char* pwm_topic = "/pwm/control"; // New topic for PWM control
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
// PWM variables and temperature threshold
int pwmValue = 0;
int tempThreshold = 25; // Default threshold
String selectedSensor = "dht22"; // Default sensor
//Variables for debounce control of WhatsApp alerts
unsigned long lastAlertTime = 0;
const unsigned long alertCooldown = 10000; // 10 seconds debounce
// Connect to Wi-Fi
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
// Handle incoming MQTT messages
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// Process PWM control messages
if (String(topic) == pwm_topic) {
DynamicJsonDocument doc(256);
DeserializationError error = deserializeJson(doc, message);
if (!error) {
if (doc.containsKey("pwm")) {
pwmValue = doc["pwm"];
pwmValue = constrain(pwmValue, 0, 255);
analogWrite(FAN_PIN, pwmValue);
Serial.print("PWM set to: ");
Serial.println(pwmValue);
}
if (doc.containsKey("threshold")) {
tempThreshold = doc["threshold"];
Serial.print("Threshold set to: ");
Serial.println(tempThreshold);
}
if (doc.containsKey("sensor")) {
selectedSensor = doc["sensor"].as<String>();
Serial.print("Sensor set to: ");
Serial.println(selectedSensor);
}
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("connected");
client.subscribe(pwm_topic); // Subscribe to PWM control topic
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
//Fetch Weather data and publish via MQTT
void publishWeatherData() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = "http://api.openweathermap.org/data/2.5/weather?q=" + String(city) +
"&appid=" + String(apiKey) + "&units=metric";
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
float temp = doc["main"]["temp"] | 0.0;
int humidity = doc["main"]["humidity"] | 0;
const char* description = doc["weather"][0]["description"] | "N/A";
DynamicJsonDocument weatherDoc(256);
weatherDoc["temperature"] = temp;
weatherDoc["humidity"] = humidity;
weatherDoc["description"] = description;
String jsonStr;
serializeJson(weatherDoc, jsonStr);
client.publish(mqtt_topic, jsonStr.c_str());
Serial.println("🌦 Weather data published via MQTT");
}
}
http.end();
}
}
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback); // Set MQTT callback
dht.begin();
pinMode(FAN_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
//Read Sensors
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int analogValue = analogRead(TMP36PIN);
float voltage = analogValue * (3.3 / 4095.0);
float temperatureTMP36 = (voltage - 0.5) * 100;
float currentTemp = (selectedSensor == "dht22") ? temperature : temperatureTMP36;
unsigned long currentTime = millis();
//Check is temperature exceeds threshold
if (currentTemp > tempThreshold) {
analogWrite(FAN_PIN, pwmValue);
tone(BUZZER_PIN, 1000); // 1 kHz tone for buzzer alert
Serial.println("⚠ Alert: Threshold Exceeded!");
//Only send alert to whatsapp only is cooldown passed
if (currentTime - lastAlertTime > alertCooldown) {
String alertMsg = "🚨 TEMP ALERT!\nSensor: " + selectedSensor +
"\nValue: " + String(currentTemp, 1) + "°C" + "\nThreshold: " + String(tempThreshold) + "°C";
sendWhatsAppAlert(alertMsg);
lastAlertTime = currentTime; // reset cooldown timer
}
} else {
analogWrite(FAN_PIN, 0);
noTone(BUZZER_PIN);
}
// Publish sensor data via MQTT
String payload_dht22 = "{\"temperature_dht22\": \"" + String(temperature) + " °C\"}";
String payload_humidity = "{\"humidity\": \"" + String(humidity) + " %\"}";
String payload_tmp36 = "{\"temperature_tmp36\": \"" + String(temperatureTMP36) + " °C\"}";
client.publish("/temp-dht22/data", payload_dht22.c_str());
client.publish("/humidity/data", payload_humidity.c_str());
client.publish("/temp-analog/data", payload_tmp36.c_str());
// Publish weather data every 10 seconds
static unsigned long lastTime = 0;
if (millis() - lastTime > 10000) {
publishWeatherData();
lastTime = millis();
}
delay(1000);
}