#include <DHTesp.h> // DHT22 Temperature sensor
#include <Adafruit_GFX.h> // OLED display
#include <Adafruit_SSD1306.h> // OLED display
#include <WiFi.h> // Library for connecting the ESP32 to a Wi-Fi network.
#include <PubSubClient.h> // Publish and subscribe to messages on an MQTT broker.
#include <NTPClient.h> // Provides access to NTP servers for synchronizing the ESP32's internal clock.
#include <WiFiUdp.h> // Used by NTPClient for communication with NTP servers.
#include <ESP32Servo.h>
// Define OLED parameters
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
// Define connection pins
#define DHT_PIN 32
#define BUZZER 33
#define SERVO 25
#define ldrRightPin 34
#define ldrLeftPin 35
// To control the OLED display.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Initializing objects related to WiFi, MQTT communication, and time synchronization.
WiFiClient espClient; // A WiFiClient object for establishing a connection to the Wi-Fi network.
PubSubClient mqttClient(espClient); // A PubSubClient object used to interact with the MQTT broker using espClient connection
WiFiUDP ntpUDP; // A WiFiUdp object used by NTPClient for communication with NTP servers.
NTPClient timeClient(ntpUDP); // An NTPClient object for synchronizing the ESP32's clock with an NTP server.
DHTesp dhtSensor; // A DHTesp object representing the DHT sensor.
Servo servo; // // A Servo object representing the servo motor.
// Variable for temperature
char tempAr[6]; // A character array of size 6 to store the temperature reading as a string.
// Variable for Light Intensity
float ldrRight = 0;
float ldrLeft = 0;
char lightAr[6];
String highIntensity;
// Constants for default control parameters
const float D_RIGHT_HIGH = 0.5; // Default value for D when right LDR detects high intensity
const float D_LEFT_HIGH = 1.5; // Default value for D when left LDR detects high intensity
// Variable for Servo Motor
float angle_offset = 30; // Initial angle offset
float controlling_factor = 0.75; // Initial controlling factor
char control_factor[6];
char min_angle[6];
// Variables for scheduling alarm
bool isScheduledON = false;
unsigned long scheduledOnTime;
/***************************************************************************************************/
/***************************************************************************************************/
void setup() {
Serial.begin(115200);
pinMode(BUZZER, OUTPUT);
pinMode(ldrRightPin, INPUT);
pinMode(ldrLeftPin, INPUT);
setupDisplay();
setupWifi();
setupMqtt();
//setupServo();
display.clearDisplay();
print_line("Welcome to Medibox!", 0, 15, 2);
delay(500);
dhtSensor.setup(DHT_PIN, DHTesp :: DHT22); // Initializes the DHT sensor object (dhtSensor).
timeClient.begin(); // Initializing the NTP client for communication with NTP servers.
timeClient.setTimeOffset(5.5 * 3600); // Adjust the retrieved NTP time to match your local time zone.
servo.attach(SERVO);
servo.write(angle_offset);
digitalWrite(BUZZER, LOW);
}
/***************************************************************************************************/
/***************************************************************************************************/
void loop() {
if (!mqttClient.connected()) { // Checks if the connection to the MQTT broker is lost.
connectToBroker(); // Reconnects if necessary using connectToBroker().
}
mqttClient.loop(); // Keeps the MQTT client connection active and manages message exchange.
display.clearDisplay();
updateTemperature();
lightCompare();
slidingWindow();
checkSchedule();
delay(500);
}
/***************************************************************************************************/
/***************************************************************************************************/
void setupDisplay() {
// Initializes the OLED display. If unsuccessful, an error message is printed.
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
// Adafruit boot screen
display.display();
delay(500);
}
/***************************************************************************************************/
void setupWifi() {
WiFi.begin("Wokwi-GUEST", "", 6);
display.clearDisplay();
print_line("Connecting", 0, 0, 2);
print_line("to WIFI", 0, 20, 2);
int counter = 0;
while (WiFi.status() != WL_CONNECTED) { // While wifi not connected
print_line("-", 5 * counter, 40, 4); // Simulate a loading bar
counter += 1;
}
display.clearDisplay();
print_line("WIFI", 0, 0, 2);
print_line("Connected", 0, 20, 2);
delay(500);
}
/***************************************************************************************************/
void setupMqtt() {
mqttClient.setServer("test.mosquitto.org", 1883); // Sets the MQTT broker server.
mqttClient.setCallback(receiveCallback);
// setCallback function allows you to specify a function that will be called
// whenever a message is received on a topic the client is subscribed to.
}
/***************************************************************************************************/
void print_line(String text, int column, int row, int text_size) {
display.setTextSize(text_size);
display.setTextColor(SSD1306_WHITE);
display.setCursor(column, row);
display.println(text);
display.display();
}
/***************************************************************************************************/
void updateTemperature() {
TempAndHumidity data = dhtSensor.getTempAndHumidity();
String(data. temperature, 2).toCharArray(tempAr, 6);
mqttClient.publish("EE-Arjun-TEMP", tempAr); // Publishes tempAr to the MQTT broker.
if (data.temperature > 32) {
print_line("TEMP HIGH", 15, 10, 2);
}
else if (data.temperature < 26) {
print_line("TEMP LOW", 15, 10, 2);
}
if (data.humidity > 80) {
print_line("HUM HIGH", 15, 40, 2);
}
else if (data.humidity < 60) {
print_line("HUM LOW", 15, 40, 2);
}
delay(200);
}
/***************************************************************************************************/
void connectToBroker() {
while (!mqttClient.connected()) {
Serial.println("Attempting MQTT connection ... ");
if (mqttClient.connect("ESP32-56465653791")) { // The connect() method returns true if the connection is successful
Serial.println("Connected to MQTT broker");
mqttClient.subscribe("EE-ARJUN-MAIN-ON-OFF");
mqttClient.subscribe("EE-ARJUN-SCH-ON-OFF");
mqttClient.subscribe("EE-ARJUN-CON-FACT");
mqttClient.subscribe("EE-ARJUN-OFFSET");
} else {
Serial.print("failed ");
Serial.println(mqttClient.state()); // To get the connection error code.
delay(2000);
}
}
}
/***************************************************************************************************/
// This function gets called whenever a message is received on a topic the ESP32 is subscribed to.
void receiveCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
char payloadCharAr[length];
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]); // Prints each byte of the payload data as a character.
payloadCharAr[i] = (char)payload[i]; // Copies each byte of the payload data into the payloadCharAr character array
}
Serial.println();
// strcmp - String compare
// If topic and EE-ARJUN-MAIN-ON-OFF matches, strcmp returns 0
if (strcmp(topic, "EE-ARJUN-MAIN-ON-OFF") == 0) {
buzzerOn(payloadCharAr[0] == 't');
}
else if (strcmp(topic, "EE-ARJUN-SCH-ON-OFF") == 0) {
if (payloadCharAr[0] == 'N') {
isScheduledON = false;
} else {
isScheduledON = true;
scheduledOnTime = atol(payloadCharAr);
}
}
else if (strcmp(topic, "EE-ARJUN-CON-FACT") == 0) {
controlling_factor = atof(payloadCharAr); // Update the controlling factor
Serial.print("Control factor = ");
Serial.println(controlling_factor);
slidingWindow(); // Adjust servo motor based on the updated parameters
}
else if (strcmp(topic, "EE-ARJUN-OFFSET") == 0) {
angle_offset = atof(payloadCharAr); // Update the offset angle
Serial.print("Offset angle = ");
Serial.println(angle_offset);
String(angle_offset).toCharArray(min_angle, 6);
slidingWindow(); // Adjust servo motor based on the updated parameters
}
}
/***************************************************************************************************/
void buzzerOn(bool on) {
if (on) {
tone(BUZZER, 256);
} else {
noTone(BUZZER);
delay(2);
}
}
/***************************************************************************************************/
unsigned long getTime() {
timeClient.update();
return timeClient.getEpochTime();
}
/***************************************************************************************************/
void checkSchedule() {
if (isScheduledON) {
unsigned long currentTime = getTime();
if (currentTime > scheduledOnTime) {
buzzerOn(true);
isScheduledON = false;
mqttClient. publish("EE-ARJUN-MAIN-ON-OFF-ESP", "1");
mqttClient. publish("EE-ARJUN-SCH-ESP-ON", "0");
Serial.println("Scheduled On");
}
}
}
/***************************************************************************************************/
void lightCompare() {
analogReadResolution(10); // Set resolution to 10 bits (0-1023)
ldrRight = analogRead(ldrRightPin);
ldrLeft = analogRead(ldrLeftPin);
ldrRight = (float)(1 - (ldrRight - 8) / (1015.0 - 8.0));
ldrLeft = (float)(1 - (ldrLeft - 8) / (1015.0 - 8.0));
// Print the measured light intensities to the serial monitor
Serial.print("Left LDR : " + String(ldrLeft) + " ");
Serial.println("Right LDR : " + String(ldrRight));
if (ldrRight > ldrLeft){
String(ldrRight, 2).toCharArray(lightAr, 6);
highIntensity = "Right LDR";
}
else if (ldrRight < ldrLeft){
String(ldrLeft, 2).toCharArray(lightAr, 6);
highIntensity = "Left LDR";
}
else{
String(ldrRight, 2).toCharArray(lightAr, 6);
highIntensity = "Equal in both LDR";
}
mqttClient.publish("EE-Arjun-LIGHT", lightAr);
mqttClient.publish("EE-Arjun-HIGH-LIGHT", highIntensity.c_str());
}
/***************************************************************************************************/
void slidingWindow() {
float D = 0;
if (ldrLeft > ldrRight) {
D = D_LEFT_HIGH;
} else if (ldrLeft < ldrRight) {
D = D_RIGHT_HIGH;
}
int angle = min(int((angle_offset * D) + ((180 - angle_offset) * max(ldrLeft, ldrRight) * controlling_factor)), 180);
servo.write(angle);
}