/*
Author: Alexandre Clem
Date: 02/02/2024
Description:
The code implements a system to collect temperature and humidity data from a DHT22
sensor connected to an ESP32 microcontroller. It provides two interfaces to interact
with the system:
1. Local Web Server: Allows users to view temperature and humidity data obtained through
a moving average. Additionally, users can configure the number of points used in the
moving average calculation and the delay between readings.
2. MQTT Server: Publishes average temperature and humidity data for potential consumption
by other clients.
Note:
To test, you need the Wokwi IoT Gateway, as explained here:
https://docs.wokwi.com/guides/esp32-wifi#the-private-gateway
Then start the simulation, and open http://localhost:9080 in another browser tab.
The IoT Gateway requires a Wokwi Club subscription. To purchase a Wokwi Club
subscription, go to https://wokwi.com/club
*/
#include <DHTesp.h>
#include <WiFi.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include "WebPage.h"
#define WIFI_DELAY 10
#define MQTT_SERVER_CONN_DELAY 1000
// Pin used for DHT22
const int DHT_PIN = 15;
DHTesp dhtSensor;
// WiFi Connection
const char* ssid = "Wokwi-GUEST";
const char* password = "";
WiFiClient espClient;
// Esp32 Local Web Server
WebServer webServer(80);
// MQTT Server (HiveMQ)
// Using the topic as parts of a random MD5 hash
const char* mqtt_server = "broker.mqttdashboard.com";
const char* SUBTOPIC_AVG_TEMPERATURE = "4940550b6c625f638387cc0a3f9bea05e29b6806626c62"
"5f638387cc0a3fd84940550bc8eed/AVG-TEMPERATURE";
const char* SUBTOPIC_AVG_HUMIDITY = "4940550b6c625f638387cc0a3f9bea05e29b6806626c62"
"5f638387cc0a3fd84940550bc8eed/AVG-HUMIDITY";
PubSubClient client(espClient);
// Settings
bool newSettings = false; // Variable to verify if new settings was received by the Web Server
int DELTA_TO_READ = 1000; // Default time to read temperature and humidity
int NUM_MOVING_AVERAGE_POINTS = 5; // Number of points used in the moving average
// Buffers to calculate the moving average
int bufferIndex = 0;
float* temperatureBuffer = (float *)calloc(NUM_MOVING_AVERAGE_POINTS, sizeof(float));
float* humidityBuffer = (float *)calloc(NUM_MOVING_AVERAGE_POINTS, sizeof(float));
bool isFullFirstTime = false; // Variable to verify if the buffer is full for the first time
// Variables to calculate the moving average
float averageTemperature = 0.0;
float averageHumidity = 0.0;
// Default WiFi set up function
void setup_wifi() {
delay(WIFI_DELAY);
Serial.println();
Serial.print("\nConnecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(5 * WIFI_DELAY);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi Connected");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}
// Functions to handle HTTP Requests
void handleRoot() {
webServer.send(200, "text/html", htmlPage);
}
void handleSettings() {
Serial.println("~ New Settings Received ~");
if (webServer.hasArg("points") && webServer.hasArg("delay")) {
// Extract values from the URL parameters
int newPoints = webServer.arg("points").toInt();
int newDelay = webServer.arg("delay").toInt();
// Update the variables with the new values
NUM_MOVING_AVERAGE_POINTS = newPoints;
DELTA_TO_READ = newDelay * 1000;
// Free the buffers memory previously allocated
free(temperatureBuffer);
free(humidityBuffer);
// Allocating again with the new NUM_MOVING_AVERAGE_POINTS
bufferIndex = 0;
temperatureBuffer = (float *)calloc(NUM_MOVING_AVERAGE_POINTS, sizeof(float));
humidityBuffer = (float *)calloc(NUM_MOVING_AVERAGE_POINTS, sizeof(float));
isFullFirstTime = false;
averageTemperature = 0.0;
averageHumidity = 0.0;
// Send a response to indicate success
webServer.send(200, "text/plain", "Settings updated successfully.");
} else {
// If the required parameters are missing, send an error response
webServer.send(400, "text/plain", "Bad Request: Required parameters missing.");
}
}
void handleData() {
Serial.println("~ Average Temperature and Humidity Data Sent ~");
String response = String(averageTemperature) + "," + String(averageHumidity);
webServer.send(200, "text/plain", response);
}
// Default callback function to receive messages from the MQTT server (Not used)
void callback(char* topic, byte* payload, unsigned int length) {}
// Function to connect the first time or reconnect to the MQTT server
void reconnect() {
while (!client.connected()) {
Serial.println("Attempting MQTT Connection...");
String clientId = "esp32-dht22-clientId-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected");
}
else
delay(MQTT_SERVER_CONN_DELAY);
}
}
// Initial configurations: Wifi, WebServer, MQTT Client, DHT22 Sensor
void setup() {
Serial.begin(115200);
setup_wifi();
// HTPP Request handlers for: root page, send temperature and humidity data, and to receive settings
webServer.on("/", HTTP_GET, handleRoot);
webServer.on("/data", HTTP_GET, handleData);
webServer.on("/update", HTTP_POST, handleSettings);
webServer.begin();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
}
// Main loop for:
// Read temperature and humidity values, calculates the moving average and publish this in the MQTT Server
void loop() {
// Handle HTTP clients in the Web Server
webServer.handleClient();
// Trying to connect to the MQTT server
if (!client.connected()) {
reconnect();
}
client.loop();
// Verify if the buffer is full the first time
// Only traverse the buffers to sum everything up only the first time the buffer is full
if (!isFullFirstTime && bufferIndex == NUM_MOVING_AVERAGE_POINTS - 1) {
for (int i = 0; i < NUM_MOVING_AVERAGE_POINTS; ++i) {
averageTemperature += temperatureBuffer[i];
averageHumidity += humidityBuffer[i];
}
averageTemperature /= NUM_MOVING_AVERAGE_POINTS;
averageHumidity /= NUM_MOVING_AVERAGE_POINTS;
isFullFirstTime = true;
}
// Get the raw values of temperature and humidity and put them inside the buffers
bufferIndex = (bufferIndex + 1) % NUM_MOVING_AVERAGE_POINTS;
TempAndHumidity data = dhtSensor.getTempAndHumidity();
float temperatureOldValue = temperatureBuffer[bufferIndex];
float humidityOldValue = humidityBuffer[bufferIndex];
float temperatureNewValue = data.temperature;
float humidityNewValue = data.humidity;
temperatureBuffer[bufferIndex] = temperatureNewValue;
humidityBuffer[bufferIndex] = humidityNewValue;
String strTemperature = String(data.temperature, 1);
String strHumidity = String(data.humidity, 1);
Serial.println("v," + strTemperature + "," + strHumidity);
if (isFullFirstTime) {
// Update the moving average. It is not necessary to traverse the buffers again
averageTemperature += (temperatureNewValue - temperatureOldValue) / NUM_MOVING_AVERAGE_POINTS;
averageHumidity += (humidityNewValue - humidityOldValue) / NUM_MOVING_AVERAGE_POINTS;
// Publish temperature and humidity average values in the MQTT Server
String strAverageTemperature = String(averageTemperature, 1);
String strAverageHumidity = String(averageHumidity, 1);
client.publish(SUBTOPIC_AVG_TEMPERATURE, strAverageTemperature.c_str());
client.publish(SUBTOPIC_AVG_HUMIDITY, strAverageHumidity.c_str());
Serial.println("a," + strAverageTemperature + "," + strAverageHumidity);
}
delay(DELTA_TO_READ);
}