#include <Arduino.h>
#include <U8g2lib.h> // u8g2 library for drawing on OLED display - needs to be installed in Arduino IDE first
#include <Wire.h> // wire library for IIC communication with the OLED display
#include "anims.h"
// U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0); // set the OLED display to 128x64px, HW IIC, no rotation, used in WOKWI
U8G2_SH1107_128X128_1_HW_I2C u8g2(U8G2_R0); // final display, 128x128px [page buffer, size = 128 bytes], HW IIC connection
// IIC connection of the OLED display and Arduino UNO
// +5V > +5V
// GND > GND
// SCL (serial clock) > A5 or SCL
// SDA (serial data) > A4 or SDA
const unsigned char upir_logo [] PROGMEM = { // upir logo
0xEA, 0x3A, 0xAA, 0x28, 0x6A, 0x1A, 0x26, 0x2A, };
int center_x = 64; // display x center, 64px for the 128x128px display
int center_y = 64; // display y center, 64px for the 128x128px display
// hardcoded time values
int time_minutes = 10;
int time_hours = 10;
int time_seconds = 45;
// HW Pins required
#define BUTTON_UP 4
#define BUTTON_DOWN 7
#define BUTTON_LEFT 3
#define BUTTON_RIGHT 6
#define BUTTON_ENTER 5
//FPS
bool SHOW_FPS = false; // < SHOW FPS ON SCREEN
unsigned int LIMITFPSTO = 60; // < LIMIT SCREEN FPS
unsigned long millisFPS = 0;
unsigned long lastMillis = 0;
unsigned int frameCount = 0;
unsigned int fps = 0;
//Calendar App
bool exitCalendarApp = false;
#define MAX_CALENDAR_EVENTS 99
struct CalendarEvent {unsigned long unixTime;String eventName;};
CalendarEvent calendarEvents[MAX_CALENDAR_EVENTS];
void displayFrame() {
if (millis() - millisFPS >= int(1000/LIMITFPSTO)) {
millisFPS = millis();
fpsCounter();
u8g2.sendBuffer();
u8g2.clearBuffer();
}
}
void updateAnims() {
unsigned long currentMillis30FPS = millis();
if (step30FPS) {step30FPS = false;}
if (currentMillis30FPS - lastMillis30FPS >= 33) {
step1FPSInt++;
if (step1FPSInt > 30) {
step1FPS = true;
step1FPSInt = 0;
}
else {step1FPS = false;}
// Save the current time
lastMillis30FPS = currentMillis30FPS;
step30FPS = true;
}
if (animFrames[0] == 1) { //Page Flip
u8g2.setBitmapMode(1);
pageFlipAnim();
}
if (animFrames[1] != 0) { //Big Gear 2
u8g2.drawXBMP(77,4,50,120,bigGear2allArray[animFrames[1]-1]);
}
}
void fpsCounter() {
if (SHOW_FPS){
frameCount++;
if (millis() - lastMillis >= 1000) { //FPS COUNTER
fps = frameCount;
// Serial.print("FPS: "); Serial.println(fps);
frameCount = 0;lastMillis = millis();
}
u8g2.drawStr(0,128,("FPS: "+String(fps)).c_str()); //Show current FPS
}
}
void setup(void) { // Arduino setup
u8g2.begin(); // begin the u8g2 library
u8g2.setContrast(255); // set display contrast/brightness
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);
pinMode(BUTTON_ENTER, INPUT_PULLUP);
}
int dayToday = 12;
int monthToday = 3;
int yearToday = 2024;
volatile int selectedDay = dayToday;
volatile int selectedMonth = monthToday;
volatile int selectedYear = yearToday;
enum CalendarAppState {HOME, DAY_VIEW, ADD_EVENT};
CalendarAppState calendarCurrentState = HOME;
bool testOnce = false;
int daysInMonth(int month, int year);
void updateCalendarInputs() {
// aButton.update();
// bButton.update();
// int sEncoder_change = sEncoder.getCount();
// int lEncoder_change = lEncoder.getCount();
switch (calendarCurrentState) {
case HOME:
// if (sEncoder_change) {
// if ((sEncoder_change < 0)) {
// selectedDay++;
// if (selectedDay > daysInMonth(selectedMonth, selectedYear)) {
// selectedDay = 1;
// selectedMonth++;
// animFrames[0] = 1;
// if (selectedMonth > 12) {
// selectedMonth = 1;
// selectedYear++;
// }
// }
// }
// else if (sEncoder_change > 0) {
// selectedDay--;
// if (selectedDay < 1) {
// animFrames[0] = 1;
// if (selectedMonth == 1) {
// selectedMonth = 12;
// selectedYear--;
// }
// else {
// selectedMonth--;
// }
// selectedDay = daysInMonth(selectedMonth, selectedYear);
// }
// }
// sEncoder.setCount(0);
// }
// if (lEncoder_change) {
// if (lEncoder_change < 0) {
// selectedMonth++;
// if (selectedMonth > 12) {
// selectedMonth = 1;
// selectedYear++;
// }
// }
// if (lEncoder_change > 0) {
// if (selectedMonth == 1) {
// selectedMonth = 12;
// selectedYear--;
// }
// else {
// selectedMonth--;
// }
// }
// if (selectedDay > daysInMonth(selectedMonth, selectedYear)) {
// selectedDay = daysInMonth(selectedMonth, selectedYear);
// }
// animFrames[0] = 1;
// }
// if (aButton.fell()) {
// calendarCurrentState = DAY_VIEW;
// }
// if (bButton.fell()) {
// exitCalendarApp = true;
// break;
// }
break;
case DAY_VIEW:
// if (aButton.fell()) {
// }
// if (bButton.fell()) {
// calendarCurrentState = HOME;
// delay(250);
// }
break;
}
}
int getDayFromUnixTime(unsigned long unixTime) {
return (unixTime / 86400L) % 31 + 1; // Assuming every month has 31 days
}
int getMonthFromUnixTime(unsigned long unixTime) {
return ((unixTime / 2629743L) % 12) + 1; // Approximate calculation assuming a 30-day month
}
int getYearFromUnixTime(unsigned long unixTime) {
return (unixTime / 31556926L) + 1970; // Approximate calculation assuming 365.25 days per year
}
// void addEvent(unsigned long time, const String& name) {
// // Find the first available slot in the array
// for (int i = 0; i < MAX_CALENDAR_EVENTS; ++i) {
// if (calendarEvents[i].unixTime == 0) {
// // Found an empty slot, add the event
// calendarEvents[i].unixTime = time;
// calendarEvents[i].eventName = name;
// break;
// }
// }
// // saveCalendarEventsToFile();
// }
// std::vector<int> findEventsOnDay(int day, int month, int year) {
// // Create a vector to store indices of events
// std::vector<int> eventIndices;
// // Iterate through events list
// for (int i = 0; i < MAX_CALENDAR_EVENTS; ++i) {
// // Extract day, month, and year from event's unix time
// int eventDay = getDayFromUnixTime(calendarEvents[i].unixTime);
// int eventMonth = getMonthFromUnixTime(calendarEvents[i].unixTime);
// int eventYear = getYearFromUnixTime(calendarEvents[i].unixTime);
// // Check if event occurs on the specified day, month, and year
// if (eventDay == day && eventMonth == month && eventYear == year) {
// // Add index of the event to the vector
// eventIndices.push_back(i);
// }
// }
// // Return the vector containing indices of events
// return eventIndices;
// }
// void saveCalendarEventsToFile() {
// File calendarEventsFile = SPIFFS.open("/events.txt", FILE_WRITE);
// if (!calendarEventsFile) {
// //Serial.println("Failed to open calendar events file for writing");
// return;
// }
// // Write each calendar event to file
// for (int i = 0; i < MAX_CALENDAR_EVENTS; i++) {
// calendarEventsFile.println(calendarEvents[i].unixTime);
// calendarEventsFile.println(calendarEvents[i].eventName);
// }
// calendarEventsFile.close();
// //Serial.println("Calendar events saved to file");
// }
// void loadCalendarEventsFromFile() {
// File calendarEventsFile = SPIFFS.open("/events.txt", FILE_READ);
// if (!calendarEventsFile) {
// //Serial.println("Failed to open calendar events file for reading");
// return;
// }
// // Read each calendar event from file
// for (int i = 0; i < MAX_CALENDAR_EVENTS; i++) {
// if (calendarEventsFile.available()) {
// calendarEvents[i].unixTime = calendarEventsFile.readStringUntil('\n').toInt();
// calendarEvents[i].eventName = calendarEventsFile.readStringUntil('\n');
// }
// }
// calendarEventsFile.close();
// //Serial.println("Calendar events loaded from file");
// }
int zellersCongruence(int day, int month, int year) {
String dayOfWeekString;
int iso = 0;
if (month < 3) {
month += 12;
year--;
}
int h = (day + 13 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
iso = (h + 6) % 7;// + 1; //0 is sunday, 1 is monday, ... , 6 is saturday
return iso;
}
int daysInMonth(int month, int year) {
if (month < 1 || month > 12) {
// Invalid month
return -1;
}
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
return 31;
case 4: case 6: case 9: case 11:
return 30;
case 2:
// Check for leap year
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
return 29; // Leap year
} else {
return 28; // Non-leap year
}
default:
return -1; // Invalid month
}
}
String getMonthName(int month) {
static const char* monthNames[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
if (month < 1 || month > 12) {
return "Invalid Month";
}
return String(monthNames[month - 1]);
}
void drawMonth () {
int i = zellersCongruence(1, selectedMonth, selectedYear);
int daysInSelectedMonth = daysInMonth(selectedMonth, selectedYear);
int j = 0;
int k = 1;
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.drawStr(4, 10, (getMonthName(selectedMonth) + " " + String(selectedYear)).c_str());
while (k <= daysInSelectedMonth) { // Draw the month day by day
u8g2.drawStr((i*18)+4,(j*18)+30,String(k).c_str());
if (k == dayToday && selectedMonth == monthToday && selectedYear == yearToday) {
u8g2.drawCircle((i*18)+9,(j*18)+28, 9);
}
else {
u8g2.drawRFrame((i*18)+1,(j*18)+20,18,18,5);
}
if (k == selectedDay) {
u8g2.drawHLine((i*18)+14,(j*18)+33,3);
u8g2.drawVLine((i*18)+14,(j*18)+33,3);
u8g2.drawLine((i*18)+14,(j*18)+33,(i*18)+14+4,(j*18)+33+4);
}
k++;i++;
if (i > 6) {i = 0; j++;}
}
}
void loadCalendar() {
switch (calendarCurrentState) {
case HOME:
drawMonth();
break;
case DAY_VIEW:
drawMonth();
u8g2.setDrawColor(0);
u8g2.drawXBMP(0, 0, 128, 128, fuzzallArray[0]);
u8g2.drawBox(24,3,78,118);
u8g2.setDrawColor(1);
u8g2.drawFrame(23,2,80,120);
u8g2.drawXBMP(60,-2,25,25,iconsallArray[2]);
String dayName = "";
switch (zellersCongruence(selectedDay, selectedMonth, selectedYear)) {
case 0:
dayName = "Sunday,";
break;
case 1:
dayName = "Monday,";
break;
case 2:
dayName = "Tuesday,";
break;
case 3:
dayName = "Wednesday,";
break;
case 4:
dayName = "Thursday,";
break;
case 5:
dayName = "Friday,";
break;
case 6:
dayName = "Saturday,";
break;
}
u8g2.drawStr(30,26,dayName.c_str());
u8g2.drawStr(30,34,(String(selectedMonth) + "/" + String(selectedDay) + "/" + String(selectedYear)).c_str());
u8g2.drawHLine(26,36,75);
//Add menu script to load events on day
// std::vector<int> indices = findEventsOnDay(selectedDay, selectedMonth, selectedYear);
// int eventY = 0;
// for (int index : indices) {
// u8g2.drawStr(26, 37+(8*eventY), (calendarEvents[index].eventName).c_str());
// eventY++;
// }
break;
}
}
void calendarLoop() {
/*if (testOnce) {
// Add some sample events
addEvent(1710280728, "Meeting");
addEvent(1710280800, "Appointment");
// Save events to file
saveCalendarEventsToFile();
testOnce = false;
std::vector<int> indices = findEventsOnDay(selectedDay, selectedMonth, selectedYear);
int eventY = 0;
for (int index : indices) {
u8g2.drawStr(26, 37+(8*eventY), (calendarEvents[index].eventName).c_str());
eventY++;
}
u8g2.sendBuffer();
delay(5000);
}*/
updateCalendarInputs();
updateAnims();
loadCalendar();
displayFrame();
}
void loop(void) { // main Arduino loop
static bool state_up = false;
static bool state_down = false;
static bool state_left = false;
static bool state_right = false;
static bool state_enter = false;
state_up |= (digitalRead(BUTTON_UP) == LOW);
state_down |= (digitalRead(BUTTON_DOWN) == LOW);
state_left |= (digitalRead(BUTTON_LEFT) == LOW);
state_right |= (digitalRead(BUTTON_RIGHT) == LOW);
state_enter |= (digitalRead(BUTTON_ENTER) == LOW);
calendarLoop();
// u8g2.firstPage(); // select the first page of the display (page is 128x8px), since we are using the page drawing method of the u8g2 library
// do { // draw
// u8g2.setFont(u8g2_font_8x13B_mn); // set the u8g2 font
// u8g2.drawStr(57,20,"12");
// u8g2.drawStr(112,69,"3");
// u8g2.drawStr(61,120,"6");
// u8g2.drawStr(9,69,"9");
// } while ( u8g2.nextPage() ); // go over all the pages until the whole display is updated
// // for(;;);
}