I have written a program for a product; it is a pill taking reminder box. It is set to remind the user to take their pills at a certain time every 24 hours. It prompts them via audio and a flashing LED. The inputs are a magnetic switch on the door to reveal their pill tray, an alarm via a RTC and a button that they push to confirm that they have taken their pill. (there is another part of the product that is a book that sequences audio pages along with volume control and language, but that is rather straightforward - so lets ignore that for now).
The sum of these three inputs (RTC alarm, door switch and pill confirm button) determines the current state: A) time to take pill but door closed B) time to take pill and door it opened C) pill-confirm button pushed but door still opened D) pill-confirm button pushed and door closed etc. etc Well you get the idea. There are more than 10 different scenarios and if they don't do anything, it needs to go to sleep and conserve power but flash the LED every so often to deplict a state.
I have some code working but not perfectly and to be truthful, it is a mess. I would like your opinion on how I can make this more simple and straighforward. It makes me think of an Arduino function that is a "Decision Matrix" where a number of different inputs, alarm states etc are in a 2x2 matrix (array?) and can that matrix can be tested continually in a loop and if any one of them is true then it is executed.
BTW, what makes this even harder, is that I'm running audio prompts that require that there be no delays() - so almost every function needs to be in the loop and the use of millis() needs to be incorporated.
Thoughts? Code here for reference:
/* sketch for READ prototype Humanity Press using FireBeetle esp32 DFR0478
Board: esp32/Firebeetle-ESP32 on COM7 Programmer: ESPtool Upload Speed 921600
DS3231 RTC breakout on SLC and SDA
Sparkfun ST25DV64KC Dynamic RFID Tag Breakout
adafruit 254 MicroSD card reader
MAX98357A I2S audio output
*/
#include "RTClib.h"
#include "SparkFun_ST25DV64KC_Arduino_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_ST25DV64KC
#include "Arduino.h"
#include "Audio.h"
#include "SD.h"
#include "FS.h"
#include "esp_sleep.h"
#include <ezButton.h>
#define BUTTON_PIN_BITMASK 0x1000008010 // GPIOs , 36(lid switch), 15(pwr button) and 4(A5) SQW TIMER TRIGGER
#define SD_CS 5 // D9 of Firebeetle microSD Card Reader connections
#define SPI_MOSI 23 // DI of SD Card
#define SPI_MISO 19 // DO of SD Card
#define SPI_SCK 18
#define I2S_DOUT 25 //(D2) DAC0 I2S output for firebeetle
#define I2S_BCLK 14
#define I2S_LRC 17
Audio audio; // Create Audio object
RTC_DS3231 rtc; // create RTC object
DateTime nowDT;
SFE_ST25DV64KC tag; // create RFID object
RTC_DATA_ATTR int bootCount = 0; // for sleep
const int MAX_ANALOG_VAL = 4095;
const float MAX_BATTERY_VOLTAGE = 4.1; // Max LiPoly voltage of a 3.7 battery is 4.2
const int MIN_BATT_THRESHOLD = 1865;
#define uS_TO_S_FACTOR 1000000 // Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 10 // Time ESP32 will sleep (in seconds) and then wake up
////////////////////////// PIN ASSIGNMENTS ///////////////////////////////////////////////
#define CLOCK_INTERRUPT_PIN A3 //A3 Physical Pin on Firebeetle The number of the pin for monitor alarm status on SQW DS3231
int const red_LED = 0; // GPIO 0
int const green_LED = 3;
int const blue_LED = 12;
int const batt_input = A0;
int const power_pin = 15;
int const lid_sw = A1; //reed switch all buttons pulled UP via 10K resistor
int pwr_button(A2);
ezButton pwr_but(A2);
ezButton lang_select_but(26);
ezButton pill_confirm_but(4);
ezButton page_back_but(13);
ezButton page_forw_but(27);
//ezButton vol_up_but(2); // digitalRead not working for GPIO 2 use analogRead instead
int vol_up_button = 2;
ezButton vol_dn_but(16);
bool lid_sw_val;
bool pwr_state = false; // initial set to false
bool lang_state = false;
bool pill_confirm_toggle = false;
bool warning_pill_not_taken = false;
bool vol_up_but_state = false;
int pwr_but_state;
const char* audiofilenames[6][24] = { //various audio files on MicroSD card
/* 0 */ { "tick.mp3", "turntone.mp3", "am.mp3", "pm.mp3", "silence.mp3" },
/* 1 */ { "1-AM.mp3", "2-AM.mp3", "3-AM.mp3", "4-AM.mp3", "5-AM.mp3", "6-AM.mp3", "7-AM.mp3", "8-AM.mp3", "9-AM.mp3", "10-AM.mp3", "11-AM.mp3", "12-PM.mp3", "1-PM.mp3", "2-PM.mp3", "3-PM.mp3", "4-PM.mp3", "5-PM.mp3", "6-PM.mp3", "7-PM.mp3", "8-PM.mp3", "9-PM.mp3", "10-PM.mp3", "11-PM.mp3", "12-PM.mp3" },
/* 2 */ { "day14inst.mp3", "end_book.mp3", "INSTRUCT.mp3", "intro_lg.mp3", "intro_sht.mp3", "med_close.mp3", "CLOSELID.mp3", "CLSELID2.mp3", "WELCOME.mp3", "DOORSTP2.mp3", "DOORSTOP.mp3" },
/* 3 */ { "TAKEMED.mp3", "NEXTTAKE.mp3", "NOT_MED.mp3", "NOT_MED2.mp3", "silence.mp3", "ENGLISH.mp3", "ISIXHOSA.mp3", "ISIZULU.mp3", "takemed3.mp3" },
/* 4 */ { "DAY_1.mp3", "DAY_2.mp3", "DAY_3.mp3", "DAY_4.mp3", "DAY_5.mp3", "DAY_6.mp3", "DAY_7.mp3", "DAY_8.mp3", "DAY_9.mp3", "DAY_10.mp3", "DAY_11.mp3", "DAY_12.mp3", "DAY_13.mp3", "DAY14INST.mp3" },
/* 5 */ { "page1.mp3", "page2.mp3", "page3.mp3", "page4.mp3", "page5.mp3", "page6.mp3", "page7.mp3", "page8.mp3", "page9.mp3", "page10.mp3", "page11.mp3", "page12.mp3", "page13.mp3", "page14.mp3", "page15.mp3", "page16.mp3", "page17.mp3", "page18.mp3", "END_BOOK.mp3" }
};
////////////////////////////// Timing ///////////////////////////////
int long current_Millis;
int long prev_button_debounce_millis = 0;
int long prev_LED_millis = 0;
int long prev_RFID_millis = 0;
int long prev_clock_millis = 0;
int long prev_take_meds_millis = 0;
int long prev_lid_close_finish_millis = 0;
int long prev_not_yet_time_millis = 0;
int long prev_pill_confirm_but_millis = 0;
int long prev_sleep_delay_millis = 0;
int long book_pwr_off_delay_millis = 0;
int16_t button_debounce_time = 250;
int16_t RFID_time = 5000;
int16_t clock_time = 3000;
int16_t LED_pause_time = 100;
int16_t take_med_repeat = 1000; // initially set by alarm and then adjusted in take_med() to be 15 seconds for 10x sleep
int16_t not_time_yet_repeat = 500; // first time led opened and not time to take med set to 500ms then after audio repeat every 15 seconds (set by not_yet_time()) then when lid closed
int16_t pill_conf_loop_time = 3000; // set initially by take_med() to be 3 second
int16_t pill_conf_lid_open_repeat = 15000; // initially set by take_med() to 500ms and then adjusted to 15 seconds by lid_close_finish()
int16_t pill_conf_lid_close_repeat; //
int long sleep_delay_time;
int long sleep_delay_time_no_door_open = 120000; // 5 minutes to sleep if alarm triggered and door not opened
int long sleep_delay_time_pill_confirm_door_closed = 15000; // go to sleep 15000 after confirm pill door closed
int long book_pwr_off_time = 300000; // (300000) 5 minutes
int8_t lid_open_count = 1;
/////////////////////////////// end of timing /////////////////////////////////////////////
int8_t audio_trigger = -1; // this triggers no. of audio prompts for given function - at -1 to keep it false
char audio_array1; // these hold the variables for for the various audio prompts after the main audio prompt in the function
char audio_array2;
char audio_array3;
char audio_array4;
char audio_array5;
char audio_array6;
char audio_array7;
char audio_array8;
int LED_color; // variable for storing LED color;
int Red = 0;
int Green = 1;
int Blue = 2;
int Yellow = 3;
bool LED_status = true; // determines toggle of whether LED is on or off flashing curr_Lang_state
bool LED_trigger = false; // LED switch ON or FF
bool alarm_fired = false;
bool sleep_mode = false;
bool door_left_open = false;
bool not_yet_time_status = false;
byte startup_block = 0; //used to bypass lang prompts in Main loop upon startup
int day_count = 0; // set at zero
int page_count = 0;
int const total_page_count = 19; // total pages plus one for end of page
int volume_level = 15;
void setup() {
pinMode(power_pin, OUTPUT);
pinMode(pwr_button, INPUT);
digitalWrite(power_pin, HIGH);
delay(1000);
Wire.begin();
delay(200);
Serial.begin(115200);
pinMode(batt_input, INPUT);
pinMode(red_LED, OUTPUT);
pinMode(green_LED, OUTPUT);
pinMode(blue_LED, OUTPUT);
pinMode(lid_sw, INPUT);
page_back_but.setDebounceTime(100);
page_forw_but.setDebounceTime(100);
lang_select_but.setDebounceTime(100);
pwr_but.setDebounceTime(100);
//vol_up_but.setDebounceTime(100); // pin 2 not working as digitalRead - analogRead instead
pinMode(vol_up_button, INPUT);
vol_dn_but.setDebounceTime(100);
pill_confirm_but.setDebounceTime(50);
if (!tag.begin(Wire)) { // check if RFID breakout is connected
Serial.println(F("ST25 not detected. Freezing..."));
while (1)
;
} // Do nothing more
Serial.println(F("ST25 connected."));
audio.setVolume(volume_level);
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
////////////////////////// RTC TIME FUNCTION SETUP ///////////////////////////////////////////
rtc.disable32K(); //we don't need the 32K Pin, so disable it
if (rtc.lostPower()) {
Serial.println("RTC lost power and resetting to time of this upload"); // if power loss following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// uncomment following to explicitly set date & time, for example to set January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP); // Making it so, that the alarm will trigger an interrupt
attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);
//esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ALL_LOW); // triggered if go LOW on GPIOs 4, 15 and 36 lid switch, pwr button, RTC SQW TIMER TRIGGER
esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); //1 = High, 0 = Low GPIO 35 A3 of FireBeetle
//esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_OFF); // power down flash
rtc.clearAlarm(1);
rtc.clearAlarm(2);
rtc.writeSqwPinMode(DS3231_OFF); // stop oscillating signals at SQW Pin otherwise setAlarm1 will fail
rtc.disableAlarm(2); // turn off alarm 2 (in case it isn't off already) again, this isn't done at reboot, so a previously set alarm could easily go overlooked
//nowDT = rtc.now(); // set the pill take time at time of reset - regardless of time lapse between 24 hour periods
DateTime now = rtc.now(); // debug
rtc.setAlarm1(now + TimeSpan(0, 0, 3, 0), DS3231_A1_Minute); // debug set alarm for every 2 minutes
DateTime alarmTime = rtc.getAlarm1();
int alarm1_hr = alarmTime.hour(); // notDT is set in setup() at time of reset
int alarm1_min = alarmTime.minute(); // notDT is set in setup() at time of reset
Serial.print("Alarm time is set to: "); // display alarm set time
Serial.print(alarm1_hr);
Serial.print(":");
Serial.println(alarm1_min);
///////////////////////////////////// END RTC FUNCTIONS ///////////////////////////////////////////////////////
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
pinMode(SD_CS, OUTPUT); // Set microSD Card CS as OUTPUT and set HIGH
digitalWrite(SD_CS, HIGH);
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI); // Initialize SPI bus for microSD Card
delay(1000);
if (!SD.begin(SD_CS)) { // Start microSD Card
Serial.println("Error accessing microSD card!");
while (true)
;
}
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT); // Setup I2S
///////////////////// first time reset sets up to take first day medication
LED_trigger = true;
pill_confirm_toggle = false;
alarm_fired = true; // on reset trigger alarm to be true
startup_block = 1;
digitalWrite(2, HIGH); // volume up button set back to high
audio.connecttoFS(SD, "WELCOME.mp3");
}
void loop() {
current_Millis = millis();
audio.loop(); // with audio.loop() nothing in the loop can be blocking
battery_monitor();
Flash_LED(); // indication for various functions
read_buttons(); // continually scans switch button states
power_status(); // monitors power for book function whether on or off
Turn_page(); // turn page forward of backward funtion
Alarm_monitor(); // monitors medication side for when to take next pill/s and door open falsely
//Clock_monitor(); // diagnostic feature
Language_select(); // toggles selected language with audio prompt
Volume_control(); // function to adjust volume for audio
Not_yet_time(); // monitors lid and audio warning if lid is open before time to take meds
Time_to_take(); // when alarm fires loops into this function to flash neopixel if lid is close or audio prompt if lid is open to push confirm button
Lid_close_finish(); // monitors whether pill confirm button is pushed and whether lid is still open or close - if closed next alarm time set and goes to sleep
goto_sleep();
//RFID_info(); // diagnostic feature
}
void audio_eof_mp3(const char* info) {
if (audio_trigger > 0) {
if (audio_trigger == 1) {
audio.connecttoFS(SD, audiofilenames[audio_array1][audio_array2]);
audio_trigger--;
}
if (audio_trigger == 2) {
audio.connecttoFS(SD, audiofilenames[audio_array3][audio_array4]);
audio_trigger--;
}
if (audio_trigger == 3) {
audio.connecttoFS(SD, audiofilenames[audio_array5][audio_array6]);
audio_trigger--;
}
if (audio_trigger == 4) {
audio.connecttoFS(SD, audiofilenames[audio_array7][audio_array8]);
audio_trigger--;
}
}
}
void read_buttons() {
lid_sw_val = digitalRead(lid_sw);
page_back_but.loop(); //MUST call the loop() function first
page_forw_but.loop();
lang_select_but.loop();
pwr_but.loop();
//vol_up_but.loop(); // pin 2 not working as digitalRead - use analogRead instead
vol_dn_but.loop();
pill_confirm_but.loop();
vol_up_but_state = analogRead(vol_up_button);
pwr_but_state = digitalRead(pwr_button);
}
void Not_yet_time() {
if ((current_Millis - prev_not_yet_time_millis > not_time_yet_repeat) && (lid_sw_val == LOW) && (not_yet_time_status == true) && (lid_open_count < 10)) { //lid is opened but not yet time to take meds
digitalWrite(power_pin, HIGH);
sleep_mode = false;
audio_trigger = 1;
audio_array1 = 2;
audio_array2 = 6; // close the lid
audio.connecttoFS(SD, audiofilenames[3][3]);
Serial.println("not yet time - close the lid");
lid_open_count++;
if (lid_open_count > 1) {
not_time_yet_repeat = 15000; // after first count make repeats time every 15 seconds for 10X
}
prev_not_yet_time_millis = current_Millis;
}
else if ((current_Millis - prev_not_yet_time_millis > not_time_yet_repeat) && (lid_sw_val == LOW) && (alarm_fired == false) && (lid_open_count >= 10)) { // lid is opened and 10x audio warnings given
sleep_mode = true;
sleep_delay_time = 1000;
not_yet_time_status = false; // cancel audio warning after 10x
prev_not_yet_time_millis = current_Millis;
}
else if ((lid_sw_val == HIGH) && (not_yet_time_status == true) && (pwr_state == false)) { // lid is close and not yet time true as well as power state is off
sleep_delay_time = 15000;
sleep_mode = true;
not_time_yet_repeat = 500; // resets to first time lid is opened and not yet time
lid_open_count = 1;
}
}
void Time_to_take() {
if (pill_confirm_but.isPressed() && !pill_confirm_toggle) { // time to take and lid opened but pill confirmed pushed
not_yet_time_status = false; // allows for door to be opened normally
pill_confirm_toggle = true;
warning_pill_not_taken = false;
lid_open_count = 1; // reset for Lid_close_finish to respond immediately first time
prev_lid_close_finish_millis = current_Millis;
alarm_fired = false; // alarm is turned off when pill confirm button pushed
LED_pause_time = 1000;
LED_color = 1;
sleep_mode = true;
sleep_delay_time = 300000; // will sleep in 5 minutes
prev_sleep_delay_millis = current_Millis; //resets for above
} else if ((current_Millis - prev_take_meds_millis > take_med_repeat) && (lid_sw_val == 1) && (alarm_fired == true) && (pill_confirm_toggle == false)) { // alarmed fired but lid still closed
Serial.println("time to take medication but lid still closed");
LED_pause_time = 1000;
LED_color = 3; //yellow
sleep_mode = true;
sleep_delay_time = sleep_delay_time_no_door_open; // if door not opened but alarm true
warning_pill_not_taken = true;
prev_take_meds_millis = current_Millis;
} else if ((current_Millis - prev_take_meds_millis > take_med_repeat) && (lid_sw_val == 0) && (alarm_fired == true) && (pill_confirm_toggle == false)) { // alarmed fired and lid open prompts audio
Serial.println("time to take triggered and lid open");
LED_pause_time = 1000;
LED_color = 3; //yellow
take_med_repeat = 15000;
warning_pill_not_taken = true;
sleep_mode = true;
sleep_delay_time = sleep_delay_time_no_door_open;
prev_take_meds_millis = current_Millis;
audio.connecttoFS(SD, audiofilenames[3][8]); // time to take med
}
}
void Lid_close_finish() {
if (day_count == 13 && pill_confirm_toggle == true) { // if reached end of 14 days go to end_of_days
end_of_days();
}
if ((current_Millis - prev_lid_close_finish_millis > pill_conf_loop_time) && (pill_confirm_toggle == true) && (alarm_fired == false)) { // pill confirm button pushed but lid open
if (lid_sw_val == 0) { // lid switch is still open but pill confirm button pushed
Serial.println(" confirm button pushed lid is open");
if (lid_open_count > 1) {
pill_conf_loop_time = pill_conf_lid_open_repeat; // increases the time of repeat past the inital time thru the loop
}
lid_open_count++;
LED_pause_time = 1000;
LED_color = 1; //green
Serial.println("pill confimed but lid still open");
audio.connecttoFS(SD, audiofilenames[2][6]); // lid still open
if (lid_open_count > 10) {
warning_pill_not_taken = false;
sleep_delay_time = 1000;
}
}
if ((lid_sw_val == 1) && (pill_confirm_toggle == true)) { // pill confirm button pushed lid switch is closed
pill_conf_loop_time = pill_conf_lid_open_repeat; // increases the time of repeat past the inital time thru the loop
Serial.println("lid is close and finished");
warning_pill_not_taken = false;
LED_trigger = false; // turn off reoccuring LED flashing
analogWrite(green_LED, 255); // green led on constant will go off when sleep
DateTime alarmTime = rtc.getAlarm1(); // always same time every day
int alarm1_hr = alarmTime.hour(); // notDT is set in setup() at time of reset
int alarm1_min = alarmTime.minute(); // notDT is set in setup() at time of reset
Serial.print("Alarm time is set to: "); // display alarm set time
Serial.print(alarm1_hr);
Serial.print(":");
Serial.println(alarm1_min);
audio_trigger = 3; // loop thru audio_eof_mp3() 1 additional audio prompts
audio_array1 = 0;
audio_array2 = 1; // turntone
audio_array3 = 1; // hour am pm
audio_array4 = (alarm1_hr); // 24 hours from time when reset was pushed
audio_array5 = 3;
audio_array6 = 1; // next time to take // 24 hours from time when reset was pushed // turn page tone
audio.connecttoFS(SD, audiofilenames[4][day_count]); // next time to take meds is
day_count++;
pill_confirm_toggle = false;
sleep_mode = true;
sleep_delay_time = sleep_delay_time_pill_confirm_door_closed; // 15 seconds after closing lid will sleep
Serial.println("sleep will occur in 15 seconds");
prev_sleep_delay_millis = current_Millis;
not_yet_time_status = true;
}
if ((lid_sw_val == 0) && (pill_confirm_toggle == false) && (lid_open_count > 10)) { // lid is open and pill confirm never pushed after 10x warnings and sleeps
sleep_mode = true;
door_left_open = true;
warning_pill_not_taken = true;
}
prev_lid_close_finish_millis = current_Millis;
}
}
void Turn_page() {
if ((page_forw_but.isReleased()) && (page_count < total_page_count)) {
Serial.println("page forward");
book_pwr_off_delay_millis = current_Millis; // resets book power off delay
audio.connecttoFS(SD, audiofilenames[5][page_count]); // Play page no.
page_count++;
} else if ((page_back_but.isReleased()) && (page_count > 0)) {
Serial.println("page back");
book_pwr_off_delay_millis = current_Millis; // resets book power off delay
page_count--;
audio.connecttoFS(SD, audiofilenames[5][page_count]); // Play click
}
}
void Language_select() {
if (lang_select_but.isReleased()) {
lang_state = !lang_state; // toggle state whenever entering loop
if (lang_state == true) {
Serial.println("isiZul language selected ");
audio.connecttoFS(SD, audiofilenames[3][7]); // isiZulu
} else {
Serial.println("english language seelected ");
audio.connecttoFS(SD, audiofilenames[3][5]); // english
}
}
}
void power_status() {
if ((pwr_but.isReleased()) || (pwr_but_state == LOW)) { //when press or is woke up via power button GPIO
pwr_state = !pwr_state; // toggle state whenever entering loop
if (pwr_state == true) {
digitalWrite(power_pin, HIGH);
sleep_mode = false;
book_pwr_off_delay_millis = current_Millis;
Serial.println("power is ON ");
audio.connecttoFS(SD, audiofilenames[2][2]); // instruction
} else if (pwr_state == false) {
Serial.println("power is OFF ");
audio.connecttoFS(SD, audiofilenames[0][1]); // turn tone to signal off
sleep_mode = true;
sleep_delay_time = 3000; // 3 second delay before sleep
prev_sleep_delay_millis = current_Millis;
}
}
if ((current_Millis - book_pwr_off_delay_millis > book_pwr_off_time) && (!audio.isRunning())) { // automatically goes into sleep after preset time
sleep_mode = true;
pwr_state = false;
sleep_delay_time = 3000;
audio.connecttoFS(SD, audiofilenames[0][1]); // turn tone to signal off
}
}
void goto_sleep() {
if ((current_Millis - prev_sleep_delay_millis > sleep_delay_time) && (sleep_mode == true)) {
pill_confirm_toggle = false;
digitalWrite(red_LED, LOW);
analogWrite(green_LED, 0);
digitalWrite(blue_LED, LOW);
pwr_state = false;
digitalWrite(power_pin, LOW);
Serial.println("Going to sleep now");
delay(1000);
esp_light_sleep_start();
prev_sleep_delay_millis = current_Millis;
}
}
void Volume_control() {
if ((vol_up_but_state == 0) && (volume_level < 22)) { // vol up state taken from analogRead
Serial.println("Volume UP");
audio.setVolume(volume_level); // 0...21
audio.connecttoFS(SD, "tick.mp3"); // Play tick
volume_level++; // set volume level up
}
if (vol_dn_but.isPressed() && volume_level > 0) {
Serial.println("Volume DOWN");
audio.setVolume(volume_level); // 0...21
audio.connecttoFS(SD, "tick.mp3"); // Play tick
volume_level--; // set volume level down
}
}
void Flash_LED() {
if ((current_Millis - prev_LED_millis > LED_pause_time) && (LED_trigger == true)) {
LED_status = !LED_status; // toggle state
if (LED_status) {
if (LED_color == 3) {
digitalWrite(red_LED, HIGH); // Yellow
analogWrite(green_LED, 120);
digitalWrite(blue_LED, 0);
} else if (LED_color == 0) { //red
digitalWrite(red_LED, HIGH);
digitalWrite(green_LED, 0);
digitalWrite(blue_LED, LOW);
} else if (LED_color == 1) { // green
digitalWrite(red_LED, LOW);
analogWrite(green_LED, 255);
digitalWrite(blue_LED, LOW);
} else if (LED_color == 2) { //blue
digitalWrite(red_LED, LOW);
digitalWrite(green_LED, LOW);
digitalWrite(blue_LED, HIGH);
}
} else if (!LED_status) {
digitalWrite(red_LED, LOW); //all LEDs off
analogWrite(green_LED, 0);
digitalWrite(blue_LED, LOW);
}
prev_LED_millis = current_Millis;
}
}
void Alarm_monitor() {
if (rtc.alarmFired(1)) {
if (sleep_mode == true) {
digitalWrite(power_pin, HIGH);
Serial.println(rtc.alarmFired(1));
DateTime now = rtc.now(); // Get the current time
char buff[] = "Alarm triggered at hh:mm:ss DDD, DD MMM YYYY";
Serial.println(now.toString(buff));
Serial.println();
rtc.clearAlarm(1);
delay(100);
not_yet_time_status = false; // allows for door to be opened
LED_trigger = true;
pwr_state = true;
alarm_fired = true;
sleep_mode = false;
pill_confirm_toggle = false;
pill_conf_loop_time = 3000; //resets for next time to confirm audio
take_med_repeat = 1000;
}
}
}
void end_of_days() {
day_count = 0;
pill_confirm_toggle = false;
rtc.clearAlarm(1);
digitalWrite(blue_LED, HIGH);
audio.connecttoFS(SD, audiofilenames[4][13]); // end of days to take pills
sleep_mode = true;
sleep_delay_time = 15000; // go to sleep 15 seconds after audio prompt
prev_sleep_delay_millis = current_Millis;
}
void battery_monitor() {
if ((pwr_state == true) && (lid_sw_val == 1) && (!audio.isRunning())) { // make sure it is not in medication cycle
int rawValue = analogRead(batt_input); // Reference voltage on ESP32 is 1.1V voltage divider = upper/lower 1M resistors
float voltageLevel = (rawValue / 4095.0) * 2 * 1.1 * 3.6; // calculate voltage level refence voltage is 1.1 and factor is 3.6
float batteryFraction = voltageLevel / MAX_BATTERY_VOLTAGE;
Serial.println((String) "Raw:" + rawValue + " Voltage:" + voltageLevel), delay(1000); // for debug
if (rawValue < MIN_BATT_THRESHOLD) { // if battery level is below threshold
digitalWrite(power_pin, HIGH);
for (int i = 0; i < 3; i++) {
digitalWrite(0, HIGH); //flash red
delay(150);
digitalWrite(0, LOW);
delay(150);
Serial.println("below battery threshold");
}
if (warning_pill_not_taken == true && alarm_fired == true) { // flashed 5 times if alarmed fired and pill not take after going to sleep
for (int i = 0; i < 5; i++) {
digitalWrite(2, HIGH); //flash blue
delay(120);
digitalWrite(2, LOW);
delay(120);
Serial.println("below battery threshold");
}
}
} else {
digitalWrite(power_pin, LOW);
esp_light_sleep_start();
}
}
}
void Clock_monitor() {
if (current_Millis - prev_clock_millis > clock_time) {
char date[10] = "hh:mm:ss"; // print current time
rtc.now().toString(date);
Serial.print(date);
Serial.print("] SQW (if = 0 it is triggered) = "); // the value at SQW-Pin (because of pullup 1 means no alarm)
Serial.println(digitalRead(CLOCK_INTERRUPT_PIN));
DateTime alarm1 = rtc.getAlarm1(); // the stored alarm value + mode
Ds3231Alarm1Mode alarm1mode = rtc.getAlarm1Mode();
char alarm1Date[12] = "DD hh:mm:ss";
alarm1.toString(alarm1Date);
Serial.print(" [Alarm1: ");
Serial.print(alarm1Date);
Serial.print(", Mode: ");
switch (alarm1mode) {
case DS3231_A1_PerSecond: Serial.print("PerSecond"); break;
case DS3231_A1_Second: Serial.print("Second"); break;
case DS3231_A1_Minute: Serial.print("Minute"); break;
case DS3231_A1_Hour: Serial.print("Hour"); break;
case DS3231_A1_Date: Serial.print("Date"); break;
case DS3231_A1_Day: Serial.println("Day"); break;
}
prev_clock_millis = current_Millis;
print_wakeup_reason(); // for prompt of cause of wake up
print_GPIO_wake_up();
if (LED_trigger == true) {
Serial.println("LED Trigger is true");
}
}
}
void onAlarm() {
// Serial.println("Alarm occured!");
}
void print_wakeup_reason() { // for debug
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP: Serial.println("Wakeup caused by ULP program"); break;
default: Serial.printf("Wakeup was not caused by light sleep: %d\n", wakeup_reason); break;
}
}
void print_GPIO_wake_up() { // Method to print the GPIO that triggered the wakeup
uint64_t GPIO_reason = esp_sleep_get_ext1_wakeup_status();
Serial.print("GPIO that triggered the wake up: GPIO ");
Serial.println((log(GPIO_reason)) / log(2), 0);
}
/*
void RFID_info() {
if (current_Millis - prev_RFID_millis > RFID_time) {
uint8_t values[8] = { 0 };
if (tag.getDeviceUID(values)) {
Serial.print(F("RFID UID: "));
for (uint8_t i = 0; i < 8; i++) {
if (values[i] < 0x0a)
Serial.print(F("0"));
Serial.print(values[i], HEX);
Serial.print(F(" "));
}
}
Serial.println(" ");
prev_RFID_millis = current_Millis;
}
}
*/