#include <SPI.h>
#include <Wire.h>
#include <RTClib.h>
#include <U8g2lib.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
//U8G2 -> what library is being used
//SSD1306 -> which OLED is being used
//128X64 -> the resolution size of the OLED
//NONAME -> suggests that it's a generic or unspecified variant OLED (just a normal OLED really)
//F_HW_I2C -> indicates that it uses hardware I2C communication.
// U8G2_R0 -> this parameter specifies the rotation of the display. U8G2_R0 means no rotation. You can change this parameter based on how your display is physically oriented.
// U8X8_PIN_NONE -> this part is specifying that the reset pin is set to U8X8_PIN_NONE, meaning that no reset pin is used. If your display has a reset pin and you want to use it, you can replace U8X8_PIN_NONE with the appropriate pin number.
RTC_DS1307 rtc; // the real time clock component variable name (rtc)
const int selectButtonPin = 8;
int lastSelectButtonPinState = LOW; // Variable to store the last state of the button
int currentSelectButtonPinState; // Variable to store the current state of the button
const int buttonPin = 7; // Define the pin for the button
int lastButtonPinState = HIGH; // Variable to store the last state of the button
int currentButtonPinState; // Variable to store the current state of the button
const int leftButtonPin = 6;
int lastLeftButtonPinState = LOW; // Variable to store the last state of the button
int currentLeftButtonPinState; // Variable to store the current state of the button
const int rightButtonPin = 5;
int lastRightButtonPinState = LOW; // Variable to store the last state of the button
int currentRightButtonPinState; // Variable to store the current state of the button
static const unsigned char image_CoolHi_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x10,0x00,0x01,0x01,0x92,0x00,0x01,0x81,0x54,0x02,0x01,0x81,0x38,0x02,0x01,0xe1,0x10,0x0e,0x01,0x01,0x11,0x01,0x01,0x11,0x92,0x10,0x01,0x21,0x54,0x08,0x01,0x41,0x38,0x04,0x01,0xf9,0xff,0x3f,0x01,0x41,0x38,0x04,0x01,0x21,0x54,0x08,0x01,0x11,0x92,0x10,0x01,0x01,0x11,0x01,0x01,0xe1,0x10,0x0e,0x01,0x81,0x38,0x02,0x01,0x81,0x54,0x02,0x01,0x01,0x92,0x00,0x01,0x01,0x10,0x00,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
static const unsigned char image_Dehumidify_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x10,0x00,0x01,0x01,0x10,0x00,0x01,0x01,0x18,0x00,0x01,0x01,0x38,0x00,0x01,0x01,0x3c,0x00,0x01,0x01,0x7c,0x00,0x01,0x01,0xfe,0x00,0x01,0x01,0xfe,0x00,0x01,0x01,0xbf,0x01,0x01,0x01,0x7f,0x01,0x01,0x81,0x7f,0x03,0x01,0x81,0xff,0x03,0x01,0x01,0xff,0x01,0x01,0x01,0xff,0x01,0x01,0x01,0xfe,0x00,0x01,0x01,0x78,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
static const unsigned char image_HeatHi_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x10,0x00,0x01,0x11,0x10,0x10,0x01,0x21,0x10,0x08,0x01,0x41,0x10,0x04,0x01,0x81,0x00,0x02,0x01,0x01,0x7c,0x00,0x01,0x01,0x82,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x79,0x01,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x82,0x00,0x01,0x01,0x7c,0x00,0x01,0x81,0x00,0x02,0x01,0x41,0x10,0x04,0x01,0x21,0x10,0x08,0x01,0x11,0x10,0x10,0x01,0x01,0x10,0x00,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
static const unsigned char image_Mute_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x00,0x00,0x01,0x01,0x00,0x03,0x01,0x21,0x80,0x14,0x01,0x41,0x40,0x0c,0x01,0x81,0x20,0x04,0x01,0x01,0x1f,0x06,0x01,0x81,0x02,0x05,0x01,0x81,0x84,0x04,0x01,0x81,0x48,0x04,0x01,0x81,0x30,0x04,0x01,0x81,0x30,0x04,0x01,0x81,0x48,0x04,0x01,0x81,0x84,0x04,0x01,0x81,0x02,0x05,0x01,0x01,0x1f,0x06,0x01,0x81,0x20,0x04,0x01,0x41,0x40,0x0c,0x01,0x21,0x80,0x14,0x01,0x01,0x00,0x03,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
static const unsigned char image_Power_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x00,0x00,0x01,0x01,0x10,0x00,0x01,0x01,0x38,0x00,0x01,0x01,0x38,0x00,0x01,0x81,0x39,0x03,0x01,0xc1,0x39,0x07,0x01,0xe1,0x38,0x0e,0x01,0x61,0x38,0x0c,0x01,0x61,0x38,0x0c,0x01,0x61,0x10,0x0c,0x01,0x61,0x00,0x0c,0x01,0x61,0x00,0x0c,0x01,0x61,0x00,0x0c,0x01,0xe1,0x00,0x0e,0x01,0xc1,0x01,0x07,0x01,0x81,0x83,0x03,0x01,0x01,0xff,0x01,0x01,0x01,0x7c,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
static const unsigned char image_Off_25x27_bits[] U8X8_PROGMEM = {0xf8,0xff,0x3f,0x00,0x04,0x00,0x40,0x00,0x02,0x00,0x80,0x00,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0xe1,0xf8,0x3e,0x01,0xb1,0x19,0x06,0x01,0x19,0x1b,0x06,0x01,0x19,0x7b,0x1e,0x01,0x19,0x1b,0x06,0x01,0x19,0x1b,0x06,0x01,0xb1,0x19,0x06,0x01,0xe1,0x18,0x06,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x03,0x00,0x80,0x01,0x06,0x00,0xc0,0x00,0xfc,0xff,0x7f,0x00,0xf8,0xff,0x3f,0x00};
// drawing frames, shapes and those that aren't images/bitmaps are different as whatever dimension is defined in the parameters are where the pixels will be drawn
// Create an array of pointers to these constant arrays
const unsigned char* iconArray[] = {
image_CoolHi_25x27_bits,
image_Dehumidify_25x27_bits,
image_HeatHi_25x27_bits,
image_Mute_25x27_bits,
image_Power_25x27_bits,
image_Off_25x27_bits
};
int iconArrayLength = (sizeof(iconArray) / sizeof(iconArray[0])) - 1;
// to get total length of icon array (0-indexed based)
// so right now, *iconArrayLength* = 5 (because 6 - 1 = 5)
bool isDisplayOn = true; // Flag to track the OLED display state
int index = 0; // an index to keep track of what icon we're up to
int pageNumber = 0;
// clock page = 0
// menu page = 1
// icon 1 page = 2
void setup() {
Serial.begin(115200);
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (!rtc.isrunning()) {
Serial.println("RTC is not running, let's set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
u8g2.begin();
pinMode(selectButtonPin, INPUT);
pinMode(buttonPin, INPUT);
pinMode(leftButtonPin, INPUT);
pinMode(rightButtonPin, INPUT);
u8g2.clearBuffer(); // display.clearDisplay();
}
void loop() {
u8g2.setBitmapMode(1);
displayState(); // ON/OFF CODE
if (pageNumber == 0) {
displayTime();
}
else if (pageNumber == 1) {
displayMenu();
}
else if (pageNumber == 2) {
icon1Page();
}
rightButtonPressed(); // right button is pressed
selectButtonPressed(); // select button is pressed
leftButtonPressed(); // left button is pressed
u8g2.sendBuffer(); // display.display();
}
void rightButtonPressed() {
if (digitalRead(rightButtonPin) != lastRightButtonPinState) {
delay(50);
currentRightButtonPinState = digitalRead(rightButtonPin);
if (currentRightButtonPinState == LOW && lastRightButtonPinState == HIGH) {
if (pageNumber == 1) {
index += 1;
}
}
lastRightButtonPinState = currentRightButtonPinState;
}
}
void leftButtonPressed() {
if (digitalRead(leftButtonPin) != lastLeftButtonPinState) {
delay(50);
currentLeftButtonPinState = digitalRead(leftButtonPin);
if (currentLeftButtonPinState == LOW && lastLeftButtonPinState == HIGH) {
if (pageNumber == 1) {
index -= 1;
}
if (pageNumber == 2) {
pageNumber = 1;
index = 0;
}
}
lastLeftButtonPinState = currentLeftButtonPinState;
}
}
void selectButtonPressed() {
if (digitalRead(selectButtonPin) != lastSelectButtonPinState) {
delay(50);
currentSelectButtonPinState = digitalRead(selectButtonPin);
if (currentSelectButtonPinState == LOW && lastSelectButtonPinState == HIGH) {
if (pageNumber == 0) {
pageNumber = 1;
}
else if (pageNumber == 1 && index == 0) { // I have this as an *else if* rather than a separate *if* because if I enter press *selectButton* when in *timeDisplay*, then the Arduino will make my next page display as icon1 rather than the menu display because they are two different if statements
pageNumber = 2;
}
}
lastSelectButtonPinState = currentSelectButtonPinState;
}
}
void drawIcons() {
u8g2.drawXBMP( 5, 3, 25, 27, image_CoolHi_25x27_bits); // the first four parameters (x1, y2, width, height) define the dimension for the image to be drawn in (which is the fifth parameter)
u8g2.drawXBMP( 52, 3, 25, 27, image_Dehumidify_25x27_bits);
u8g2.drawXBMP( 98, 3, 25, 27, image_HeatHi_25x27_bits);
u8g2.drawXBMP( 5, 34, 25, 27, image_Mute_25x27_bits);
u8g2.drawXBMP( 52, 34, 25, 27, image_Power_25x27_bits);
u8g2.drawXBMP( 98, 34, 25, 27, image_Off_25x27_bits);
}
void displayTime() {
DateTime now = rtc.now();
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.setCursor(35, 24);
u8g2.print(now.day(), DEC);
u8g2.print('/');
u8g2.print(now.month(), DEC);
u8g2.print('/');
u8g2.print(now.year(), DEC);
u8g2.setCursor(44,44);
u8g2.print(now.hour(), DEC);
u8g2.print(':');
u8g2.print(now.minute(), DEC);
u8g2.print(':');
u8g2.print(now.second(), DEC);
} while (u8g2.nextPage());
delay(50);
}
void displayMenu() {
if (index == 0) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(2, 0, 31, 33); // x1 = 2 for first icon
}
else if (index == 1) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(49, 0, 31, 33); // x1 = 49 for second icon
}
else if (index == 2) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(95, 0, 31, 33); // x1 = 95 for third icon
}
else if (index == 3) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(2, 31, 31, 33); // x1 = 95 for fourth icon
}
else if (index == 4) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(49, 31, 31, 33); // x1 = 95 for fifth icon
}
else if (index == 5) {
u8g2.clearBuffer();
drawIcons();
u8g2.drawFrame(95, 31, 31, 33); // x1 = 95 for sixth icon
}
if (index < 0) {
index = iconArrayLength;
} else if (index > iconArrayLength) {
index = 0;
}
}
void icon1Page() {
u8g2.clearBuffer();
u8g2.firstPage(); // with ug82 library, you have to declare the first page and next page to put text on the OLED. the *nextPage()* will only be called for the pages that are required to have pixels displayed, so this function won't call all pages if it doesn't need to.
do { // Rendering code for the current page
u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.drawStr(0, 20, "Hello, World!"); // x, y, string
} while (u8g2.nextPage());
}
void displayState() {
if (digitalRead(buttonPin) != lastButtonPinState) {
delay(50); // Delay for debounce
currentButtonPinState = digitalRead(buttonPin);
if (currentButtonPinState == LOW && lastButtonPinState == HIGH) { // when sketch is run for the first time, this if-code block actually runs as *lastButtonState* is set to HIGH initially which then changes when I press the button
// Button is pressed for the first time
isDisplayOn = !isDisplayOn; // Toggle the display state
if (isDisplayOn) { // turn on
pageNumber = 0;
u8g2.setPowerSave(0); // Wake up the display (display.ssd1306_command(SSD1306_DISPLAYON);)
} else { // turn off
index = 0; // reset back to 0
pageNumber = 0;
u8g2.clearBuffer();
u8g2.setPowerSave(1); // Put the display to sleep (display.ssd1306_command(SSD1306_DISPLAYOFF);)
}
}
lastButtonPinState = currentButtonPinState;
}
}