/* my name is vaishnavi shanmugam and my index number is 210666H .
I'm excited to present my project, a MEDIBOX…
let me walk you through the key features of the MEDIBOX project.
It includes:
1. Setting the time zone: Users can input their UTC offset
to set their local time zone.
2. Setting alarms: Users can set up to three alarms
for medication reminders.
3. Disabling alarms: A feature to disable all alarms if
needed.
4. Fetching time from NTP server: The MEDIBOX fetches
the current time from an NTP server over Wi-Fi and displays
it on an OLED screen.
5. Ringing alarms: When the set alarm times are reached,
the MEDIBOX triggers alarms with proper indications.
6. Stopping alarms: Users can stop alarms using a push button.
7. Monitoring environmental conditions: The MEDIBOX monitors
temperature and humidity levels and providing warnings
if they exceed healthy limits.
Healthy Temperature : 26 ◦C ≤ Temperature ≤ 32 ◦C
Healthy Humidity : 60% ≤ Humidity ≤ 80%*/
//Now, let's DIVE into the structure of my code.
/*.I've included necessary libraries for OLED display,
sensor readings, Wi-Fi connectivity, and time management.*/
//Including all needed libraries for this MEDIBOX project
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <DHTesp.h> // for DHT22 sensor
#include <WiFi.h> // for time from wifi
/*Here, I've declared Global variables to manage time,
alarms, user inputs, and alarm triggers.*/
//Defining OLED Display parameters
#define Screenwidth 128
#define Screenheight 64
#define OLEDReset -1
#define ScreenAddress 0x3C
//Defining the corresponding pins in ESP32 for the elements
#define BUZZER 5
#define LED_1 15
#define PB_CANCEL 34
#define PB_OK 32
#define PB_UP 33
#define PB_DOWN 35
#define DHTPIN 12
//Defining the time from wifi
#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET 19800
#define UTC_OFFSET_DST 0
long utc_offset_in_sec = 0;
Adafruit_SSD1306 display(Screenwidth, Screenheight, &Wire, OLEDReset);
DHTesp dhtSensor;
//Declaration of all Global Variables
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
boolean alarm_enabled = true;
int n_alarms = 3;
int alarm_hours[] = {0,0,0};
int alarm_minutes[] = {0,0,0};
bool alarm_triggered[] = {false, false, false};
int n_notes = 8;
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 notes[] = {C, D, E, F, G, A, B, C_H};
unsigned long timeNow = 0;
unsigned long timeLast = 0;
int current_mode = 0;
int max_modes = 5;
String modes[] = {"1 - Set Time", "2 - Set Alarm 1", "3 - Set Alarm 2", "4 - Set Alarm 3", "5 - Disable Alarms"};
/*then the Setup Function**: In the setup function,
I've initialized pins, sensors, OLED display,
and connected to Wi-Fi.*/
void setup() {
//PIN configurations
pinMode(BUZZER, OUTPUT);
pinMode(LED_1, OUTPUT);
pinMode(PB_CANCEL, INPUT);
pinMode(PB_DOWN, INPUT);
pinMode(PB_OK, INPUT);
pinMode(PB_UP, INPUT);
//Initializing Temperature and Humidity Sensor
dhtSensor.setup(DHTPIN,DHTesp::DHT22);
//Initializing OLED Display
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, ScreenAddress)) {
Serial.println(F("SSD1306 Allocation Failed"));
for(;;);
}
display.display();
delay(500);
//Initializing Wifi
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
display.clearDisplay();
print_line("Connecting to WIFI....", 2, 0, 2);
}
display.clearDisplay();
print_line("Connected to WIFI.", 2, 0, 2);
//Configuring Time Using Wifi
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
display.clearDisplay();
print_line("Welcome to Medi Box", 2, 0, 0);
display.clearDisplay();
}
/*. then the Loop Function**: The loop function
continuously updates the time, checks for user inputs,
and monitors temperature and humidity.*/
void loop(){
update_time_with_check_alarm();
//Peeking in to the menu option by clicking the PushButton OK
if (digitalRead(PB_OK) == LOW){
delay(200);
go_to_menu();
}
check_temp();
}
/* FROM HERE ONWARDS I've implemented various helper functions
for setting the time zone, setting alarms,
disabling alarms, printing on the OLED display, and more.*/
/*This function prints a text string on the OLED
display at the specified position with the specified text size.*/
void print_line(String text, int text_size, int row, int column){
display.setTextSize(text_size); //setting up the text size
display.setTextColor(SSD1306_WHITE); //Default text color(white)
display.setCursor(column,row); // Starting points of the text
display.println(text); // displaying the text on the screen
display.display();
}
/*This function allows the user to set the timezone
by inputting the offset hours and minutes from UTC.*/
void set_the_timezone() {
int current_offset_hours = utc_offset_in_sec / 3600; //Calculating offset in hours
int current_offset_minutes = (utc_offset_in_sec % 3600) / 60; //calculating offset in minutes
int offset_hours = read_data("Enter the offset hours: ", current_offset_hours, 14, -12);
int offset_minutes = read_data("Enter the offset minutes: ", current_offset_minutes, 59, 0);
//Calculating the offset in seconds
utc_offset_in_sec = offset_hours * 3600 + offset_minutes * 60;
configTime(utc_offset_in_sec, UTC_OFFSET_DST, NTP_SERVER);
print_line("Timezone set to " + String(offset_hours) + ":" + String(offset_minutes), 0, 0, 2);
delay(250);
}
/*This function displays the current time(hours, minutes, seconds)
on the OLED display.*/
void print_time_now(void) {
//clear the Display
display.clearDisplay();
print_line(String(hours), 2, 0, 30);
print_line(":" , 2, 0, 50);
print_line(String(minutes), 2, 0, 60);
print_line(":" , 2, 0, 80);
print_line(String(seconds), 2, 0, 90);
}
/*This function updates the global variables `hours`, `minutes`,
and `seconds` with the current time obtained from the system.*/
void update_time(void) {
struct tm timeinfo;
getLocalTime(&timeinfo);
char timeHour[3];
strftime(timeHour,3, "%H", &timeinfo);
hours = atoi(timeHour);
char timeMinute[3];
strftime(timeMinute,3, "%M", &timeinfo);
minutes = atoi(timeMinute);
char timeSecond[3];
strftime(timeSecond,3, "%S", &timeinfo);
seconds = atoi(timeSecond);
}
/*This function triggers an alarm by turning on an
LED and making the buzzer emit a sound.
It waits for the user to press a cancel button to stop the alarm.*/
void ring_alarm(){
//clear the Display
display.clearDisplay();
print_line("MEDICINE TIME", 0, 0, 2);
digitalWrite(LED_1, HIGH);
bool break_happened = false;
//Buzzer Ringing until the Push Button is being Pressed
while(break_happened == false && digitalRead(PB_CANCEL) == HIGH){
for (int i=0; i<n_notes; i++){
if (digitalRead(PB_CANCEL) == LOW){
delay(200);
break_happened == true;
break;
}
tone(BUZZER, notes[i]);
delay(500);
noTone(BUZZER);
delay(2);
}
}
digitalWrite(LED_1, LOW);
//clear the Display
display.clearDisplay();
}
/*This function updates the current time and
checks if any alarms need to be triggered based
on the set alarm times.*/
void update_time_with_check_alarm(void){
update_time();
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_minutes[i] == minutes){
ring_alarm();
alarm_triggered[i] = true;
}
}
}
}
/*This function waits for a button press(up, down, cancel, or OK)
and returns the pressed button's identifier.*/
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_CANCEL)==LOW){
delay(200);
return PB_CANCEL;
}
else if (digitalRead(PB_OK)==LOW){
delay(200);
return PB_OK;
}
update_time();
}
}
/*This function navigates through the menu options
using the up and down buttons and executes the
selected mode when the OK button is pressed.*/
void go_to_menu(void){
while (digitalRead(PB_CANCEL) == HIGH){
display.clearDisplay();
print_line(modes[current_mode], 0, 0, 2);
int pressed = wait_for_button_press();
if (pressed == PB_UP){
delay(200);
current_mode += 1; //current_mode=current_mode+1
current_mode = current_mode % max_modes;
}
else if (pressed == PB_DOWN){
delay(200);
current_mode -= 1; //current_mode=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;
}
}
}
/*This function allows the user to set the
hours and minutes for a specific alarm (1, 2, or 3).*/
void set_alarm(int alarm){
int temp_hour = alarm_hours[alarm];
while(true){
display.clearDisplay();
print_line("Enter hour: " + String(temp_hour), 0, 0, 2);
int pressed = wait_for_button_press();
if (pressed == PB_UP){
delay(200);
temp_hour += 1;
temp_hour = temp_hour % 24;
}
else if (pressed == PB_DOWN){
delay(200);
temp_hour -= 1;
if(temp_hour < 0){
temp_hour = 23;
}
}
else if (pressed == PB_OK){
delay(200);
alarm_hours[alarm] = temp_hour;
break;
}
if (pressed == PB_CANCEL){
delay(200);
break;
}
}
int temp_minute = alarm_minutes[alarm];
while(true){
display.clearDisplay();
print_line("Enter minute: " + String(temp_minute), 0, 0, 2);
int pressed = wait_for_button_press();
if (pressed == PB_UP){
delay(200);
temp_minute += 1;
temp_minute = temp_minute % 60;
}
else if (pressed == PB_DOWN){
delay(200);
temp_minute -= 1;
if(temp_minute < 0){
temp_minute = 59;
}
}
else if (pressed == PB_OK){
delay(200);
alarm_minutes[alarm] = temp_minute;
break;
}
if (pressed == PB_CANCEL){
delay(200);
break;
}
}
display.clearDisplay();
print_line("Alarm is SET", 0, 0, 2);
delay(1000);
}
/*This function executes the selected mode from the menu
(set time, set alarm, disable alarms) based on the user's input.*/
void run_mode(int mode){
if(mode == 0){
set_the_timezone();
}
else if(mode == 1 || mode == 2 || mode == 3){
set_alarm(mode - 1);
}
else if (mode == 4) {
disable_all_the_alarms();
}
}
/*This function reads temperature and humidity data
from the DHT22 sensor and displays warnings on the
OLED display if the values exceed predefined thresholds. */
void check_temp(){
TempAndHumidity data = dhtSensor.getTempAndHumidity() ;
if (data.temperature > 32){
display.clearDisplay();
print_line("TEMP IS HIGH", 0, 40, 1);
}
if (data.temperature < 26){
display.clearDisplay();
print_line("TEMP IS LOW", 0, 40, 1);
}
if (data.humidity > 80){
display.clearDisplay();
print_line("HUMIDITY IS HIGH", 0, 50, 1);
}
if (data.humidity < 60){
display.clearDisplay();
print_line("HUMIDITY IS LOW", 0, 50, 1);
}
}
/*This function allows the user to input data
(e.g., offset hours, alarm hours) using the
up, down, OK, and cancel buttons.*/
int read_data(String text, int init_value, int max_value, int min_value) {
int temp_value = init_value;
while (true) {
display.clearDisplay();
print_line(text + String(temp_value), 0, 0, 2);
int pressed = wait_for_button_press();
if (pressed == PB_UP) {
delay(200);
temp_value += 1;
if (temp_value > max_value) {
temp_value = min_value;
}
}
else if (pressed == PB_DOWN) {
delay(200);
temp_value -= 1;
if (temp_value < min_value) {
temp_value = max_value;
}
}
else if (pressed == PB_OK) {
delay(200);
return temp_value;
}
else if (pressed == PB_CANCEL) {
delay(200);
break;
}
}
return init_value;
}
/*This function disables all alarms by setting the
`alarm_enabled` flag to false and clearing the alarm settings.*/
void disable_all_the_alarms() {
alarm_enabled = false;
// Clear all the alarms
for (int i = 0; i < n_alarms; i++) {
alarm_hours[i] = 0 ;
alarm_minutes[i] = 0 ;
}
display.clearDisplay();
print_line("Alarms are disabled", 0, 0, 2);
delay(1000);
}
/*These ALL functions work together to provide the
core functionality of the MEDIBOX project.
In summary, the MEDIBOX project offers a comprehensive solution
for medication management and environmental monitoring.
I'm grateful for the opportunity to work on this project.
Thank you .*/
//END