#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHTesp.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#define Screen_Width 128
#define Screen_Height 64
#define OLED_reset -1
#define screen_address 0x3Co
#define Buzzer 14
#define PB_Cancel 13
#define PB_OK 33
#define PB_UP 35
#define PB_Down 32
#define DHTPIN 26
#define AlarmLed 2
#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET_DST 5.5 * 3600
#define MOTOR 18
#define LDR_Right 36
#define LDR_Left 39
WiFiClient espClient;
PubSubClient mqttClient(espClient);
Servo servo;
Adafruit_SSD1306 display(Screen_Width,Screen_Height,&Wire,OLED_reset);
DHTesp dhtSensor;
int days = 0;
int hours = 0;
int minitues = 0;
int seconds = 0;
int UTC_OFFSET = 0;
int timezone_hours = 0;
int timezone_minitues = 0;
bool alarm_enabled = true;
int n_alarms = 3;
int alarm_hours[] = {9,9,11};
int alarm_minitues[] = {30,32,28};
bool alarm_triggered[] = {false,false,false};
int current_mode = 0;
int max_modes = 5;
String modes[] = {"1-Set Time Zone","2-Set Alarm 1","3-Set Alarm 2","4-Set Alarm 3","5 - Cancel alarms"};
int angle = 0;
int Angle_Offset = 30;
float lux = 0.5;
float Controlling_Factor = 0.75;
char tempAr[6];
char intensity_Arr[6];
char max_Intensity_LDR[6];
float D = 0;
void setup()
{
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.display();
delay(2000);
display.clearDisplay();
print_line("Welcome to Medibox",0,0,2);
delay(2000);
display.clearDisplay();
pinMode(Buzzer, OUTPUT);
pinMode(AlarmLed, OUTPUT);
pinMode(PB_OK, INPUT);
pinMode(PB_Cancel, INPUT);
pinMode(PB_UP, INPUT);
pinMode(PB_Down, INPUT);
pinMode(LDR_Right, INPUT);
pinMode(LDR_Left, INPUT);
pinMode(MOTOR, OUTPUT);
dhtSensor.setup(DHTPIN,DHTesp::DHT22);
setupWifi();
setupMqtt();
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
servo.attach(MOTOR);
}
void loop()
{
update_time_with_check_alarm();
check_Temp_and_Humidity();
delay(1000);
if(digitalRead(PB_OK)== LOW ){
delay(200);
goToMenu();
}
if(!mqttClient.connected()){
connectToBroker();
}
mqttClient.loop();
mqttClient.publish("Medibox-Temp",tempAr);
mqttClient.publish("Medibox-Intensity",intensity_Arr);
mqttClient.publish("Medibox-Intensity-LDR",max_Intensity_LDR);
updateLightIntensity();
rotateMotor();
}
void buzzerOn(bool on){
if(on){
tone(Buzzer,256);
}
else{
noTone(Buzzer);
}
}
void setupMqtt(){
mqttClient.setServer("test.mosquitto.org",1883);
mqttClient.setCallback(receive);
}
void receive(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]);
payloadCharAr[i] = (char)payload[i];
}
Serial.println("");
if(strcmp(topic,"Medibox-Alarm") == 0){
if(payloadCharAr[0]=='1'){
buzzerOn(true);
}
else{
buzzerOn(false);
}
}
if(strcmp(topic,"ADMIN-ANGLE-OFFSET") == 0){
Angle_Offset = atoi(payloadCharAr);
Serial.println(Angle_Offset);
}
if(strcmp(topic,"ADMIN-CONTROLLING_FACTOR") == 0){
Controlling_Factor = atof(payloadCharAr);
Serial.println(Controlling_Factor);
}
}
void connectToBroker(){
while (!mqttClient.connected()){
Serial.println("Attempting MQTT Connection...");
if(mqttClient.connect("Esp32-123456")){
Serial.println("Connected");
mqttClient.subscribe("Medibox-Alarm");
mqttClient.subscribe("ADMIN-ANGLE-OFFSET");
mqttClient.subscribe("ADMIN-CONTROLLING_FACTOR");
}
else{
Serial.print("Failed");
Serial.print(mqttClient.state());
Serial.println("");
delay(5000);
}
}
}
void updateLightIntensity(){
int analogValue_Right = 4063-analogRead(LDR_Right);
int analogValue_Left = 4063-analogRead(LDR_Left);
int analogValue = 0;
String side = "";
if(analogValue_Right >= analogValue_Left){
analogValue = analogValue_Right;
side = "Right";
D = 0.5;
}
else{
analogValue = analogValue_Left;
side = "Left";
D = 1.5;
}
lux = (float)analogValue/4031;
String(lux).toCharArray(intensity_Arr,6);
side.toCharArray(max_Intensity_LDR,6);
Serial.println(lux);
}
void rotateMotor(){
angle = min((float)180.0,Angle_Offset*D + (180 - Angle_Offset) * lux * Controlling_Factor);
servo.write(angle);
delay(15);
}
void setupWifi(){
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
display.clearDisplay();
print_line("Coonnecting to wifi",0,0,2);
}
display.clearDisplay();
print_line("Connected to the wifi",0,0,2);
}
void print_line(String text , int column,int row,int text_size){
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(column,row);
display.println(text);
display.display();
}
void print_time_now(void){
display.clearDisplay();
print_line(String(days),0,0,2);
print_line(":",20,0,2);
print_line(String(hours),30,0,2);
print_line(":",50,0,2);
print_line(String(minitues),60,0,2);
print_line(":",80,0,2);
print_line(String(seconds),90,0,2);
}
//Updates the internal time variables based on the current local time.
void updateTime(){
struct tm timeinfo;
getLocalTime(&timeinfo);
char timeHour[3];
strftime(timeHour,3,"%H",&timeinfo);
hours = atoi(timeHour);
char timeMinitue[3];
strftime(timeMinitue,3,"%M",&timeinfo);
minitues = atoi(timeMinitue);
char timeSecond[3];
strftime(timeSecond,3,"%S",&timeinfo);
seconds = atoi(timeSecond);
char timeDay[3];
strftime(timeDay,3,"%d",&timeinfo);
days = atoi(timeDay);
}
void update_time_with_check_alarm(){
updateTime();
print_time_now();
if(alarm_enabled == true){
for(int i=0; i<n_alarms; i++){
if(alarm_triggered[i] == false && alarm_hours[i] == hours && alarm_minitues[i]== minitues){
ring_alarm(i);
}
}
}
}
void ring_alarm(int i){
display.clearDisplay();
print_line("Medicine Time",0,0,2);
while(digitalRead(PB_Cancel) == HIGH){
tone(Buzzer,256);
tone(Buzzer,290);
digitalWrite(AlarmLed, HIGH);
delay(200);}
noTone(Buzzer);
digitalWrite(AlarmLed, LOW);
alarm_triggered[i] = true;
}
int wait_for_button_press(){
while(true){
if(digitalRead(PB_UP) == LOW){
delay(200);
return PB_UP;
}
else if(digitalRead(PB_Down )== LOW){
delay(200);
return PB_Down;
}
else if(digitalRead(PB_OK )== LOW ){
delay(200);
return PB_OK;
}
else if(digitalRead(PB_Cancel )== LOW){
delay(200);
return PB_Cancel;
}
updateTime();
}
}
void goToMenu(){
while(digitalRead(PB_Cancel) == HIGH){
display.clearDisplay();
print_line(modes[current_mode],0,0,2);
delay(200);
int pressed = wait_for_button_press();
if(pressed == PB_UP){
delay(200);
current_mode +=1;
current_mode = current_mode % max_modes;
}
else if(pressed == PB_Down){
delay(200);
current_mode -=1;
if(current_mode < 0){
current_mode = max_modes - 1;
}
}
else if(pressed == PB_OK){
delay(200);
run_mode(current_mode);
}
else if(pressed == PB_Cancel){
delay(200);
break;
}
}
}
//set the time zone by getting offset from the user
void set_time_zone(){
int temp_hours = timezone_hours;
while(true){
display.clearDisplay();
print_line("Enter hour: " + String(temp_hours),0,0,2);
int pressed = wait_for_button_press();
if(pressed == PB_UP){
delay(200);
temp_hours +=1;
if(temp_hours > 14){
temp_hours = -12;
}
}
else if(pressed == PB_Down){
delay(200);
temp_hours -=1;
if(temp_hours < -12){
temp_hours = 14;
}
}
else if(pressed == PB_OK){
delay(200);
timezone_hours = temp_hours;
break;
}
else if(pressed == PB_Cancel){
delay(200);
break;
}
}
int temp_minitues = timezone_minitues;
while(true){
display.clearDisplay();
print_line("Enter minite: " + String(temp_minitues),0,0,2);
int pressed = wait_for_button_press();
if(pressed == PB_UP){
delay(200);
temp_minitues +=15;
temp_minitues = temp_minitues % 60;
}
else if(pressed == PB_Down){
delay(200);
temp_minitues -=15;
if(temp_minitues < 0){
temp_minitues = 45;
}
}
else if(pressed == PB_OK){
delay(200);
timezone_minitues = temp_minitues;
break;
}
else if(pressed == PB_Cancel){
delay(200);
break;
}
}
display.clearDisplay();
UTC_OFFSET = 3600 * timezone_hours + 60* timezone_minitues;
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
print_line("Time Zone is set",0,0,2);
delay(500);
}
void set_alarm(int alarm){
int temp_hours = alarm_hours[alarm];
while(true){
display.clearDisplay();
print_line("Enter hour: " + String(temp_hours),0,0,2);
int pressed = wait_for_button_press();
if(pressed == PB_UP){
delay(200);
temp_hours +=1;
temp_hours = temp_hours % 24;
}
else if(pressed == PB_Down){
delay(200);
temp_hours -=1;
if(temp_hours < 0){
temp_hours = 23;
}
}
else if(pressed == PB_OK){
delay(200);
alarm_hours[alarm] = temp_hours;
break;
}
else if(pressed == PB_Cancel){
delay(200);
break;
}
}
int temp_minitues = alarm_minitues[alarm];
while(true){
display.clearDisplay();
print_line("Enter minite: " + String(temp_minitues),0,0,2);
int pressed = wait_for_button_press();
if(pressed == PB_UP){
delay(200);
temp_minitues +=1;
temp_minitues = temp_minitues % 60;
}
else if(pressed == PB_Down){
delay(200);
temp_minitues -=1;
if(temp_minitues < 0){
temp_minitues = 59;
}
}
else if(pressed == PB_OK){
delay(200);
alarm_minitues[alarm] = temp_minitues;
break;
}
else if(pressed == PB_Cancel){
delay(200);
break;
}
}
display.clearDisplay();
alarm_enabled = true;
alarm_triggered[alarm] = false; //If an alarm is canceled and then set again, the alarm should be set.
print_line("Alarm"+ String(alarm+1) +"is set",0,0,2);
delay(500);
}
// run mode that choose from the menu
void run_mode(int mode){
if(mode == 0){
set_time_zone();
}
else if(mode == 1){
set_alarm(0);
}
else if(mode == 2){
set_alarm(1);
}
else if(mode == 3){
set_alarm(2);
}
else if(mode == 4){
alarm_enabled = false;
print_line("Alarms are disabled",0,0,2);
delay(500);
}
}
// check temperature and humidity
void check_Temp_and_Humidity(){
TempAndHumidity data = dhtSensor.getTempAndHumidity();
String(data.temperature,2).toCharArray(tempAr,6);
if (data.temperature>32){
display.clearDisplay();
print_line("Temparature is high",0,20,1);
delay(500);
}
else if (data.temperature<26){
display.clearDisplay();
print_line("Temparature is low",0,20,1);
delay(500);
}
if (data.humidity>80){
display.clearDisplay();
print_line("Humidity is high",0,20,1);
delay(500);
}
else if (data.humidity<60){
display.clearDisplay();
print_line("Humidity is low",0,20,1);
delay(500);
}
}