#include <Arduino.h>
#include <Wire.h>
#include<EEPROM.h>
#include "LiquidCrystal_PCF8574.h" // Бібліотека для роботи з LCD по I2C.
#include <RTClib.h> // Бібліотека для роботи з моделем реального часу
#include <Ds1302.h> //непотрібна
DS1302 rtc (4, 2, 3); //RST,CLK,DAT
LiquidCrystal_PCF8574 lcd(0x27); // I2c адреса дисплея, колонки, рядки.
#define SCREEN_1 0
#define SCREEN_2 1
#define SCREEN_3 2
#define IIC_SCL 11 //длґ матриці
#define IIC_SDA 12
unsigned char ARROW[] = {0x00, 0x10, 0x38, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00};
unsigned char DRUGS[] = {0x7C, 0x30, 0x0C, 0x7C, 0x00, 0x44, 0x28, 0x10, 0x7C, 0x00, 0x5C, 0x00, 0x3C, 0x40, 0x40, 0x3C};
int screens = 0; // лічильник екранів
bool isScreenChanged = true;
long previous_Millis = 0;
unsigned long PassedTime = 0;
long previous_Millis_LCD = 0; // for LCD screen update
long interval_LCD = 3000; // интервал смены экранов на ЖК дисплее
long interval_buzzer = 500; // частота піканія
const long Alarm_delay = 30000; // 10 хвилин = 600000 мілісек затримка будильника (0,5 хв = 30000)
unsigned long current_Millis_LCD;
unsigned long currentMillis;
unsigned long StartTime = 0 ;
int nowHr, nowMin, nowSec; // to show current mm,hh,ss
int ledState = LOW;
// установка времен срабатывания напоминателя
int buzz9amHH = 20; // HH - hours ##Set these for reminder time in 24hr Format
int buzz9amMM = 15; // MM - Minute
int buzz9amSS = 00; // SS - Seconds
int buzz2pmHH = 20; // HH - hours
int buzz2pmMM = 16; // MM - Minute
int buzz2pmSS = 00; // SS - Seconds
int buzz9pmHH = 20; // HH - hours
int buzz9pmMM = 17; // MM - Minute
int buzz9pmSS = 00; // SS - Seconds
int stop9am = 0; //прапор спрацьовування будильника
int stop2pm = 0;
int stop9pm = 0;
int val; //змінна для останнього значення, що було збережено
int addr = 1; //номер натиснутої кнопки зберігається по адресу 1!
int push1_state, push2_state, push3_state, stopinState = 1;
int DelBtnState = 1;
int pushVal = 0;
const int push1_pin = 10;
const int push2_pin = 9;
const int push3_pin = 8;
const int stopPin = 7;
const int delayPin = 6;
const int ledPin = 5; // buzzer and led pin
boolean button1WasUp = true; //прапор натискання на кнопок 1/2/3
boolean pushpressed = false; //прапор натискання кнопки стоп
boolean DelBtn_WasUp = true; //прапор натискання кнопки відкласти
boolean ALARM_delayed = false; //прапор відкладеного будильника
// функция для налаштування
void setup() {
Serial.begin(115200);
Serial.println("Let's start!");
rtc.begin(); // запускаємо модуль реального часу
if (!rtc.isrunning()) // перевіряємо, чи з'єднано модуль реального часу
{
Serial.println("Couldn't find RTC");
}
rtc.adjust(DateTime(__DATE__, __TIME__));
lcd.begin(16, 2); // Ініціалізуємо дисплей.
lcd.setBacklight(255); // Вмикаємо підсвітку.
Serial.println("Savchenko bachelor project"); // Виводимо текст.
pinMode(push1_pin, INPUT_PULLUP); // встановлюємо режим роботи контакта 10, до якого пдключена кнопка 1
pinMode(push2_pin, INPUT_PULLUP); // встановлюємо режим роботи контакта 9, до якого пдключена кнопка 2
pinMode(push3_pin, INPUT_PULLUP); // встановлюємо режим роботи контакта 8, до якого пдключена кнопка 3
pinMode(stopPin, INPUT_PULLUP); // встановлюємо режим роботи контакта 7, до якого пдключена кнопка 4
pinMode(delayPin, INPUT_PULLUP); // увстановлюємо режим роботи контакта 6, до якого пдключена кнопка 5
pinMode(ledPin, OUTPUT); // встановлюємо режим роботи контакта 5, до якого пдключено світлодіод та пищалка
pinMode(IIC_SCL, OUTPUT); // встановлюємо режим роботи контакта 11, до якого пдключено матрицю
pinMode(IIC_SDA, OUTPUT); // встановлюємо режим роботи контакта 12, до якого пдключено матрицю
delay(200);
Serial.println(EEPROM.read(addr));
val = EEPROM.read(addr); // считываем из EEPROM предыдущее установленное состояние системы
switch (val) {
case 1:
Serial.println("Set for 1 time per day");
push1_state = 0;
push2_state = 1;
push3_state = 1;
pushVal = 1;
break;
case 2:
Serial.println("Set for 2 times per day");
push1_state = 1;
push2_state = 0;
push3_state = 1;
pushVal = 2;
break;
case 3:
Serial.println("Set for 3 times per day");
push1_state = 1;
push2_state = 1;
push3_state = 0;
pushVal = 3;
break;
}
}
//головна функція
void loop()
{
push1();
push2();
push3();
if (pushVal == 1) { // якщо кнопка 1 натиснута, то нагадуємо тільки об 9am
at9am(); // функція запуску нагадування об 9am
}
else if (pushVal == 2) { // якщо кнопка 2 натиснута, то нагадуємо тільки об 9am та 9pm
at9am();
at9pm(); // функція запуску нагадування об 9pm
}
else if (pushVal == 3) { // якщо кнопка 1 натиснута, то нагадуємо тільки об 9am,2pm та 9pm
at9am();
at2pm();
at9pm();
}
current_Millis_LCD = millis(); //скільки часу пройшло з моменту старта програми
currentMillis = millis();
push1_state = digitalRead(push1_pin); // визначаємо поточний стан кнопки 1
push2_state = digitalRead(push2_pin);
push3_state = digitalRead(push3_pin);
stopinState = digitalRead(stopPin);
DelBtnState = digitalRead(delayPin);
delayBuzzing(); // відкласти будильник
stopBuzzing(); // зупинити будильник
changeScreen();
for (int i = 0; i < 2; i++)
{
switch (i)
{
case 0:
matrix_display(ARROW); // показуємо стрілку
break;
case 1:
matrix_display(DRUGS); // показуємо стрілку
break;
default: break;
}
delay (2000);
}
}
// функція для нагадування 1 раз на день
void push1() {
if (button1WasUp && !push1_state)
{
delay(10); //задержка необходима для исключения влияния дребезга контактов и ложного срабатывания кнопки
if (!push1_state) //обернена логіка, тобто якщо натиснули на кнопку, то отримуємо логічний нуль.
{
push1_state = 1;
push2_state = 1;
push3_state = 1;
EEPROM.write(addr, 1);
Serial.print("Push1 is pressed : "); Serial.println(EEPROM.read(addr)); // для відладки
pushVal = 1; //save the state of push button-1
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reminder set ");
lcd.setCursor(0, 1);
lcd.print("for ONCE a day");
delay(1200);
lcd.clear();
}
button1WasUp = push1_state;
}
}
// функція для нагадування 2 рази на день
void push2() {
if (button1WasUp && !push2_state)
{
delay(10); //затримка необходима для виключення впливу брязкіту контакту и помилкового спрацьовування кнопки
if (!push2_state) //обернена логіка, тобто якщо натиснули на кнопку, то отримуємо логічний нуль.
{
push1_state = 1;
push2_state = 1;
push3_state = 1;
EEPROM.write(addr, 2);
Serial.print("Push2 is pressed : "); Serial.println(EEPROM.read(addr));
pushVal = 2; //save the state of push button-1
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reminder set ");
lcd.setCursor(0, 1);
lcd.print("for TWICE a day");
delay(2000);
lcd.clear();
}
button1WasUp = push2_state;
}
}
// функція для нагадування 3 рази на день
void push3() {
if (button1WasUp && !push3_state)
{
delay(10);
if (!push3_state) //обернена логіка, тобто якщо натиснули на кнопку, то отримуємо логічний нуль.
{
push1_state = 1;
push2_state = 1;
push3_state = 1;
EEPROM.write(addr, 3);
Serial.print("Push3 is pressed : "); Serial.println(EEPROM.read(addr));
pushVal = 3;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reminder set ");
lcd.setCursor(0, 1);
lcd.print("for THRICE a day");
delay(1200);
lcd.clear();
}
button1WasUp = push3_state;
}
}
// функція для початку звучання зумера, коли настане час нагадування
void startBuzz() {
if (currentMillis - previous_Millis >= interval_buzzer) {
previous_Millis = currentMillis; // зберігаємо останнім часом, коли був увімкнений світлодіод
Serial.println("Start bipping");
if (ledState == LOW) { // якщо світлодіод вимкнено, то включити його – і навпаки
ledState = HIGH;
}
else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
//функція для зупинки звучання зумеру, коли користувач натисне кнопку зупинки
void stopBuzzing() {
if (!stopinState && !pushpressed) //кнопка СТОП натиснута
{
delay(10);
Serial.println("Кнопка СТОП натиснена!");
pushpressed = true;
stopinState = 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Take your ");
lcd.setCursor(0, 1);
lcd.print("medicine !!!");
delay(2000);
lcd.clear();
}
if (stopinState && pushpressed) //кнопка СТОП відпущена
{
pushpressed = false;
Serial.println("Кнопка СТОП відпущена!");
DateTime now = rtc.now();
if (nowHr == buzz9amHH && nowMin == buzz9amMM || ALARM_delayed) { //якщо поточний час співпав з часом будильника,
stop9am = 1; // то встановлюємо прапор спрацьовання будильника
ALARM_delayed = false;
ledState = LOW; //вимикаємо світлодіод та піщалку
digitalWrite(ledPin, ledState);
}
if (nowHr == buzz2pmHH && nowMin == buzz2pmMM || ALARM_delayed) {
stop2pm = 1;
ALARM_delayed = false;
ledState = LOW;
digitalWrite(ledPin, ledState);
}
if (nowHr == buzz9pmHH && nowMin == buzz9pmMM || ALARM_delayed) {
stop9pm = 1;
ALARM_delayed = false;
ledState = LOW;
digitalWrite(ledPin, ledState);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void delayBuzzing() {
if (!DelBtnState && DelBtn_WasUp) //кнопка ВІДКЛАСТИ натиснута
{
delay(10);
DelBtnState = digitalRead(delayPin);
if (!DelBtnState) // якщо кнопка ВІДКЛАСТИ все ще натиснута
{ Serial.println("Кнопка ВІДКЛАСТИ натиснена!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Alarm snoose ");
lcd.setCursor(0, 1);
lcd.print("for 30 minutes!!!");
delay(2000);
lcd.clear();
StartTime = millis();
Serial.print("StartTime"); Serial.println(StartTime);
}
DelBtn_WasUp = DelBtnState;
ALARM_delayed = false;
}
if (DelBtnState && !DelBtn_WasUp) //кнопка ВІДКЛАСТИ відпущена
{
delay(10);
DelBtnState = digitalRead(delayPin);
Serial.println("Кнопка ВІДКЛАСТИ відпущена!");
ledState = LOW; // вимикаємо світлодіод
digitalWrite(ledPin, ledState);
while (DelBtnState && !ALARM_delayed) //якщо кнопка ВІДКЛАСТИ все ще відпущена
{
PassedTime = millis () - StartTime;
Serial.println("PassedTime"); Serial.println(PassedTime);
if (PassedTime >= Alarm_delay) // Якщо минуло потрібну кількість часу, встановлюємо прапор та виводимо інформацію
{
ALARM_delayed = true;
Serial.println("Пройшло ХХ хвилин!");
}
}
DelBtn_WasUp = DelBtnState;
}
}
//функція для перемикання екранів LCD дисплея
void changeScreen()
{
if (current_Millis_LCD - previous_Millis_LCD > interval_LCD) // Зберігаємо останній час, коли ми змінювали екран
{
previous_Millis_LCD = current_Millis_LCD;
screens++;
if (screens > 2)
{
screens = 0; //якщо всі екрани закінчилися, починаємо знову з 1 екрану
}
isScreenChanged = true;
}
// Start displaying current screen
if (isScreenChanged) // оновлюємо екран тільки коли номер екрана змінився
{
isScreenChanged = false;
switch (screens)
{
case SCREEN_1:
helloMSG(); // показуємо вітальне повідомлення
break;
case SCREEN_2:
helpScreen(); // Екран з інструкціями
break;
case SCREEN_3:
timeScreen(); // екран з датою та часом
break;
default: break;
}
}
}
// функція для включення будильника о 9am
void at9am() {
DateTime now = rtc.now();
if (nowHr == buzz9amHH && nowMin == buzz9amMM && nowSec >= buzz9amSS && stop9am != 1 && !ALARM_delayed || ALARM_delayed ) {
startBuzz();
}
}
// функція для включення будильника о 2pm
void at2pm() {
DateTime now = rtc.now();
if (nowHr == buzz2pmHH && nowMin == buzz2pmMM && nowSec >= buzz2pmSS && stop2pm != 1 && !ALARM_delayed) {
startBuzz();
}
}
// функція для включення будильника о 9pm
void at9pm() {
DateTime now = rtc.now();
if (nowHr == buzz9pmHH && nowMin == buzz9pmMM && nowSec >= buzz9pmSS && stop9pm != 1 && !ALARM_delayed) {
startBuzz();
}
}
// функція для відображення 1-го екрана на LCD-дисплеї
void helloMSG()
{
lcd.clear();
lcd.setCursor(0, 0); // Курсор на першу колонку, перший рядок.
lcd.print("Stay healthy !");
lcd.setCursor(0, 1); // Курсор на 1 колонку, 2 рядок.
lcd.print("Get well soon :)");
}
// функція для відображення 2-го екрана на LCD-дисплеї
void helpScreen()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set medication");
lcd.setCursor(0, 1);
lcd.print("schedule: 1/2/3");
}
// функція для відображення 3-го екрана на LCD-дисплеї
void timeScreen() {
DateTime now = rtc.now(); // считуємо поточний час
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Date: ");
lcd.print(now.day(), DEC);
lcd.print("/");
lcd.print(now.month(), DEC);
lcd.print("/");
lcd.print(now.year(), DEC);
lcd.setCursor(0, 1);
lcd.print("Time:");
lcd.print(nowHr = now.hour(), DEC);
lcd.print(":");
lcd.print(nowMin = now.minute(), DEC);
lcd.print(":");
lcd.print(nowSec = now.second(), DEC);
delay(200);
}
//функція для матриці
void matrix_display(unsigned char matrix_value[])
{
IIC_start(); // use the function of the data transmission start condition
IIC_send(0xc0); //select address
for (int i = 0; i < 16; i++) //pattern data has 16 bits
{
IIC_send(matrix_value[i]); //convey the pattern data
}
IIC_end(); //end the transmission of pattern data
IIC_start();
IIC_send(0x8A); //display control, set pulse width to 4/16 s
IIC_end();
IIC_start();
IIC_send(0x8A);// set the brightness display
IIC_end();
delay (100);
}
void IIC_start()
{
digitalWrite(IIC_SCL, LOW);
delayMicroseconds(3);
digitalWrite(IIC_SDA, HIGH);
delayMicroseconds(3);
digitalWrite(IIC_SCL, HIGH);
delayMicroseconds(3);
digitalWrite(IIC_SDA, LOW);
delayMicroseconds(3);
}
void IIC_send(unsigned char send_data)
{
for (char i = 0; i < 8; i++)
{
digitalWrite(IIC_SCL, LOW); // pull down clock pin SCL_Pin to change the signal of SDA
delayMicroseconds(3);
if (send_data & 0x01) //set high and low level of SDA_Pin according to 1 or 0 of every bit
{
digitalWrite(IIC_SDA, HIGH);
}
else
{
digitalWrite(IIC_SDA, LOW);
}
delayMicroseconds(3);
digitalWrite(IIC_SCL, HIGH); //pull up the clock pin SCL_Pin to stop transmission
delayMicroseconds(3);
send_data = send_data >> 1; // detect bit by bit, shift the data to the right by one
}
}
void IIC_end()
{
digitalWrite(IIC_SCL, LOW);
delayMicroseconds(3);
digitalWrite(IIC_SDA, LOW);
delayMicroseconds(3);
digitalWrite(IIC_SCL, HIGH);
delayMicroseconds(3);
digitalWrite(IIC_SDA, HIGH);
delayMicroseconds(3);
}