#include <Arduino.h>
#include <EEPROM.h>
#include <SD.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//SD, Audio setup
TMRpcm tmrpcm;
static const int COUNT_SCHEDULE_IDX = 0;
static const int SCHEDULE_START_IDX = 1;
static const int SCHEDULE_HOUR_IDX = 0;
static const int SCHEDULE_MINUTE_IDX = 1;
static const int SCHEDULE_SELECT_CONTAINER_IDX = 2;
static const int SCHEDULE_SELECT_CONTAINER_BYTES_LENGHT = 2;
static const int CONTAINER_MAX = 7;
static const int SCHEDULE_MAX = 10;
static const int SCHEDULE_BYTES_LENGTH = 4;
static const uint8_t SCR_HOME = 0;
static const uint8_t SCR_MENU = 10;
static const uint8_t SCR_ADD_SCHEDULE = 20;
static const uint8_t SCR_VIEW_SCHEDULE = 30;
static const uint8_t SCR_DELETE_SCHEDULE = 40;
static const uint8_t SCR_SET_SYS_TIME = 50;
static const uint8_t SCR_REMINDER = 60;
static const uint8_t MENU_ADD_SCHEDULE = 0;
static const uint8_t MENU_VIEW_SCHEDULE = 1;
static const uint8_t MENU_DELETE_SCHEDULE = 2;
static const uint8_t MENU_SET_SYS_TIME = 3;
byte arrowUpIdx = 0;
byte arrowUp[8] = {
0b00100,
0b01110,
0b11111,
0b00100,
0b00100,
0b00100,
0b00100,
0b00100
};
byte arrowDownIdx = 1;
byte arrowDown[8] = {
0b00100,
0b00100,
0b00100,
0b00100,
0b00100,
0b11111,
0b01110,
0b00100
};
//Light
class Light{
const byte lightPin;
public:
Light(byte attachTo): lightPin(attachTo){}
void begin(){
pinMode(lightPin, OUTPUT);
}
void setONOFF(bool ON){
digitalWrite(lightPin, ON);
}
void on(){
digitalWrite(lightPin, HIGH);
}
void off(){
digitalWrite(lightPin, LOW);
}
void toggle(){
bool current = digitalRead(lightPin);
digitalWrite(lightPin, !current);
}
};
class Button
{
const byte buttonPin; // the GPIO / pin for the button
static constexpr byte debounceDelay = 30; // the debounce time; increase if the output flickers. Static because we only need one value for all buttons
const bool active; // is the pin active HIGH or active LOW (will also activate the pullups!)
bool lastButtonState = HIGH; // the previous reading from the input pin
byte lastDebounceTime = 0; // the last time the output pin was toggled - we check only ONE byte, so I didn't mess around with unsigned long
public:
/**
\brief constructor for a button
The constructor takes the GPIO as parameter.
If you omit the second parameter, the library will activate the internal pullup resistor
and the button should connect to GND.
If you set the second parameter to HIGH, the button is active HIGH.
The button should connect to VCC.
The internal pullups will not be used but you will need an external pulldown resistor.
\param attachTo the GPIO for the button
\param active LOW (default) - if button connects to GND, HIGH if button connects to VCC
*/
Button(byte attachTo, bool active = LOW) : buttonPin(attachTo), active(active) {}
/**
\brief set the pin to the proper state
Call this function in your setup().
The pinMode will be set according to your constructor.
*/
void begin() {
if (active == LOW)
pinMode(buttonPin, INPUT_PULLUP);
else
pinMode(buttonPin, INPUT);
}
/**
\brief indicate if button was pressed since last call
@return HIGH if button was pressed since last call - debounce
*/
bool wasPressed() {
bool buttonState = LOW; // the current reading from the input pin
byte reading = LOW; // "translated" state of button LOW = released, HIGH = pressed, despite the electrical state of the input pint
if (digitalRead(buttonPin) == active) reading = HIGH; // if we are using INPUT_PULLUP we are checking invers to LOW Pin
if (((millis() & 0xFF ) - lastDebounceTime) > debounceDelay) // If the switch changed, AFTER any pressing or noise
{
if (reading != lastButtonState && lastButtonState == LOW) // If there was a change and and last state was LOW (= released)
{
buttonState = HIGH;
}
lastDebounceTime = millis() & 0xFF;
lastButtonState = reading;
}
return buttonState;
}
};
class Schedule{
public:
Schedule(){}
Schedule(uint8_t _hour, uint8_t _minute){
hour = _hour;
minute = _minute;
}
uint8_t hour = 0;
uint8_t minute = 0;
bool sltCons[CONTAINER_MAX];
void reset(){
hour = 0;
minute = 0;
for (int i = 0; i < CONTAINER_MAX; i++){
sltCons[i] = 0;
}
}
static int count(){
return EEPROM.read(COUNT_SCHEDULE_IDX);
}
static Schedule* getAll(){
static Schedule schedules[SCHEDULE_MAX];
int cnt = EEPROM.read(COUNT_SCHEDULE_IDX);
for (int i = 0; i < cnt; i++){
Schedule schedule;
schedule.hour = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_HOUR_IDX);
schedule.minute = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_MINUTE_IDX);
uint8_t sltConByte1 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX);
uint8_t sltConByte2 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX + 1);
uint16_t sltConBytes = (static_cast<uint16_t>(sltConByte1) << 8) | sltConByte2;
for (int j = CONTAINER_MAX - 1; j >= 0; --j) {
// Extract the bit at position i
bool bit = (sltConBytes >> j) & 1;
schedule.sltCons[j] = bit;
}
schedules[i] = schedule;
}
Serial.println(schedules[0].hour);
return schedules;
}
static bool save(Schedule newSchedule){
int cnt = EEPROM.read(COUNT_SCHEDULE_IDX); // Read the number of schedules
Schedule schedules[SCHEDULE_MAX]; // Temporary array to hold schedules
for (int i = 0; i < cnt; i++) {
Schedule schedule;
schedule.hour = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_HOUR_IDX);
schedule.minute = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_MINUTE_IDX);
uint8_t sltConByte1 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX);
uint8_t sltConByte2 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX + 1);
uint16_t sltConBytes = (static_cast<uint16_t>(sltConByte1) << 8) | sltConByte2;
for (int j = CONTAINER_MAX - 1; j >= 0; --j) {
// Extract the bit at position i
bool bit = (sltConBytes >> j) & 1;
schedule.sltCons[j] = bit;
}
schedules[i] = schedule;
}
// Check if the new schedule already exists and update it
bool exists = false;
for (int i = 0; i < cnt; i++) {
if (schedules[i].hour == newSchedule.hour && schedules[i].minute == newSchedule.minute) {
exists = true;
for (int j = 0; j < CONTAINER_MAX; j++) {
schedules[i].sltCons[j] = newSchedule.sltCons[j];
}
break;
}
}
// If it does not exist, append it
if (!exists) {
if (cnt >= SCHEDULE_MAX) {
return false;
}
schedules[cnt++] = newSchedule;
}
// Sort the schedules by hour and minute
for (int i = 0; i < cnt - 1; i++) {
for (int j = 0; j < cnt - i - 1; j++) {
if (schedules[j].hour > schedules[j + 1].hour ||
(schedules[j].hour == schedules[j + 1].hour && schedules[j].minute > schedules[j + 1].minute)) {
// Swap schedules
Schedule temp = schedules[j];
schedules[j] = schedules[j + 1];
schedules[j + 1] = temp;
}
}
}
// Write the updated schedules back to EEPROM
EEPROM.write(COUNT_SCHEDULE_IDX, cnt); // Save the count of schedules
for (int i = 0; i < cnt; i++) {
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_HOUR_IDX, schedules[i].hour);
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_MINUTE_IDX, schedules[i].minute);
uint16_t sltConBytes = 0;
for (int j = 0; j < CONTAINER_MAX; j++) {
if (schedules[i].sltCons[j]) {
sltConBytes |= (1 << j);
}
}
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX, (sltConBytes >> 8) & 0xFF); // MSB
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX + 1, sltConBytes & 0xFF); // LSB
}
return true;
}
static void del(int index){
int cnt = EEPROM.read(COUNT_SCHEDULE_IDX); // Read the number of schedules
Schedule schedules[SCHEDULE_MAX]; // Temporary array to hold schedules
for (int i = 0; i < cnt; i++) {
if(i == index)
continue;
Schedule schedule;
schedule.hour = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_HOUR_IDX);
schedule.minute = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_MINUTE_IDX);
uint8_t sltConByte1 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX);
uint8_t sltConByte2 = EEPROM.read(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX + 1);
uint16_t sltConBytes = (static_cast<uint16_t>(sltConByte1) << 8) | sltConByte2;
for (int j = CONTAINER_MAX - 1; j >= 0; --j) {
// Extract the bit at position i
bool bit = (sltConBytes >> j) & 1;
schedule.sltCons[j] = bit;
}
if(i < index){
schedules[i] = schedule;
}else if(i > index){
schedules[i - 1] = schedule;
}
}
EEPROM.write(COUNT_SCHEDULE_IDX, cnt - 1); // Save the count of schedules
for (int i = 0; i < cnt; i++) {
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_HOUR_IDX, schedules[i].hour);
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_MINUTE_IDX, schedules[i].minute);
uint16_t sltConBytes = 0;
for (int j = 0; j < CONTAINER_MAX; j++) {
if (schedules[i].sltCons[j]) {
sltConBytes |= (1 << j);
}
}
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX, (sltConBytes >> 8) & 0xFF); // MSB
EEPROM.write(SCHEDULE_START_IDX + (i * SCHEDULE_BYTES_LENGTH) + SCHEDULE_SELECT_CONTAINER_IDX + 1, sltConBytes & 0xFF); // LSB
}
}
};
class GlobalState{
public:
static GlobalState state;
uint8_t screenMode = SCR_HOME;
uint8_t hourOffSet = 23;
uint8_t minuteOffSet = 59;
unsigned long setSystemTimeAt = millis()/1000;
//Menu
uint8_t sltMenu = MENU_ADD_SCHEDULE;
//Add schedule data
Schedule schedule = Schedule();
bool isEditHourOrMinute = 0; //0: hour, 1: minute
bool blinkStatus = 1;
unsigned long lastBlinkChane = millis();
void checkBlink(){
float secondDiff = (float)(millis() - lastBlinkChane)/1000;
if(secondDiff > 0.5){
blinkStatus = !blinkStatus;
lastBlinkChane = millis();
}
}
void addScheduleReload(){
schedule.reset();
isEditHourOrMinute = 0; //0: hour, 1: minute
blinkStatus = 1;
lastBlinkChane = millis();
}
//View/delete schedule
int selectScheduleIdx = 0;
int cntSchedule = Schedule::count();
Schedule* schedules;
void viewScheduleReload(){
selectScheduleIdx = 0;
cntSchedule = Schedule::count();
schedules = Schedule::getAll();
}
//Set system time
uint8_t sysHour = 0;
uint8_t sysMinute = 0;
void setSystemTimeReload(){
sysHour = 0;
sysMinute = 0;
isEditHourOrMinute = 0; //0: hour, 1: minute
blinkStatus = 1;
lastBlinkChane = millis();
}
//Reminder job
unsigned long lastReminderJobAt = millis()/1000;
unsigned long startReminderAt = 0;
Schedule currentSchedule = Schedule(-1,-1);
};
GlobalState GlobalState::state;
class Time
{
public:
int hour = 0;
int minute = 0;
int second = 0;
Time(){}
Time(int _hour, int _minute, int _second = 0) : hour(_hour), minute(_minute), second(_second){}
static Time now(){
unsigned long seconds = getTime() - GlobalState::state.setSystemTimeAt;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
// Remaining time components
seconds %= 60;
minutes %= 60;
hours %= 24;
int currentHour = (GlobalState::state.hourOffSet + hours) % 24;
int currentMinute = (GlobalState::state.minuteOffSet + minutes);
int currentSecond = seconds;
if (currentMinute >= 60) {
currentMinute -= 60;
currentHour = (currentHour + 1) % 24; // Increment hour if minutes overflow
}
return Time(currentHour, currentMinute, currentSecond);
}
static unsigned long getTime(){
return millis()/(1000);
}
};
//Control buttons
Button ctrl_options{A0};
Button ctrl_cancel{A1};
Button ctrl_ok{A2};
Button ctrl_up{A3};
Button ctrl_down{A4};
//Container buttons
Button conBtns[7] =
{
Button{22},
Button{24},
Button{26},
Button{28},
Button{30},
Button{32},
Button{34}
};
//Container lights
Light conLeds[7] = {
Light{23},
Light{25},
Light{27},
Light{29},
Light{31},
Light{33},
Light{35}
};
//LCD screen setup
// const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
// LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
LiquidCrystal_I2C lcd(0x27, 16, 2);
String scrLine1, scrLine2;
void printLn(String text, int lineNumber = 0){
if(lineNumber == 0 && text == scrLine1)
return;
if(lineNumber == 1 && text == scrLine2)
return;
lcd.setCursor(0, lineNumber);
lcd.print(" ");
lcd.setCursor(0, lineNumber);
lcd.print(text);
if(lineNumber == 0)
scrLine1 = text;
if(lineNumber == 1)
scrLine2 = text;
};
void print(String line1, String line2 = ""){
if(line1 != scrLine1)
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(line1);
scrLine1 = line1;
}
if(line2 != scrLine2){
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(line2);
scrLine2 = line2;
}
};
//common function
String padding(int number, byte width ) {
String result = "";
for(int i = width - 1; i >= 0; i--){
if(number / pow(10 ,i) < 1){
result += "0";
}else{
result += number;
break;
}
}
return result;
}
void printScroll(){
lcd.setCursor(15,0);
lcd.write(arrowUpIdx);
lcd.setCursor(15,1);
lcd.write(arrowDownIdx);
}
void ToHome(){
Time now = Time::now();
print("MedicineBox v1.0", padding(now.hour, 2) + ":" + padding(now.minute, 2) + ":" + padding(now.second, 2));
//listen pressed button
if (ctrl_options.wasPressed()) {
GlobalState::state.sltMenu = MENU_ADD_SCHEDULE;
GlobalState::state.screenMode = SCR_MENU;
}
}
void ToMenu(){
printLn("Select options:", 0);
if(GlobalState::state.sltMenu == MENU_ADD_SCHEDULE)
printLn("Add a schedule", 1);
if(GlobalState::state.sltMenu == MENU_VIEW_SCHEDULE)
printLn("View Schedules", 1);
if(GlobalState::state.sltMenu == MENU_DELETE_SCHEDULE)
printLn("Delete Schedule", 1);
if(GlobalState::state.sltMenu == MENU_SET_SYS_TIME)
printLn("Set system time", 1);
printScroll();
//listen pressed button
if (ctrl_cancel.wasPressed()) {
GlobalState::state.screenMode = SCR_HOME;
}
if(ctrl_down.wasPressed()){
GlobalState::state.sltMenu = (GlobalState::state.sltMenu + 1) % 4;
}
if(ctrl_up.wasPressed()){
GlobalState::state.sltMenu = ((GlobalState::state.sltMenu != 0 ? GlobalState::state.sltMenu : 4) - 1) % 4;
}
if(ctrl_ok.wasPressed()){
if(GlobalState::state.sltMenu == MENU_ADD_SCHEDULE){
GlobalState::state.screenMode = SCR_ADD_SCHEDULE;
GlobalState::state.addScheduleReload();
}
if(GlobalState::state.sltMenu == MENU_VIEW_SCHEDULE){
GlobalState::state.selectScheduleIdx = 0;
GlobalState::state.screenMode = SCR_VIEW_SCHEDULE;
}
if(GlobalState::state.sltMenu == MENU_DELETE_SCHEDULE){
GlobalState::state.selectScheduleIdx = 0;
GlobalState::state.screenMode = SCR_DELETE_SCHEDULE;
}
if(GlobalState::state.sltMenu == MENU_SET_SYS_TIME)
GlobalState::state.screenMode = SCR_SET_SYS_TIME;
}
}
void ToAddSchedule(){
printLn("Add a schedule...", 0);
GlobalState::state.checkBlink();
String disHour = GlobalState::state.isEditHourOrMinute == 0 & GlobalState::state.blinkStatus == 0 ? " " : padding(GlobalState::state.schedule.hour, 2);
String disMinute = GlobalState::state.isEditHourOrMinute == 1 & GlobalState::state.blinkStatus == 0 ? " ": padding(GlobalState::state.schedule.minute, 2);
printLn(disHour + ":" + disMinute, 1);
//set time
if (ctrl_options.wasPressed()) {
GlobalState::state.isEditHourOrMinute = !GlobalState::state.isEditHourOrMinute;
}
if (ctrl_up.wasPressed()) {
if(!GlobalState::state.isEditHourOrMinute){ //hour
GlobalState::state.schedule.hour = (GlobalState::state.schedule.hour + 1) % 24;
}else{ //minute
GlobalState::state.schedule.minute = (GlobalState::state.schedule.minute + 1) % 60;
}
}
if (ctrl_down.wasPressed()) {
if(!GlobalState::state.isEditHourOrMinute){ //hour
GlobalState::state.schedule.hour = GlobalState::state.schedule.hour > 0 ? GlobalState::state.schedule.hour - 1 : 23;
}else{ //minute
GlobalState::state.schedule.minute = GlobalState::state.schedule.minute > 0 ? GlobalState::state.schedule.minute - 1 : 59;
}
}
//select containers
for(int i = 0; i < CONTAINER_MAX; i++){
if(conBtns[i].wasPressed()){
GlobalState::state.schedule.sltCons[i] = !GlobalState::state.schedule.sltCons[i];
conLeds[i].setONOFF(GlobalState::state.schedule.sltCons[i]);
}
}
if (ctrl_ok.wasPressed()) {
print("Saving...");
if(Schedule::save(GlobalState::state.schedule)){
print("Save successfull");
GlobalState::state.viewScheduleReload();
GlobalState::state.addScheduleReload();
delay(1000);
}else{
print("Only allow","max 10 schedules");
GlobalState::state.viewScheduleReload();
GlobalState::state.addScheduleReload();
delay(3000);
}
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_HOME;
}
if (ctrl_cancel.wasPressed()) {
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_HOME;
}
}
void ToViewSchedule(){
printLn((String)GlobalState::state.cntSchedule + " schedules");
if(GlobalState::state.cntSchedule > 0){
Schedule sltSechdule = GlobalState::state.schedules[GlobalState::state.selectScheduleIdx];
printLn(padding(sltSechdule.hour, 2) + ":" + padding(sltSechdule.minute, 2), 1);
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].setONOFF(sltSechdule.sltCons[i]);
}
if (ctrl_down.wasPressed()) {
GlobalState::state.selectScheduleIdx = (GlobalState::state.selectScheduleIdx + 1) % GlobalState::state.cntSchedule;
}
if (ctrl_up.wasPressed()) {
GlobalState::state.selectScheduleIdx = GlobalState::state.selectScheduleIdx == 0 ? GlobalState::state.cntSchedule - 1: GlobalState::state.selectScheduleIdx - 1;
}
if(GlobalState::state.cntSchedule > 1){
printScroll();
}
}else
printLn("", 1);
if (ctrl_cancel.wasPressed()) {
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_HOME;
}
}
void ToDeleteSchedule(){
if(GlobalState::state.cntSchedule > 0){
printLn("Del schedule?");
Schedule sltSechdule = GlobalState::state.schedules[GlobalState::state.selectScheduleIdx];
printLn(padding(sltSechdule.hour, 2) + ":" + padding(sltSechdule.minute, 2), 1);
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].setONOFF(sltSechdule.sltCons[i]);
}
if (ctrl_down.wasPressed()) {
GlobalState::state.selectScheduleIdx = (GlobalState::state.selectScheduleIdx + 1) % GlobalState::state.cntSchedule;
}
if (ctrl_up.wasPressed()) {
GlobalState::state.selectScheduleIdx = GlobalState::state.selectScheduleIdx == 0 ? GlobalState::state.cntSchedule - 1: GlobalState::state.selectScheduleIdx - 1;
}
if(GlobalState::state.cntSchedule > 1){
printScroll();
}
}else
print("Schedule empty");
if (ctrl_ok.wasPressed()) {
if(GlobalState::state.cntSchedule > 0){
GlobalState::state.screenMode = SCR_MENU;
return;
}
print("Deleteing...");
Schedule::del(GlobalState::state.selectScheduleIdx);
GlobalState::state.viewScheduleReload();
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_MENU;
}
if (ctrl_cancel.wasPressed()) {
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_HOME;
}
}
void ToSetSysTime(){
printLn("Set system time", 0);
GlobalState::state.checkBlink();
String disHour = GlobalState::state.isEditHourOrMinute == 0 & GlobalState::state.blinkStatus == 0 ? " " : padding(GlobalState::state.sysHour, 2);
String disMinute = GlobalState::state.isEditHourOrMinute == 1 & GlobalState::state.blinkStatus == 0 ? " ": padding(GlobalState::state.sysMinute, 2);
printLn(disHour + ":" + disMinute, 1);
//set time
if (ctrl_options.wasPressed()) {
GlobalState::state.isEditHourOrMinute = !GlobalState::state.isEditHourOrMinute;
}
if (ctrl_up.wasPressed()) {
if(!GlobalState::state.isEditHourOrMinute){ //hour
GlobalState::state.sysHour = (GlobalState::state.sysHour + 1) % 24;
}else{ //minute
GlobalState::state.sysMinute = (GlobalState::state.sysMinute + 1) % 60;
}
}
if (ctrl_down.wasPressed()) {
if(!GlobalState::state.isEditHourOrMinute){ //hour
GlobalState::state.sysHour = GlobalState::state.sysHour > 0 ? GlobalState::state.sysHour - 1 : 23;
}else{ //minute
GlobalState::state.sysMinute = GlobalState::state.sysMinute > 0 ? GlobalState::state.sysMinute - 1 : 59;
}
}
if (ctrl_ok.wasPressed()) {
print("Saving...");
GlobalState::state.hourOffSet = GlobalState::state.sysHour;
GlobalState::state.minuteOffSet = GlobalState::state.sysMinute;
GlobalState::state.setSystemTimeAt = Time::getTime();
print("Save successfull");
GlobalState::state.setSystemTimeReload();
delay(1000);
GlobalState::state.screenMode = SCR_HOME;
}
if (ctrl_cancel.wasPressed()) {
GlobalState::state.screenMode = SCR_HOME;
}
}
void ExitReminder(){
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].off();
}
GlobalState::state.screenMode = SCR_HOME;
tmrpcm.stopPlayback();
}
void ToReminder(){
print("Time to take", "medicine!! " + padding(GlobalState::state.currentSchedule.hour, 2) + ":" + padding(GlobalState::state.currentSchedule.minute, 2));
for(int i = 0; i < CONTAINER_MAX; i++){
conLeds[i].setONOFF(GlobalState::state.currentSchedule.sltCons[i]);
}
if (ctrl_ok.wasPressed()) {
ExitReminder();
}
}
void ReminderJob(){
//Exit reminder after 60s
if(GlobalState::state.startReminderAt > 0 &&
Time::getTime() - GlobalState::state.startReminderAt >= 60){
GlobalState::state.startReminderAt = 0;
ExitReminder();
}
//run reminder job every 10 seconds
if(Time::getTime() - GlobalState::state.lastReminderJobAt < 10){
// printLn("a"+(String)GlobalState::state.lastReminderJobAt, 0);
return;
}
//Already in reminder
if(GlobalState::state.startReminderAt > 0 &&
Time::getTime() - GlobalState::state.startReminderAt < 60){
// printLn("b" + (String)GlobalState::state.startReminderAt, 1);
return;
}
GlobalState::state.lastReminderJobAt = Time::getTime();
Time now = Time::now();
for(int i = 0; i < GlobalState::state.cntSchedule; i++){
if(GlobalState::state.schedules[i].hour == now.hour &&
GlobalState::state.schedules[i].minute == now.minute){
GlobalState::state.currentSchedule = GlobalState::state.schedules[i];
GlobalState::state.startReminderAt = Time::getTime();
GlobalState::state.screenMode = SCR_REMINDER;
tmrpcm.play("YOURPH~1.WAV");
break;
}
}
}
void CheckScreenMode(){
switch (GlobalState::state.screenMode)
{
case SCR_HOME:
ToHome();
break;
case SCR_MENU:
ToMenu();
break;
case SCR_ADD_SCHEDULE:
ToAddSchedule();
break;
case SCR_VIEW_SCHEDULE:
ToViewSchedule();
break;
case SCR_DELETE_SCHEDULE:
ToDeleteSchedule();
break;
case SCR_SET_SYS_TIME:
ToSetSysTime();
break;
case SCR_REMINDER:
ToReminder();
break;
default:
break;
}
}
void printFiles(File dir, int numTabs)
{
while (true)
{
File entry = dir.openNextFile();
if (! entry)
{
break;
}
for (uint8_t i = 0; i < numTabs; i++)
{
Serial.print('\t');
}
Serial.println(entry.name());
entry.close();
}
}
void setup() {
Serial.begin(9600);
//SD, audio setup
tmrpcm.speakerPin = 5;
pinMode(5,OUTPUT);
if (!SD.begin(53)) {
Serial.println("SD fail");
// return;
}else{
Serial.println("SD success");
File root = SD.open("/");
printFiles(root,0);
}
tmrpcm.setVolume(5); //max volumn
if(SD.exists("YOURPH~1.WAV")){
Serial.println("exist file");
// tmrpcm.play("YOURPH~1.WAV");
}
// EEPROM.begin();
if(Schedule::count() > SCHEDULE_MAX)
EEPROM.update(COUNT_SCHEDULE_IDX, 0);
//setup screen
lcd.begin();
lcd.createChar(arrowUpIdx, arrowUp);
lcd.createChar(arrowDownIdx, arrowDown);
//setup button
ctrl_options.begin();
ctrl_cancel.begin();
ctrl_ok.begin();
ctrl_up.begin();
ctrl_down.begin();
//setup container btn and led
for(int i = 0; i < CONTAINER_MAX; i++){
conBtns[i].begin();
conLeds[i].begin();
}
//first screen
GlobalState::state.screenMode = SCR_HOME;
}
void loop() {
ReminderJob();
CheckScreenMode();
}
Cancel
Ok
Options
Up
Down