// For: https://forum.arduino.cc/t/i2c-display-not-show-line/1012257
#include <EEPROM.h>
#include "RTClib.h"
RTC_DS3231 rtc;
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NOP __asm__ __volatile__ ("nop\n\t") //Definice nop jen protože mě nefungoval prázdnej while
#define CLOCK_INTERRUPT_PIN 2 //Definice pinu přerušení(máme 2 hardwarový 1 a 2)
byte DoorNum; //Počet dveří
//-----------------------------------//Pomocná která se bude ukládat do EEPROMKY aby se předešlo ztacení dat když vypadne proud
//Otevření Minuty
byte OpenMin0 = 255;
byte OpenMin1 = 255;
byte OpenMin2 = 255;
byte OpenMin3 = 255;
byte OpenMin4 = 255;
byte OpenMin5 = 255;
byte OpenMin6 = 255;
byte OpenMin7 = 255;
//Otevření Hodiny
byte OpenHour0 = 255;
byte OpenHour1 = 255;
byte OpenHour2 = 255;
byte OpenHour3 = 255;
byte OpenHour4 = 255;
byte OpenHour5 = 255;
byte OpenHour6 = 255;
byte OpenHour7 = 255;
//Zavření Minuty
byte CloseMin0 = 255;
byte CloseMin1 = 255;
byte CloseMin2 = 255;
byte CloseMin3 = 255;
byte CloseMin4 = 255;
byte CloseMin5 = 255;
byte CloseMin6 = 255;
byte CloseMin7 = 255;
//Zavření Hodiny
byte CloseHour0 = 255;
byte CloseHour1 = 255;
byte CloseHour2 = 255;
byte CloseHour3 = 255;
byte CloseHour4 = 255;
byte CloseHour5 = 255;
byte CloseHour6 = 255;
byte CloseHour7 = 255;
byte Rok;
byte Mesic;
byte Den;
byte Hodina;
byte Minuta;
//--------------------------------//BTNS
byte LBTN = 5;
byte MBTN = 6;
byte RBTN = 7;
byte LBTNval;
byte MBTNval;
byte RBTNval;
//---------------------------------//
byte Wait1A = 0; //Příznak přetečení časovače 0 podle A registru
byte BootBTN = 10;
byte BootBTNval;
int AlarmSetRow; //Pomocná proměná pro Tabulku pomocí které se bude nastavovat alarm, Jediná důležitá
int DoorArray[18][2] = { //Tabulka ať se lépe pracuje s daty na nastavení alarmu
{OpenHour0, OpenMin0},
{OpenHour1, OpenMin1},
{OpenHour2, OpenMin2},
{OpenHour3, OpenMin3},
{OpenHour4, OpenMin4},
{OpenHour5, OpenMin5},
{OpenHour6, OpenMin6},
{OpenHour7, OpenMin7},
{12, 0}, //Kontrolní bod
{CloseHour0, CloseMin0},
{CloseHour1, CloseMin1},
{CloseHour2, CloseMin2},
{CloseHour3, CloseMin3},
{CloseHour4, CloseMin4},
{CloseHour5, CloseMin5},
{CloseHour6, CloseMin6},
{CloseHour7, CloseMin7},
{23, 59}
}; //Kontrolní bod
void setup() {
// put your setup code here, to run once:
//----------------------------------------------------interupt-------------------//
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 625; // compare match register 10ms
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
Serial.begin(9600);
#ifndef ESP8266
while (!Serial); // wait for serial port to connect. Needed for native USB
#endif
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay1A(1);
}
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay1A(200); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
//-----------------------------BTNSetup---------------------------------------------------//
pinMode(LBTN, INPUT_PULLUP);
pinMode(MBTN, INPUT_PULLUP);
pinMode(RBTN, INPUT_PULLUP);
pinMode(BootBTN, INPUT_PULLUP);
BootBTNval = digitalRead(BootBTN);
LBTNval = digitalRead(LBTN);
MBTNval = digitalRead(MBTN);
RBTNval = digitalRead(RBTN);
//------------------------------Date set--------------------------------------------------//
pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP);
rtc.disable32K();
attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), onAlarm, FALLING);
rtc.clearAlarm(2);
rtc.disableAlarm(2);
if (BootBTNval == HIGH) {
DoorNum = Dateset(DoorNum, 5, "PocetDveri");
for (byte i = 0; i < DoorNum; i++) {
DoorAlarmSet(i, "otevreni", DoorArray);
DoorAlarmSet(i, "zavreni", DoorArray);
}
rtc.disableAlarm(1);
Rok = Dateset(Rok, 99, "Rok"); //Nastavení jednotlivých dat (zde se nastaví rok
Mesic = Dateset(Mesic, 12, "Mesic");
Den = Dateset(Den, 31, "Den");
Hodina = Dateset(Hodina, 23, "Hodiny");
Minuta = Dateset(Minuta, 59, "Minuty");
rtc.adjust(DateTime(Rok, Mesic, Den, Hodina, Minuta, 0)); //year, Month,Day,Hour,Minute,Seconds //Finální nastavení RTC pomocí předchozích
//---------------------------DoorSetup--------------------------------------------//
delay1A(10);
ArraySave();
DoorNumSave();
}
if (BootBTNval == LOW) {
DataLoad();
}
AlarmSet(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<------------ THIS LINE
}
void loop() {
// put your main code here, to run repeatedly:
DisplayMenu();
delay1A(100);
}
ISR(TIMER1_COMPA_vect) //Přerušení časovače 0 podle registru A
{
Wait1A = 1;
}
void delay1A(int timevector) { //10ms delay (protože nikdo nemá rád když napíšeš delay(10) tak si ho musíš udělat sám :)
for (int i = timevector; i > Wait1A; i--) {
while (Wait1A == 0)
{
NOP; //Zde ta nopka která vyřešila problémky
}
Wait1A = 0;
}
}
void onAlarm() { //Zde udělat vše když se zapne alarm
//otevření/zavření dveří
Serial.println("Alarm occured!");
}
int Dateset (int cislo, int maximum, String What) { //Nastavení Dat RTC hodin (Rok, měsíc, den, hodiny,....)(int datum- Co nastavuji, int maximum- maximální počet (dnů v ,ěsíci, hodin, minut..., String What-Pomocná pro naspání co nastavuju)
NormDisplaySet(2);
display.println("Natav ");
display.println(What);
display.print(cislo);
DispBottom();
display.display();
while (RBTNval == 1) { //Nastavení dat(levé tlačítko přidat, Prostřední ubrat, Pravé uložit
if (LBTNval == 0) {
if (cislo == maximum) {
cislo = 0;
}
else {
cislo = cislo + 1;
}
}
if (MBTNval == 0) {
if (cislo == 0) {
cislo = maximum;
}
else {
cislo = cislo - 1;
}
}
NormDisplaySet(2);
display.println("Nastav ");
display.println(What);
display.print(cislo);
Serial.println(What);
DispBottom();
display.display();
delay1A(50);
BTNpress();
}
while (RBTNval == 0) {
RBTNval = digitalRead(RBTN);
NormDisplaySet(2);
display.println("");
display.print(" Nastaveno");
display.display();
delay1A(50);
}
return cislo;
}
void BTNpress() {
LBTNval = digitalRead(LBTN);
RBTNval = digitalRead(RBTN);
MBTNval = digitalRead(MBTN);
while (LBTNval == HIGH && RBTNval == HIGH && MBTNval == HIGH) {
LBTNval = digitalRead(LBTN);
RBTNval = digitalRead(RBTN);
MBTNval = digitalRead(MBTN);
delay1A(1);
}
}
void DoorAlarmSet(int i, String what, int DoorArray[19][2]) {
NormDisplaySet(2);
display.println("Nastav");
display.println(what);
display.print("dveri ");
display.println(i);
display.setCursor(64, 55);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.print("OK");
display.display();
BTNpress();
while (RBTNval == 0) {
RBTNval = digitalRead(RBTN);
}
delay1A(50);
DoorArray[i][0] = Dateset(Hodina, 23, "Hodinu");
DoorArray[i][1] = Dateset(Minuta, 59, "Minutu");
}
void NormDisplaySet(int TextSize) {
display.clearDisplay();
display.setTextSize(TextSize);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
}
void DispBottom() {
display.fillTriangle(10, 64, 20, 64, 15, 55, SSD1306_WHITE);
display.fillTriangle(59, 55, 64, 64, 69, 55, SSD1306_WHITE);
display.setCursor(110, 55);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.print("OK");
}
void AlarmSet() {
int AlarmSetHour = 250; //Pomocná proměná pro nastavení Hodiny Otevření/Zavření dveří podle času
int AlarmSetMinute = 250; //Pomocná proměná pro nastavení Minuty Otevření/Zavření dveří podle času
AlarmSetRow = 0;
for (int i = 0; i < 18; i++) { //Filtr pro alarm kdy se snažíme oddělat aktuální alarm, a alarmy který už proběhly. Dále hledáme nejbližší alarm který má nadejít
if (DoorArray[i][0] == Hodina) {
if (DoorArray[i][1] > Minuta) {
if (DoorArray[i][1] < AlarmSetMinute) {
AlarmSetHour = DoorArray[i][0];
AlarmSetRow = i;
AlarmSetMinute = DoorArray[i][1];
}
}
}
if (DoorArray[i][0] > Hodina) {
if (DoorArray[i][0] < AlarmSetHour) {
AlarmSetHour = DoorArray[i][0];
AlarmSetRow = i;
AlarmSetMinute = 255;
}
if (DoorArray[i][0] == AlarmSetHour) {
if (DoorArray[i][1] < AlarmSetMinute) {
AlarmSetHour = DoorArray[i][0];
AlarmSetRow = i;
AlarmSetMinute = 255;
}
}
}
}
delay1A(1);
rtc.setAlarm1(DateTime(Rok, Mesic, Den, DoorArray[AlarmSetRow][0], DoorArray[AlarmSetRow][1], 0), DS3231_A1_Hour);
delay1A(10);
}
void ArraySave() {
int k = 0;
for (int i = 0; i < 18; i++) {
for (int j = 0; j < 2; j++) {
EEPROM.write(20 + k, DoorArray[i][j]);
k++;
}
}
}
void DataLoad() {
int k = 0;
for (int i = 0; i < 18; i++) {
for (int j = 0; j < 2; j++) {
DoorArray[i][j] = EEPROM.read(20 + k);
k++;
}
}
}
void DoorNumSave() {
EEPROM.write(10, DoorNum);
}
void DisplayMenu() {
NormDisplaySet(2);
DateTime now = rtc.now();
display.print(now.year(), DEC);
display.print('/');
display.print(now.month(), DEC);
display.print('/');
display.println(now.day(), DEC);
display.print(now.hour(), DEC);
display.print(':');
display.print(now.minute(), DEC);
display.print(':');
display.print(now.second(), DEC);
display.println();
if (AlarmSetRow < 8) {
display.print("Otevreni dv. ");
display.print(AlarmSetRow);
}
if (AlarmSetRow == 8 || AlarmSetRow == 17) {
display.print("Kontrolni bod");
display.print(AlarmSetRow);
}
if (AlarmSetRow > 8 && AlarmSetRow < 17) {
display.print("Zavreni dv. ");
display.print(AlarmSetRow - 8);
}
display.display();
}