#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define PIN_RELAY_FAN 17
#define PIN_RELAY_COOLING 18
#define PIN_RELAY_DEFROST 19
#define PIN_ROTARY_CLK 21
#define PIN_ROTARY_DT 22
#define PIN_ROTARY_SW 23
#define PIN_HUMIDITY 32
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Enumeration for text alignment options
enum Alignment {
CENTER_BOTTOM,
CENTER_TOP,
CENTER_LEFT,
CENTER_RIGHT,
LEFT_BOTTOM,
RIGHT_BOTTOM,
CENTER, // Center both horizontally and vertically
LEFT_TOP,
RIGHT_TOP
};
int lastEncoderPosition = 0; // Last position of the encoder
int currentSelection = 0; // Current menu selection
bool menuActive = true; // Flag to indicate if the menu is active
// Menu options
const char* mainMenuOptions[] = {"[Menu]", "[Exit]"};
const char* setupMenuOptions[] = {"Setup Sensor", "Other Option", "[Back]"};
const int mainMenuCount = sizeof(mainMenuOptions) / sizeof(mainMenuOptions[0]);
const int setupMenuCount = sizeof(setupMenuOptions) / sizeof(setupMenuOptions[0]);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Function to calculate positions and display text based on alignment and text size
void displayText(String text, Alignment alignment, int textSize = 1) {
int textLength = text.length();
int charWidth = 6 * textSize; // Each character width increases with text size
int charHeight = 8 * textSize; // Each character height increases with text size
int textWidth = textLength * charWidth; // Total text width
int xPosition = 0, yPosition = 0;
// Set the text size
display.setTextSize(textSize);
// Determine the position based on the alignment type
switch (alignment) {
case CENTER_BOTTOM:
xPosition = (SCREEN_WIDTH - textWidth) / 2;
yPosition = SCREEN_HEIGHT - charHeight;
break;
case CENTER_TOP:
xPosition = (SCREEN_WIDTH - textWidth) / 2;
yPosition = 0;
break;
case CENTER_LEFT:
xPosition = 0;
yPosition = (SCREEN_HEIGHT - charHeight) / 2;
break;
case CENTER_RIGHT:
xPosition = SCREEN_WIDTH - textWidth;
yPosition = (SCREEN_HEIGHT - charHeight) / 2;
break;
case LEFT_BOTTOM:
xPosition = 0;
yPosition = SCREEN_HEIGHT - charHeight;
break;
case RIGHT_BOTTOM:
xPosition = SCREEN_WIDTH - textWidth;
yPosition = SCREEN_HEIGHT - charHeight;
break;
case CENTER:
xPosition = (SCREEN_WIDTH - textWidth) / 2;
yPosition = (SCREEN_HEIGHT - charHeight) / 2;
break;
case LEFT_TOP:
xPosition = 0;
yPosition = 0;
break;
case RIGHT_TOP:
xPosition = SCREEN_WIDTH - textWidth;
yPosition = 0;
break;
}
Serial.print("x: ");
Serial.println(xPosition);
Serial.print("y: ");
Serial.println(yPosition);
// Set the cursor to the calculated position
display.setCursor(xPosition, yPosition);
display.println(text);
display.display(); // Update the display with the aligned text
}
void displayMainMenu() {
display.clearDisplay(); // Clear the display
for (int i = 0; i < mainMenuCount; i++) {
display.setCursor(0, i * 10); // Set cursor for each menu item
if (i == currentSelection) {
display.print("> "); // Indicate the selected menu item
}
display.println(mainMenuOptions[i]); // Display menu options
}
display.display(); // Update the display
}
void displaySetupMenu() {
display.clearDisplay(); // Clear the display
for (int i = 0; i < setupMenuCount; i++) {
display.setCursor(0, i * 10); // Set cursor for each setup item
if (i == currentSelection) {
display.print("> "); // Indicate the selected setup item
}
display.println(setupMenuOptions[i]); // Display setup options
}
display.display(); // Update the display
}
// Function to read the rotary encoder
int readEncoder() {
static int lastStateA = LOW; // Last state of pin A
int currentStateA = digitalRead(PIN_ROTARY_CLK); // Read the current state of pin A
// Check if the state has changed
if (currentStateA != lastStateA) {
// If the state of A is HIGH, check the state of B
if (currentStateA == HIGH) {
int currentStateB = digitalRead(PIN_ROTARY_DT);
// If B is HIGH, we are turning clockwise
if (currentStateB == HIGH) {
return 1; // Clockwise
} else {
return -1; // Counterclockwise
}
}
}
lastStateA = currentStateA; // Update the last state
return 0; // No change
}
// Function to draw aligned text on the display
void drawAlignedText(const char* text, int xCenter, int yCenter, int textSize, bool centerVertical) {
display.setTextSize(textSize); // Set the text size
// Calculate the width and height of the text
int textWidth = strlen(text) * 6 * textSize; // 6 is the pixel width of each character at size 1
int textHeight = 8 * textSize; // Height of each character at size 1
// Calculate the x and y positions for horizontal and vertical centering
int xPos = xCenter - (textWidth / 2);
int yPos = centerVertical ? (yCenter - (textHeight / 2)) : yCenter;
// Set cursor and print the text
display.setCursor(xPos, yPos);
display.println(text); // Print the text
display.display(); // Update the display
}
void setup() {
//DEFINE PIN MODE
//RELAY
pinMode(PIN_RELAY_FAN, OUTPUT);
pinMode(PIN_RELAY_COOLING, OUTPUT);
pinMode(PIN_RELAY_DEFROST, OUTPUT);
//HUMIDITY
pinMode(PIN_HUMIDITY, INPUT);
//ROTARY ENCODER
pinMode(PIN_ROTARY_CLK, INPUT);
pinMode(PIN_ROTARY_DT, INPUT);
pinMode(PIN_ROTARY_SW, INPUT_PULLUP);
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Hello, ESP32!, Trial Thermostat Controller");
Wire.begin(26, 25);
// Initialize the OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 OLED
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Stop here if display initialization fails
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
displayText("HELLO, RIKO;", CENTER);
delay(5000);
display.clearDisplay(); // Clear the display buffer
// Initial display of the main menu
displayMainMenu();
}
void loop() {
int encoderPosition = readEncoder();
// Check if the encoder position has changed
if (encoderPosition != lastEncoderPosition) {
lastEncoderPosition = encoderPosition;
// Update current selection based on encoder rotation
if (encoderPosition == 1) {
currentSelection = (currentSelection + 1) % (menuActive ? mainMenuCount : setupMenuCount); // Move down the menu
} else if (encoderPosition == -1) {
currentSelection = (currentSelection - 1 + (menuActive ? mainMenuCount : setupMenuCount)) % (menuActive ? mainMenuCount : setupMenuCount); // Move up the menu
}
// Refresh the display based on the active menu
if (menuActive) {
displayMainMenu();
} else {
displaySetupMenu();
}
}
// Check if the button is pressed
if (digitalRead(PIN_ROTARY_SW) == LOW) { // Button pressed (active LOW)
delay(200); // Debounce delay
if (menuActive) {
// Handle selection based on the current selection in the main menu
if (currentSelection == 0) {
menuActive = false; // Switch to setup menu
currentSelection = 0; // Reset selection for the setup menu
} else {
// Handle exit logic if needed
display.clearDisplay();
drawAlignedText("Exiting...", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 2, true);
display.display();
delay(2000); // Show exit message for 2 seconds
ESP.restart(); // Restart the ESP32
}
} else {
// Handle selection based on the current selection in the setup menu
if (currentSelection < setupMenuCount - 1) { // If not "[Back]"
display.clearDisplay();
drawAlignedText(setupMenuOptions[currentSelection], SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 2, true);
delay(2000); // Show selected option for 2 seconds
display.clearDisplay();
currentSelection = 0; // Reset selection for the setup menu
} else {
menuActive = true; // Switch back to main menu
currentSelection = 0; // Reset selection for the main menu
}
}
}
}