// Including Libraries
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHTesp.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiClient.h>
#include <ESP32Servo.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
//#include <math.h>
//Defining Screen parameters
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
//Defining pin arrangements
#define BUZZER 27
#define LED_alarm 14
#define LED_temp 13
#define button_cancel 34
#define button_ok 32
#define button_down 35
#define button_up 33
#define DHTpin 26
#define LDR 39
#define ServoPin 15
WiFiClient espClient;
PubSubClient mqttClient(espClient);
Servo myservo; // create servo object to control a servo
//Defining NTP parameters
WiFiUDP ntpClient;
NTPClient timeClient(ntpClient);
//Declaring objects
DHTesp dhtsensor;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//Global variable
int hours = 0;
int minutes = 0;
int seconds = 0;
int time_offset = 0;
String time_now = "";
String temp_status = "";
String hum_status = "";
char TempAr[6];
char TempHu[6];
char Lightint[6];
char Clock[8];
char ShadeAngle[6];
float LDRvalue = 0.0;
float LDROutput = 0.0;
int ContFactor = 75;
int MinAngle = 30;
int ServoAngle = 0;
bool GUI_alarm_enabled = false;
bool alarm_enabled = false;
int n_alarms = 3;
int alarm_hours[] = {0, 0, 0};
int alarm_minutes[] = {0, 0, 0};
bool alarm_triggered[] = {false, false, false};
unsigned long GUI_Alarm_Time;
// Alarm tone for Buzzer
int n_notes = 5;
int C4 = 262;
int E4 = 330;
int notes[] = {C4,E4};
// Setting up the main menu
int current_menu = 0;
int max_menu = 4;
String menu[] = {"1 - Set\nTime Zone","2 - Set\nAlarm 1","3 - Set\nAlarm 2","4 - Set\nAlarm 3","5 - Remove\nAlarms"};
// Setup
void setup() {
pinMode(BUZZER,OUTPUT);
pinMode(LED_alarm,OUTPUT);
pinMode(LED_temp,OUTPUT);
pinMode(button_cancel,INPUT);
pinMode(button_ok, INPUT);
pinMode(button_down, INPUT);
pinMode(button_up, INPUT);
pinMode(DHTpin, INPUT);
pinMode(LDR, INPUT);
dhtsensor.setup(DHTpin,DHTesp::DHT22);
SetupMQTT();
myservo.attach(ServoPin); // Servo Motor pin settings
Serial.begin(115200);
//Check display functionality
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.display();
delay(1000);
display.clearDisplay();
print_text("Welcome to Medibox!", 2, 20, 2);
delay(1000);
display.clearDisplay();
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
display.clearDisplay();
print_text("Connecting to Wi-FI..", 1, 20, 0);
}
display.clearDisplay();
print_text("Wi-FI Connected..", 1, 20, 0);
timeClient.begin();
timeClient.setTimeOffset(19800); // +5.30hrs of offset is set. This can be change from the main menu upon requirement.
}
// main loop
void loop() {
if (!mqttClient.connected()) {
connectToBroker();
}
if (digitalRead(button_ok) == LOW){
delay(200);
go_to_menu();
}
mqttClient.loop();
check_alarm_and_temp_hum();
print_time_temp_hum();
Update_Lightintensity();
Shade_Control();
}
// Checking alarm time with current time
void check_alarm_and_temp_hum(){
update_time();
if (alarm_enabled){
for(int i=0; i<n_alarms; i++){
if(alarm_triggered[i] == false && alarm_hours[i] == hours && alarm_minutes[i] == minutes){
ring_alarm();
alarm_triggered[i] = true;
}
}
}
if (GUI_alarm_enabled) {
update_time();
unsigned long Current_Time = timeClient.getEpochTime();
if (Current_Time > GUI_Alarm_Time) {
myservo.detach();
ring_alarm();
GUI_alarm_enabled = false;
myservo.attach(ServoPin);
}
}
update_temp_and_hum();
}
// Time update trough NTP
void update_time(void){
timeClient.update();
hours = timeClient.getHours();
minutes = timeClient.getMinutes();
seconds = timeClient.getSeconds();
char TimeArray [8]; //Char Array for store converted two digits numbers
sprintf (TimeArray, "%02u:%02u:%02u\n", timeClient.getHours(), timeClient.getMinutes(), timeClient.getSeconds()); //Converting hours, miniutes and seconds for two digits numbers
time_now = String(TimeArray); // Assinged value of present time
mqttClient.publish("239958U-Clock", TimeArray);
}
// Obtaining temperature and humidity data
void update_temp_and_hum(){
TempAndHumidity data = dhtsensor.getTempAndHumidity();
String(data.temperature, 2).toCharArray(TempAr, 6);
String(data.humidity, 2).toCharArray(TempHu, 6);
mqttClient.publish("239958U-Temp-Data", TempAr);
mqttClient.publish("239958U-Hum-Data", TempHu);
if(data.temperature > 32){
temp_status = "High";
digitalWrite(LED_temp, HIGH);
}
else if(data.temperature < 26){
temp_status = "Low";
digitalWrite(LED_temp, HIGH);
}
else if(26 <= data.temperature <= 32){
temp_status = "Ok";
}
if(data.humidity > 80){
hum_status = "High";
digitalWrite(LED_temp, HIGH);
}
else if(data.humidity < 60){
hum_status = "Low";
digitalWrite(LED_temp, HIGH);
}
else if(60 <= data.humidity <= 80){
hum_status = "Ok";
}
if((data.humidity >= 60 && data.humidity <= 80) && (data.temperature >= 26 && data.temperature <= 32)){
digitalWrite(LED_temp, LOW);
}
}
// Main Screen with time and environment data
void print_time_temp_hum() {
display.clearDisplay();
print_text(time_now + "\n\nTemp " + temp_status + "\nHum " + hum_status, 2, 0, 15);
}
// Displying texts
void print_text(String text, int text_size , int row, int column){
display.setTextSize(text_size);
display.setTextColor(SSD1306_WHITE);
display.setCursor(column,row);
display.println(text);
display.display();
}
//Alarm ringing with tone
void ring_alarm() {
display.clearDisplay();
print_text(" Medicine\n time!", 2, 15, 0);
digitalWrite(LED_alarm, HIGH);
while(digitalRead(button_cancel) == HIGH) {
tone(BUZZER, notes[0]);
delay(500);
noTone(BUZZER);
delay(50);
tone(BUZZER, notes[1]);
delay(200);
noTone(BUZZER);
delay(50);
}
if(digitalRead(button_cancel) == LOW){
delay(200);
digitalWrite(LED_alarm, LOW);
}
}
// Function to check any button press
int wait_for_button_press(){
while(true){
if(digitalRead(button_ok) == LOW){
delay(200);
return button_ok;
}
else if(digitalRead(button_up) == LOW){
delay(200);
return button_up;
}
else if(digitalRead(button_down) == LOW){
delay(200);
return button_down;
}
else if(digitalRead(button_cancel) == LOW){
delay(200);
return button_cancel;
}
}
}
// Accessing menu from buttons
void go_to_menu(){
while(digitalRead(button_cancel) == HIGH){
display.clearDisplay();
print_text(menu[current_menu], 2, 0, 0);
int pressed = wait_for_button_press();
if(pressed == button_up){
delay(200);
current_menu += 1;
if(current_menu > 4){
current_menu = 0;
}
}
else if(pressed == button_down){
delay(200);
current_menu -= 1;
if(current_menu < 0){
current_menu = 4;
}
}
else if(pressed == button_ok){
delay(200);
run_mode(current_menu);
current_menu = 0;
break;
}
else if(pressed == button_cancel){
delay(200);
//configTime(time_offset, UTC_OFFSET_DST, NTP_SERVER); //Update time to avoid time error while using the menu
break;
}
}
}
// Menu selection
void run_mode(int current_menu){
if(current_menu == 0){
set_time_offset();
}
else if(current_menu == 1 || current_menu==2 || current_menu==3){
set_alarm(current_menu - 1) ;
alarm_enabled = true;
}
else if(current_menu == 4){
alarm_enabled = false;
display.clearDisplay();
print_text("Alarms\ndisabled!", 2, 0, 0);
delay(1000);
}
}
// Menu for set Time Zone
void set_time_offset() {
int temp_hour = 0;
while (true) {
display.clearDisplay();
print_text("UTC Hour\noffset:" + String(temp_hour), 2, 0, 0);
int pressed = wait_for_button_press();
if (pressed == button_up) {
delay(200);
temp_hour += 1;
if (temp_hour > 12) {
temp_hour = 0;
}
}
else if (pressed == button_down) {
delay(200);
temp_hour -= 1;
if (temp_hour < -12) {
temp_hour = 0;
}
}
else if (pressed == button_ok) {
delay(200);
time_offset = temp_hour * 3600;
break;
}
else if (pressed == button_cancel) {
delay(200);
break;
}
}
int temp_minute = 0;
while (true) {
display.clearDisplay();
print_text("UTC Minute\noffset:" + String(temp_minute), 2, 0, 0);
int pressed = wait_for_button_press();
if (pressed == button_up) {
delay(200);
temp_minute += 15;
if (temp_minute > 45) {
temp_minute = 0;
}
}
else if (pressed == button_down) {
delay(200);
temp_minute -= 15;
if (temp_minute < -45) {
temp_minute = 0;
}
}
else if (pressed == button_ok) {
delay(200);
time_offset = time_offset + (temp_minute * 60);
Serial.println(time_offset);
display.clearDisplay();
print_text("Time Zone\nis set!", 2, 0, 0);
delay(1000);
//configTime(time_offset, UTC_OFFSET_DST, NTP_SERVER); //Update time to avoid time error while using the menu
timeClient.setTimeOffset(time_offset); // Set Offset
timeClient.update(); //Update time to avoid time error while using the menu
break;
}
else if (pressed == button_cancel) {
delay(200);
break;
}
}
}
// Menu for setting alarms
void set_alarm(int alarm){
int temp_hour = hours;
while(true){
display.clearDisplay();
print_text("Enter\nhour :" + String(temp_hour), 2, 0, 2);
int pressed = wait_for_button_press();
if(pressed == button_up){
delay(200);
temp_hour += 1;
if(temp_hour > 23){
temp_hour = 0;
}
}
else if(pressed == button_down){
delay(200);
temp_hour -= 1;
if(temp_hour < 0){
temp_hour = 23;
}
}
else if(pressed == button_ok){
delay(200);
alarm_hours[alarm] = temp_hour;
break;
}
else if(pressed == button_cancel){
delay(200);
break;
}
}
int temp_minute = minutes;
while(true){
display.clearDisplay();
print_text("Enter\nminute :" + String(temp_minute), 2, 0, 0);
int pressed = wait_for_button_press();
if(pressed == button_up){
delay(200);
temp_minute += 1;
if(temp_minute > 59){
temp_minute = 0;
}
}
else if(pressed == button_down){
delay(200);
temp_minute -= 1;
if(temp_minute < 0){
temp_minute = 59;
}
}
else if(pressed == button_ok){
delay(200);
alarm_minutes[alarm] = temp_minute;
display.clearDisplay();
print_text("Alarm is\nset!", 2, 0, 0);
alarm_enabled = true;
alarm_triggered[alarm] = false;
delay(1000);
break;
}
else if(pressed == button_cancel){
delay(200);
break;
}
}
}
// MQTT Setup
void SetupMQTT() {
mqttClient.setServer("test.mosquitto.org", 1883);
mqttClient.setCallback(ReceiveCallback);
}
// Connection to MQTT and subscribe for topics
void connectToBroker() {
while (!mqttClient.connected()) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect("ESP32-239958U")) {
Serial.println("connected");
mqttClient.subscribe("239958U-Min_Angle");
mqttClient.subscribe("239958U-Cont_Factor");
mqttClient.subscribe("239958U-Alarm");
} else {
Serial.print("failed ");
Serial.print(mqttClient.state());
delay(5000);
}
}
}
// Reading messages from the MQTT Server
void ReceiveCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
char mqttCharAr[length];
for (int i = 0; i < length; i++) {
mqttCharAr[i] = (char)payload[i];
}
if (strcmp(topic, "239958U-Min_Angle") == 0) {
MinAngle = atoi(mqttCharAr);
Serial.print("[");
Serial.print(MinAngle);//print the minimum angle
Serial.print("]");
Serial.println();
delay(200);
} else if (strcmp(topic, "239958U-Cont_Factor") == 0) {
ContFactor = atoi(mqttCharAr);
Serial.print("[");
Serial.print(ContFactor);//print the control fcator
Serial.print("]");
Serial.println();
delay(200);
} else if (strcmp(topic, "239958U-Alarm") == 0) {
if (mqttCharAr[0] == 'N') {
GUI_alarm_enabled = false;
} else {
GUI_alarm_enabled = true;
GUI_Alarm_Time = atol(mqttCharAr);
Serial.print("[");
Serial.print(GUI_Alarm_Time);//print the Alarm time from Epoch in Seconds
Serial.print("]");
Serial.println();
delay(200);
}
}
}
// Transfering LDR Data to Dashboard
void Update_Lightintensity() {
LDRvalue = analogRead(LDR);
LDROutput = map(LDRvalue, 4063, 32, 0, 100);
LDROutput = round(LDROutput);;
String(LDROutput/100, 2).toCharArray(Lightint, 6);
mqttClient.publish("239958U-Lightintensity", Lightint);
}
void Shade_Control() {
ServoAngle = round(MinAngle + (180 - MinAngle) * LDROutput/100 * ContFactor/100);
myservo.write(ServoAngle);
String(ServoAngle).toCharArray(ShadeAngle, 6);
mqttClient.publish("239958U-Shade", ShadeAngle);
Serial.print("[");
Serial.print(ServoAngle);//print the Alarm time from Epoch in Seconds
Serial.print("]");
Serial.println();
delay(200);
}