#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHTesp.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Sharing ESP32 reset pin
#define SCREEN_ADDRESS 0x3C // Default I2C address of the SSD1306 module
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org"); // Fetch the time
int utc_offset = 0; // Define initial UTC offset for startup
#define LED 13 // LED == 13
#define Buzzer 12 // Buzzer == 12
int C = 262;
int D = 294;
int E = 330;
int F = 349;
int G = 392;
int A = 440;
int B = 494;
int C_H = 523;
int button1 = 34; // active high
int button2 = 35; // active high
int button3 = 32; // active high
int button4 = 33; // active high
const int DHT_PIN = 14;
DHTesp dhtSensor; // Variable for DHT sensor
// Define button states
int buttonState1;
int buttonState2;
int buttonState3;
int buttonState4;
// Define current hour and time
int Chours, Cminutes;
// define alarms as global variables
String Alarms[] = {"00 : 00 Disable", "00 : 00 Disable", "00 : 00 Disable"}; // Use 24h format for time
String temp, humi;
void setup(){
Serial.begin(115200);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
// initialize the digital pins as inputs
pinMode(button1, INPUT);
pinMode(button2, INPUT);
pinMode(button3, INPUT);
pinMode(button4, INPUT);
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't processed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay (1000); // Pause for 2 seconds
// Clear thr buffer
display.clearDisplay();
display.setTextSize(1); // 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display("Welcome to", 33, 20); // Start at middle
OLED_display("MEDIBOX!", 40, 35);
pinMode(LED,OUTPUT);
pinMode(Buzzer, OUTPUT);
// Start indicate to Meedibox is ON
int notes[] = {D, F, A, C, C_H}; // Define the notes
int states[] = {HIGH, LOW};
int duration = 200; // Set the duration for each note
for (int i = 0; i < 5; i++) {
digitalWrite(LED, states[i%2]);
tone(Buzzer, notes[i]);
delay(duration);
}
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off the tone
// End of indication
WiFi.begin("Wokwi-GUEST", "", 6);
delay(200);
while (WiFi.status() != WL_CONNECTED) {
delay(350);
display.clearDisplay();
OLED_display("Connecting to WIFI..", 5, 27);
}
display.clearDisplay(); //Clear the buffer
OLED_display("WIFI connected", 24, 27); //Show device is now online
delay(400);
timeClient.begin();
timeClient.setTimeOffset(utc_offset);
configTime(utc_offset, 0, "pool.ntp.org");
}
void loop(){
// 1.) Menu with 3 options
display.clearDisplay();
display.setCursor(0,0); // Start at top-left corner
// Use the OLED_display function to display
update_time(1);
menu();
checkTempAndHumi(); // Show temperature and humidity
// Check button states before start the while loop
buttonStates();
int times = 0; // Define variable to number of know iteration
while((buttonState1 != HIGH) & (buttonState2 != HIGH) & (buttonState3 != HIGH)){
buttonStates();
// For get more smooth operations
times ++;
if (times > 500000){
times = 0;
checkTempAndHumi(); // First get temperature and humidity
}
}
if(buttonState1 == HIGH){
UTC_offset();
}
else if(buttonState2 == HIGH){
display.clearDisplay();
// Display the second menu
OLED_display("All alarms,", 0, 0);
OLED_display("1 - "+ Alarms[0], 8, 12);
OLED_display("2 - "+ Alarms[1], 8, 22);
OLED_display("3 - "+ Alarms[2], 8, 32);
OLED_display("4 - Go back", 8, 42);
OLED_display(String(Chours)+ " : "+ String(Cminutes), 45, 55); // Display curent time
buttonStates(); // Check button states before start the while loop
while((buttonState1 != HIGH) & (buttonState2 != HIGH) & (buttonState3 != HIGH) & (buttonState4 != HIGH)){
buttonStates();
}
if(buttonState1 == HIGH){
display.setTextColor(SSD1306_BLACK); // Show selected alarm
OLED_display(" "+ Alarms[0].substring(0, 2) +" "+ Alarms[0].substring(5), 8, 12);
OLED_display("2 - ", 8, 22);
OLED_display("3 - ", 8, 32);
setAlarm(1, 8, 12);
}
else if(buttonState2 == HIGH){
display.setTextColor(SSD1306_BLACK); // Show selected alarm
OLED_display("1 - ", 8, 12);
OLED_display(" "+ Alarms[1].substring(0, 2) +" "+ Alarms[1].substring(5), 8, 22);
OLED_display("3 - ", 8, 32);
setAlarm(2, 8, 22);
}
else if(buttonState3 == HIGH){
display.setTextColor(SSD1306_BLACK); // Show selected alarm
OLED_display("1 - ", 8, 12);
OLED_display("2 - ", 8, 22);
OLED_display(" "+ Alarms[2].substring(0, 2) +" "+ Alarms[2].substring(5), 8, 32);
setAlarm(3, 8, 32);
}
}
else {
// Disable all alarms
Alarms[0] = "00 : 00 Disable";
Alarms[1] = "00 : 00 Disable";
Alarms[2] = "00 : 00 Disable";
display.clearDisplay();
// Indicate action is success
OLED_display("All alarms", 33, 18);
OLED_display("DISABLED", 40, 33);
successSound();
}
}
void OLED_display(String text, int X_POSITION, int Y_POSITION){
display.setCursor(X_POSITION, Y_POSITION); // Start at top-left corner
display.println(text);
display.display();
}
void buttonStates(){
buttonState1 = digitalRead(button1);
buttonState2 = digitalRead(button2);
buttonState3 = digitalRead(button3);
buttonState4 = digitalRead(button4);
}
void menu(){
OLED_display("MEDIBOX Menu,", 0, 0);
OLED_display("1-Set time zone", 8, 11);
OLED_display("2-Set alarms", 8, 22);
OLED_display("3-Disable all alarms", 8, 33);
OLED_display(String(Chours)+ " : "+ String(Cminutes), 45, 45); // Display curent time
OLED_display("Temp:" + temp + "C", 0, 55); // Display temperature
OLED_display("Humidity:" + humi + "%", 55, 55); // Display humidity
}
void update_time(int k){
configTime(utc_offset, 0, "pool.ntp.org"); // Update new UTC offset
if (k==1){
OLED_display("Updating time..", 20, 27);
}
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
if (k==1){
display.clearDisplay();
OLED_display("Connection Err..", 18, 27);
delay(350);
display.clearDisplay();
}
return;
}
char timeHour[3];
strftime(timeHour, 3, "%H", &timeinfo);
Chours = atoi(timeHour);
char timeMinute[3];
strftime(timeMinute, 3, "%M", &timeinfo);
Cminutes = atoi(timeMinute);
if (k==1){
display.clearDisplay();
}
}
void UTC_offset(){
char S_offset_sign = '+';
int digit[] = {0, 0, 0, 0, 0};
int position = -1;
display.clearDisplay();
OLED_display("Enter UTC offset:", 5, 10);
OLED_display(String(S_offset_sign)+ " "+ String(digit[0])+ " "+ String(digit[1])+ " "+ String(digit[2])+ " "+ String(digit[3])+ " "+ String(digit[4]), 35, 33);
while(position != 5){
buttonStates(); // Check button states before start the while loop
while((buttonState1 != HIGH) & (buttonState2 != HIGH)){
buttonStates();
}
if (position == -1){
if(buttonState1 == HIGH){
display.setTextColor(SSD1306_BLACK); // Remove before offset
OLED_display(String(S_offset_sign)+ " "+ String(digit[0])+ " "+ String(digit[1])+ " "+ String(digit[2])+ " "+ String(digit[3])+ " "+ String(digit[4]), 35, 33);
S_offset_sign = '-';
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display(String(S_offset_sign)+ " "+ String(digit[0])+ " "+ String(digit[1])+ " "+ String(digit[2])+ " "+ String(digit[3])+ " "+ String(digit[4]), 35, 33);
position = 0; // Increase the digit by 0ne
delay(200); // Add delay to give time to update button states
}
else{
position = 0; // Go right of digits of time
delay(200); // Add delay to give time to update button states
}
}
else{
if(buttonState1 == HIGH){
display.setTextColor(SSD1306_BLACK); // Remove before offset
OLED_display(String(S_offset_sign)+ " "+ String(digit[0])+ " "+ String(digit[1])+ " "+ String(digit[2])+ " "+ String(digit[3])+ " "+ String(digit[4]), 35, 33);
digit[position] ++; // Increase the digit by 0ne
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display(String(S_offset_sign)+ " "+ String(digit[0])+ " "+ String(digit[1])+ " "+ String(digit[2])+ " "+ String(digit[3])+ " "+ String(digit[4]), 35, 33);
}
else{
position ++; // Go right of digits of time
delay(200); // Add delay to give time to update button states
}
}
}
int signValue = 1;
if (S_offset_sign == '-'){
signValue = -1;
}
int temp_offset = signValue*((String(digit[0])+ String(digit[1])+ String(digit[2])+ String(digit[3])+ String(digit[4])).toInt());
if ((-43200 < temp_offset) & (temp_offset < 50400)){
utc_offset = temp_offset;
display.clearDisplay();
OLED_display("UTC offset updated", 10, 25);
successSound();
}
else{
display.clearDisplay();
// Indicate action is Failed
OLED_display("ERROR occurred", 20, 25);
errorSound();
}
}
void setAlarm(int alarm, int position_x, int position_y){
OLED_display("4 - ", 8, 42);
int digit[] = {0, 0, 0, 0};
int position = 0; // Set the editing digit
while(position != 4){
buttonStates(); // Check button states before start the while loop
while((buttonState1 != HIGH) & (buttonState2 != HIGH)){
buttonStates();
}
if(buttonState1 == HIGH){
display.setTextColor(SSD1306_BLACK); // Remove before time
OLED_display(String(alarm)+ " - "+ String(digit[0])+ String(digit[1])+ " : "+ String(digit[2])+ String(digit[3]), position_x, position_y);
digit[position] ++; // Increase the time by 0ne
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display(String(alarm)+ " - "+ String(digit[0])+ String(digit[1])+ " : "+ String(digit[2])+ String(digit[3]) + " Enable", position_x, position_y);
}
else{
position ++; // Go right of digits of time
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display(String(alarm)+ " - "+ String(digit[0])+ String(digit[1])+ " : "+ String(digit[2])+ String(digit[3]) + " Enable", position_x, position_y);
delay(200); // Add delay to give time to update button states
}
}
// Discard wrong alarm settings
int hours = atoi((String(digit[0]) + String(digit[1])).c_str());
int minutes = atoi((String(digit[2]) + String(digit[3])).c_str());
if(hours > 23 || minutes > 59){
display.clearDisplay();
// Indicate action is Failed
OLED_display("ERROR occurred", 20, 25);
errorSound();
// Disable set alarm
Alarms[alarm - 1] = "00 : 00 Disable";
}
else{
// Store the alarm
Alarms[alarm - 1] = String(digit[0])+ String(digit[1])+ " : "+ String(digit[2])+ String(digit[3])+" Enable";
display.setTextColor(SSD1306_WHITE); // Draw white text
// Indicate action is success
display.clearDisplay();
// Indicate action is Failed
OLED_display("ALARM SAVED", 30, 25);
successSound();
}
}
void ringAlarm(){
if ((Alarms[0].substring(0, 3).toInt() == Chours) & ((Alarms[0].substring(5, 7).toInt() == Cminutes)) & (Alarms[0].substring(9, 15) == "Enable")){
ringAlarmSupport(0);
}
if ((Alarms[1].substring(0, 3).toInt() == Chours) & ((Alarms[1].substring(5, 7).toInt() == Cminutes)) & (Alarms[1].substring(9, 15) == "Enable")){
ringAlarmSupport(1);
}
if ((Alarms[2].substring(0, 3).toInt() == Chours) & ((Alarms[2].substring(5, 7).toInt() == Cminutes)) & (Alarms[2].substring(9, 15) == "Enable")){
ringAlarmSupport(2);
}
}
void ringAlarmSupport(int i){
display.clearDisplay();
alarmSound(i);
display.clearDisplay();
Alarms[i] = String(0)+ String(0)+ " : "+ String(0)+ String(0)+" Disable";
OLED_display("ALARM DELETED", 25, 25);
successSound();
delay(400);
display.clearDisplay();
menu();
}
void checkTempAndHumi(){ // Output temperature and huminidity
TempAndHumidity data = dhtSensor.getTempAndHumidity();
// Remove before temperature, Humidity and time
display.setTextColor(SSD1306_BLACK);
OLED_display("Temp:" + temp + "C", 0, 55); // Display temperature
OLED_display("Humidity:" + humi + "%", 55, 55); // Display humidity
OLED_display(String(Chours)+ " : "+ String(Cminutes), 45, 45); // Display curent time
update_time(0);
ringAlarm(); // Check for alarms
// Renew temperature, humidity and time
temp = String(data.temperature, 0);
humi = String(data.humidity, 0);
display.setTextColor(SSD1306_WHITE);
OLED_display("Temp:" + temp + "C", 0, 55); // Display temperature
OLED_display("Humidity:" + humi + "%", 55, 55); // Display humidity
OLED_display(String(Chours)+ " : "+ String(Cminutes), 45, 45); // Display curent time
delay(200);
// Check for good conditions
int tempNum = atoi(temp.c_str());
int humiNum = atoi(humi.c_str());
if ( tempNum<26 || tempNum>32 || humiNum<60 || humiNum>80 ){
display.clearDisplay();
OLED_display("Temp:" + temp + "C", 0, 55); // Display temperature
OLED_display("Humidity:" + humi + "%", 55, 55); // Display humidity
OLED_display("Environment is NOT in", 0, 15); // Display warning
OLED_display("healthy limit", 25, 30);
warningSound(); // Play warning sound
display.clearDisplay();
menu(); // Display menu
}
}
void successSound(){
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, D); // Turn on tone
delay(400);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off the tone
delay(200);
}
void errorSound(){
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, C_H); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off tone
delay(200);
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, C_H); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off the tone
}
void warningSound(){
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, G); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off tone
delay(200);
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, G); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off the tone
delay(200);
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, G); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off the tone
}
void alarmSound(int j){
buttonStates();
while((buttonState1 != HIGH) & (buttonState2 != HIGH) & (buttonState3 != HIGH) & (buttonState4 != HIGH)){
buttonStates();
display.setTextColor(SSD1306_WHITE); // Draw white text
OLED_display("Medicine Time", 26, 20);
OLED_display(Alarms[j].substring(0, 7), 44, 35); // Show alarm time
display.setTextColor(SSD1306_BLACK); // Draw white text
OLED_display(Alarms[j].substring(0, 7), 44, 35); // Show alarm time
display.setTextColor(SSD1306_WHITE); // Draw white text
digitalWrite(LED, HIGH); // Turn on LED
tone(Buzzer, C); // Turn on tone
delay(200);
digitalWrite(LED, LOW); // Turn off LED
noTone(Buzzer); // Turn off tone
}
}