Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #include <EEPROM.h>
- #define EEPROM_START_ADDR 0 // starting EEPROM address for the map
- #define EEPROM_MAP_SIZE (4 * 4) // 16 bytes (one byte per bool)
- // 4 inputs, each can control 4 relays (true = ON, false = OFF)
- bool inputRelayMap[4][4] = {
- {false, false, false, false},
- {false, false, false, false},
- {false, false, false, false},
- {false, false, false, false}
- };
- // === OLED ===
- #define SCREEN_WIDTH 128
- #define SCREEN_HEIGHT 64
- #define OLED_RESET -1
- #define OLED_ADDR 0x3C
- Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
- volatile int8_t encoderDelta = 0;
- volatile uint8_t lastEncoderState = 0;
- uint8_t editingInputIndex = 0; // Which input we are editing (0-3)
- uint8_t editingRelayIndex = 0; // Which relay (0-3) cursor is on in edit mode
- bool inEditMode = false; // True when in the relay map editing screen
- // === Input Pins ===
- const uint8_t INPUT_PINS[4] = {7, 6, 5, 4};
- const uint8_t ENCODER_PIN_A = 2;
- const uint8_t ENCODER_PIN_B = 3;
- const uint8_t ENCODER_BUTTON = 8;
- // === Analog Inputs ===
- const uint8_t SENSOR_PIN = A1;
- const uint8_t BATTERY_PIN = A0;
- // === Relay Outputs ===
- const uint8_t RELAY_PINS[4] = {10, 11, 12, 13};
- // === Menu Navigation State ===
- #define MAX_MENU_DEPTH 4
- struct MenuNode {
- const char* label;
- void (*action)();
- MenuNode* parent;
- MenuNode** children;
- uint8_t childCount;
- };
- MenuNode* currentMenu = nullptr;
- uint8_t currentIndex = 0;
- MenuNode* menuStack[MAX_MENU_DEPTH];
- uint8_t indexStack[MAX_MENU_DEPTH];
- uint8_t menuDepth = 0;
- void encoderISR() {
- uint8_t state = (digitalRead(ENCODER_PIN_A) << 1) | digitalRead(ENCODER_PIN_B);
- uint8_t transition = (lastEncoderState << 2) | state;
- switch (transition) {
- case 0b0001:
- case 0b0111:
- case 0b1110:
- case 0b1000:
- encoderDelta++;
- break;
- case 0b0010:
- case 0b0100:
- case 0b1101:
- case 0b1011:
- encoderDelta--;
- break;
- default:
- break;
- }
- lastEncoderState = state;
- }
- void loadInputRelayMapFromEEPROM() {
- for (uint8_t input = 0; input < 4; input++) {
- for (uint8_t relay = 0; relay < 4; relay++) {
- uint8_t val = EEPROM.read(EEPROM_START_ADDR + input * 4 + relay);
- inputRelayMap[input][relay] = (val == 1);
- }
- }
- }
- void saveInputRelayMapToEEPROM() {
- for (uint8_t input = 0; input < 4; input++) {
- for (uint8_t relay = 0; relay < 4; relay++) {
- EEPROM.update(EEPROM_START_ADDR + input * 4 + relay, inputRelayMap[input][relay] ? 1 : 0);
- }
- }
- }
- // Show the menu with Back option if applicable
- void renderMenu() {
- display.clearDisplay();
- display.setTextSize(1);
- display.setTextColor(SSD1306_WHITE);
- uint8_t itemCount = currentMenu->childCount;
- bool showBack = (currentMenu->parent != nullptr);
- if (showBack) itemCount++; // add Back option
- for (uint8_t i = 0; i < itemCount; i++) {
- int y = i * 8;
- display.setCursor(0, y);
- if (i == currentIndex) display.print("> ");
- else display.print(" ");
- if (showBack && i == itemCount - 1) {
- display.println("< Back");
- } else {
- char label[18];
- strncpy(label, currentMenu->children[i]->label, 16);
- label[16] = '\0';
- display.println(label);
- }
- }
- display.display();
- }
- void renderRelayMapEditor() {
- display.clearDisplay();
- display.setTextSize(1);
- display.setTextColor(SSD1306_WHITE);
- // Title e.g. "Input 1 Relay Map"
- display.setCursor(0, 0);
- display.print("Input ");
- display.print(editingInputIndex + 1);
- display.println(" Relay Map");
- // Show each relay with ON/OFF status
- for (uint8_t i = 0; i < 4; i++) {
- display.setCursor(0, 16 + i * 10);
- // Cursor indicator
- if (i == editingRelayIndex) display.print("> ");
- else display.print(" ");
- // Show relay number
- display.print("Relay ");
- display.print(i + 1);
- display.print(": ");
- // Show ON/OFF status
- if (inputRelayMap[editingInputIndex][i]) {
- display.println("[X]");
- } else {
- display.println("[ ]");
- }
- // Show Back option
- display.setCursor(0, 60);
- if (editingRelayIndex == 4) display.print("> ");
- else display.print(" ");
- display.println("Back");
- display.display();
- }
- }
- void goBack() {
- if (menuDepth > 0) {
- menuDepth--;
- currentMenu = menuStack[menuDepth];
- currentIndex = indexStack[menuDepth];
- renderMenu();
- }
- }
- void enterMenu(MenuNode* menu) {
- if (menuDepth < MAX_MENU_DEPTH) {
- menuStack[menuDepth] = currentMenu;
- indexStack[menuDepth] = currentIndex;
- menuDepth++;
- currentMenu = menu;
- currentIndex = 0;
- renderMenu();
- }
- }
- // === Menu Structure ===
- void editInputRelayMap0() { editingInputIndex = 0; editingRelayIndex = 0; inEditMode = true; renderRelayMapEditor(); }
- void editInputRelayMap1() { editingInputIndex = 1; editingRelayIndex = 0; inEditMode = true; renderRelayMapEditor(); }
- void editInputRelayMap2() { editingInputIndex = 2; editingRelayIndex = 0; inEditMode = true; renderRelayMapEditor(); }
- void editInputRelayMap3() { editingInputIndex = 3; editingRelayIndex = 0; inEditMode = true; renderRelayMapEditor(); }
- /// 2. Leaf nodes
- MenuNode input1 = { "Input 1 -> Relay Map", editInputRelayMap0, nullptr, nullptr, 0 };
- MenuNode input2 = { "Input 2 -> Relay Map", editInputRelayMap1, nullptr, nullptr, 0 };
- MenuNode input3 = { "Input 3 -> Relay Map", editInputRelayMap2, nullptr, nullptr, 0 };
- MenuNode input4 = { "Input 4 -> Relay Map", editInputRelayMap3, nullptr, nullptr, 0 };
- // 3. Array of pointers
- MenuNode* inputItems[] = { &input1, &input2, &input3, &input4 };
- // 4. Submenu node
- MenuNode inputConfig = { "Input Config", nullptr, nullptr, inputItems, 4 };
- // 5. Other leaf nodes
- MenuNode viewStatus = { "View Status", showStatus, nullptr, nullptr, 0 };
- MenuNode saveSettingsItem = { "Save Settings", saveSettings, nullptr, nullptr, 0 };
- // 6. Root menu items
- MenuNode* rootItems[] = { &viewStatus, &inputConfig, &saveSettingsItem };
- // 7. Root menu node
- MenuNode root = { "Main Menu", nullptr, nullptr, rootItems, 3 };
- void handleEncoder() {
- static unsigned long lastButtonTime = 0;
- static bool buttonHandled = false;
- noInterrupts();
- int8_t delta = encoderDelta;
- encoderDelta = 0;
- interrupts();
- // === Relay Map Editing Mode ===
- if (inEditMode) {
- if (delta != 0) {
- editingRelayIndex = (editingRelayIndex + delta + 5) % 5;
- renderRelayMapEditor();
- }
- if (digitalRead(ENCODER_BUTTON) == LOW) {
- if (!buttonHandled && millis() - lastButtonTime > 200) {
- buttonHandled = true;
- lastButtonTime = millis();
- if (editingRelayIndex == 4) {
- // Back selected
- inEditMode = false;
- currentMenu = &inputConfig;
- currentIndex = editingInputIndex; // Return to same input in menu
- renderMenu();
- } else {
- // Toggle the selected relay
- inputRelayMap[editingInputIndex][editingRelayIndex] =
- !inputRelayMap[editingInputIndex][editingRelayIndex];
- renderRelayMapEditor();
- }
- }
- } else {
- buttonHandled = false;
- }
- return; // Skip normal menu logic while editing
- }
- // === Standard Menu Navigation ===
- if (delta != 0) {
- uint8_t itemCount = currentMenu->childCount + (currentMenu->parent ? 1 : 0);
- currentIndex = (currentIndex + delta + itemCount) % itemCount;
- renderMenu();
- }
- if (digitalRead(ENCODER_BUTTON) == LOW) {
- if (!buttonHandled && millis() - lastButtonTime > 200) {
- buttonHandled = true;
- lastButtonTime = millis();
- uint8_t itemCount = currentMenu->childCount + (currentMenu->parent ? 1 : 0);
- if (currentIndex == itemCount - 1 && currentMenu->parent != nullptr) {
- goBack();
- } else {
- MenuNode* selected = currentMenu->children[currentIndex];
- if (selected->childCount > 0) {
- enterMenu(selected);
- } else if (selected->action) {
- selected->action();
- renderMenu();
- }
- }
- }
- } else {
- buttonHandled = false;
- }
- }
- void showStatus() {
- Serial.println("Status screen");
- }
- void saveSettings() {
- Serial.println("Saving settings to EEPROM...");
- saveInputRelayMapToEEPROM();
- Serial.println("Settings saved.");
- }
- void setupMenuParents() {
- for (uint8_t i = 0; i < inputConfig.childCount; i++) {
- inputConfig.children[i]->parent = &inputConfig;
- }
- inputConfig.parent = &root;
- root.children[1]->parent = &root;
- root.children[1]->children = inputItems;
- root.children[1]->childCount = 4;
- }
- void setup() {
- Serial.begin(9600);
- for (uint8_t i = 0; i < 4; i++) {
- pinMode(INPUT_PINS[i], INPUT_PULLUP);
- pinMode(RELAY_PINS[i], OUTPUT);
- digitalWrite(RELAY_PINS[i], LOW);
- }
- pinMode(ENCODER_PIN_A, INPUT_PULLUP);
- pinMode(ENCODER_PIN_B, INPUT_PULLUP);
- pinMode(ENCODER_BUTTON, INPUT);
- if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
- Serial.println(F("OLED init failed"));
- while (true);
- }
- display.clearDisplay();
- display.setTextSize(1);
- display.setTextColor(SSD1306_WHITE);
- display.setCursor(0, 0);
- display.println(F("System Booting..."));
- display.display();
- delay(1000);
- setupMenuParents();
- // **Load EEPROM settings here:**
- loadInputRelayMapFromEEPROM();
- currentMenu = &root;
- currentIndex = 0;
- menuDepth = 0;
- renderMenu();
- lastEncoderState = (digitalRead(ENCODER_PIN_A) << 1) | digitalRead(ENCODER_PIN_B);
- attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), encoderISR, CHANGE);
- attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), encoderISR, CHANGE);
- }
- void loop() {
- handleEncoder();
- }
Advertisement
Add Comment
Please, Sign In to add comment