// DHT Logger Source Code
// Version: 0.1.5
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
#include <time.h>
#include <HTTPClient.h>
// WiFi Credentials
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// DHT Sensor Pin
#define DHT_PIN 4 // Change this according to your DHT11/DHT22 analog pin
#define DHT_TYPE DHT22 // Change this according to your DHT sensor types
// MQTT Broker Settings
#define MQTT_BROKER_HOST "bb1b6610.ala.asia-southeast1.emqxsl.com"
#define MQTT_BROKER_PORT 8883
#define MQTT_USERNAME "test_user"
#define MQTT_PASSWORD "test_user!23"
// MQTT Topics
#define MQTT_TOPIC_DATA "DHT_Logger/sensors/data"
#define MQTT_TOPIC_ACK "DHT_Logger/app/ack"
// NTP Server Settings
#define NTP_SERVER "pool.ntp.org"
#define GMT_OFFSET_SEC 25200 // GMT+7 (WIB) = 7 * 3600
#define DAYLIGHT_OFFSET_SEC 0
// Device Configuration
// Remember! Only 100 characters that will be stored in db
#define DEVICE_ID "Testing" // Change this for each device
// Telegram Bot Configuration
#define TELEGRAM_BOT_TOKEN "8435739030:AAECsd1EBVfuftTeSVxrpf0hPrV4ZaQQexM" // Change this to your Telegram Bot Token
#define TELEGRAM_CHAT_ID "-5092256862" // Change this to your Personal/Group Chat ID
// Initialize DHT sensor using defines from secret.h
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClientSecure espClient;
PubSubClient client(espClient);
String mac_address;
// Flag to prevent spamming alerts in Telegram
bool alertSent = false;
unsigned long lastAlertTime = 0;
const signed long ALERT_COOLDOWN = 300000; // 5 minutes cooldown
float TEMP_MAX_THRESHOLD = 30.0; // Change this variable according to your condition!
float HUMI_MAX_THRESHOLD = 75.0; // Change this variable according to your condition!
void setup() {
Serial.begin(115200);
// Connect to WiFi using credentials from secret.h
WiFi.begin(WIFI_SSID, WIFI_PASSWORD, 0, NULL, true);
Serial.print("π‘ Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nβ
WiFi connected");
Serial.print("π IP Address: ");
Serial.println(WiFi.localIP());
// Get MAC address
mac_address = WiFi.macAddress();
Serial.print("π MAC Address: ");
Serial.println(mac_address);
// Initialize NTP
configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);
Serial.println("β° Waiting for NTP time sync...");
struct tm timeinfo;
int retry = 0;
while (!getLocalTime(&timeinfo) && retry < 10) {
delay(1000);
Serial.print(".");
retry++;
}
if (retry < 10) {
Serial.println("\nβ
Time synchronized");
Serial.println(&timeinfo, "π
Current time: %Y-%m-%d %H:%M:%S");
} else {
Serial.println("\nβ Failed to sync time");
}
// Setup MQTT
espClient.setInsecure();
client.setServer(MQTT_BROKER_HOST, MQTT_BROKER_PORT);
client.setKeepAlive(60);
client.setCallback(callback);
// Initialize DHT sensor
dht.begin();
delay(2000); // Wait a few seconds between measurements
Serial.println("π‘οΈ DHT sensor initialized");
reconnect();
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("=== π¨ ACK Message Received from Topic: ");
Serial.print(topic);
Serial.print(" ===");
Serial.println();
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, message);
if (error) {
Serial.println("β JSON Parse Error!");
Serial.print("Error: ");
Serial.println(error.c_str());
return;
}
bool success = doc["success"] | false;
const char* device_id = doc["device_id"] | "N/A";
const char* mac_addr = doc["mac_address"] | "N/A";
const char* timestamp = doc["timestamp"] | "N/A";
Serial.print("π Device ID: ");
Serial.println(device_id);
Serial.print("π» MAC Address: ");
Serial.println(mac_addr);
Serial.print("π Timestamp: ");
Serial.println(timestamp);
if (success) {
int log_id = doc["log_id"] | 0;
const char* message_text = doc["message"] | "Data logged successfully";
Serial.println("β
Status: SUCCESS");
Serial.print("π Log ID: ");
Serial.println(log_id);
Serial.print("π§Ύ Message: ");
Serial.println(message_text);
Serial.println("π Data successfully saved to database!");
} else {
const char* error_msg = doc["error"] | "Unknown error";
Serial.println("β Status: FAILED");
Serial.print("π Error: ");
Serial.println(error_msg);
Serial.println("β οΈ Data was NOT saved to database!");
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("π Connecting to MQTT broker...");
String clientId = "ESP32_DHT_" + mac_address;
clientId.replace(":", "");
if (client.connect(clientId.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) {
Serial.println("connected β
");
client.subscribe(MQTT_TOPIC_ACK);
Serial.printf("π‘ Subscribed to: %s\n", MQTT_TOPIC_ACK);
} else {
Serial.print("failed β, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds...");
delay(5000);
}
}
}
String getISOTimestamp() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("β οΈ Failed to get time");
return "";
}
char buffer[25];
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", &timeinfo);
return String(buffer);
}
// ===========================================
// v0.1.1: Send Alert Notification to Telegram
// ===========================================
bool sendTelegramAlert(float temperature, float humidity) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("β WiFi not connected!");
return false;
}
// Build alert message
String message = "β οΈ *ALERT: Threshold Exceeded!*\n\n"; // β Fixed: single \n
message += "π‘οΈ *Temperature:* " + String(temperature, 1) + "Β°C\n";
message += "π§ *Humidity:* " + String(humidity, 1) + "%\n";
message += "π *Time:* " + getISOTimestamp() + "\n";
message += "π» *Device ID:* " + String(DEVICE_ID) + "\n\n";
// Check which threshold exceeded
if (temperature >= TEMP_MAX_THRESHOLD) {
message += "π₯INFO: Temperature Threshold >= " + String(TEMP_MAX_THRESHOLD, 1) + "Β°C\n";
}
if (humidity >= HUMI_MAX_THRESHOLD) {
message += "π¦INFO: Humidity Threshold >= " + String(HUMI_MAX_THRESHOLD, 1) + "%\n";
}
// Prepare HTTP client
HTTPClient http;
WiFiClientSecure client;
client.setInsecure();
String url = "https://api.telegram.org/bot";
url += TELEGRAM_BOT_TOKEN;
url += "/sendMessage";
bool overallSuccess = true;
int successCount = 0;
int totalTargets = 0;
// Send alert notification to Telegram
#ifdef TELEGRAM_CHAT_ID
totalTargets++;
Serial.println("π¬ Sending Alert Notification to Telegram...");
StaticJsonDocument<512> doc;
doc["chat_id"] = String(TELEGRAM_CHAT_ID);
doc["text"] = message;
doc["parse_mode"] = "Markdown";
String payload;
serializeJson(doc, payload);
http.begin(client, url);
http.addHeader("Content-Type", "application/json");
int httpCode = http.POST(payload);
if (httpCode > 0) {
Serial.println("β
Sent alarm notification (Code: " + String(httpCode) + ")");
successCount++;
} else {
Serial.println("β Failed to send alarm notification");
overallSuccess = false;
}
http.end();
#endif
// Summary of alert notifications
Serial.printf("π Telegram Alert Summary: %d/%d sent successfully\n", successCount, totalTargets);
return overallSuccess;
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Read sensor data
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
if (isnan(humidity) || isnan(temperature)) {
Serial.println("β Failed to read from DHT sensor!");
delay(2000);
return;
}
// ======================================================
// v0.1.1: Check threshold and send Telegram alert
// ======================================================
bool thresholdExceeded = (temperature >= TEMP_MAX_THRESHOLD) ||
(humidity >= HUMI_MAX_THRESHOLD);
unsigned long currentTime = millis();
bool cooldownPassed = (currentTime - lastAlertTime) >= ALERT_COOLDOWN;
if (thresholdExceeded && (!alertSent || cooldownPassed)) {
Serial.println("\nπ¨ THRESHOLD EXCEEDED! Sending alert...");
if (sendTelegramAlert(temperature, humidity)) {
alertSent = true;
lastAlertTime = currentTime;
}
} else if (!thresholdExceeded && alertSent) {
// Reset flag when values return to normal
alertSent = false;
Serial.println("β
Values returned to normal range");
}
// ============================================================
// Get ISO timestamp string
String timestamp = getISOTimestamp();
if (timestamp == "") {
Serial.println("β οΈ No valid timestamp, skipping send");
delay(5000);
return;
}
// Create JSON payload
StaticJsonDocument<256> doc;
doc["device_id"] = DEVICE_ID;
doc["mac_address"] = mac_address;
doc["temperature"] = round(temperature * 10) / 10.0;
doc["humidity"] = round(humidity * 10) / 10.0;
doc["timestamp"] = timestamp;
String payload;
serializeJson(doc, payload);
// Publish to MQTT
Serial.println("\nπ€ Publishing data:");
Serial.println(payload);
if (client.publish(MQTT_TOPIC_DATA, payload.c_str(), false)) {
Serial.println("β
Published successfully");
} else {
Serial.println("β Publish failed");
}
// NON-BLOCKING DELAY: Keep calling client.loop() during wait
unsigned long startWait = millis();
while (millis() - startWait < 30000) { // v0.1.2: Send data every 30 minutes (1800000 ms)
if (!client.connected()) {
reconnect();
}
client.loop();
delay(100);
}
}