hms11

ProgrammableRelayBoard.03

May 29th, 2025
23
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.89 KB | None | 0 0
  1. #include <Wire.h>
  2. #include <Adafruit_GFX.h>
  3. #include <Adafruit_SSD1306.h>
  4.  
  5. // === OLED ===
  6. #define SCREEN_WIDTH 128
  7. #define SCREEN_HEIGHT 64
  8. #define OLED_RESET -1
  9. #define OLED_ADDR 0x3C
  10. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
  11.  
  12. // === Input Pins ===
  13. const uint8_t INPUT_PINS[4] = {7, 6, 5, 4};
  14. const uint8_t ENCODER_PIN_A = 2;
  15. const uint8_t ENCODER_PIN_B = 3;
  16. const uint8_t ENCODER_BUTTON = 8;
  17.  
  18. // === Analog Inputs ===
  19. const uint8_t SENSOR_PIN = A1;
  20. const uint8_t BATTERY_PIN = A0;
  21.  
  22. // === Relay Outputs ===
  23. const uint8_t RELAY_PINS[4] = {10, 11, 12, 13};
  24.  
  25. // === Rotary Encoder State ===
  26. volatile int encoderPos = 0;
  27. int lastReportedPos = 0;
  28.  
  29. // === Menu Navigation State ===
  30. #define MAX_MENU_DEPTH 4
  31.  
  32. struct MenuNode {
  33. const char* label;
  34. void (*action)();
  35. MenuNode* parent;
  36. MenuNode** children;
  37. uint8_t childCount;
  38. };
  39.  
  40. MenuNode* currentMenu = nullptr;
  41. uint8_t currentIndex = 0;
  42.  
  43. MenuNode* menuStack[MAX_MENU_DEPTH];
  44. uint8_t indexStack[MAX_MENU_DEPTH];
  45. uint8_t menuDepth = 0;
  46.  
  47. MenuNode backNode = {"\u2190 Back", nullptr, nullptr, nullptr, 0};
  48.  
  49. // === Encoder ISR ===
  50. void encoderISR() {
  51. static bool lastA = LOW;
  52. bool A = digitalRead(ENCODER_PIN_A);
  53. bool B = digitalRead(ENCODER_PIN_B);
  54.  
  55. if (A != lastA && A == HIGH) {
  56. if (B == LOW) encoderPos++;
  57. else encoderPos--;
  58. }
  59. lastA = A;
  60. }
  61.  
  62. void renderMenu() {
  63. display.clearDisplay();
  64. display.setTextSize(1);
  65. display.setTextColor(SSD1306_WHITE);
  66. for (uint8_t i = 0; i < currentMenu->childCount; i++) {
  67. int y = i * 8;
  68. display.setCursor(0, y);
  69. if (i == currentIndex) display.print("> ");
  70. else display.print(" ");
  71.  
  72. char label[18];
  73. strncpy(label, currentMenu->children[i]->label, 16);
  74. label[16] = '\0';
  75. display.println(label);
  76. }
  77. display.display();
  78. }
  79.  
  80. void enterMenu(MenuNode* menu) {
  81. if (menuDepth < MAX_MENU_DEPTH - 1) {
  82. menuStack[menuDepth] = currentMenu;
  83. indexStack[menuDepth] = currentIndex;
  84. menuDepth++;
  85.  
  86. static MenuNode* augmentedChildren[8];
  87. for (uint8_t i = 0; i < menu->childCount; i++) {
  88. augmentedChildren[i] = menu->children[i];
  89. }
  90. backNode.parent = menu->parent;
  91. augmentedChildren[menu->childCount] = &backNode;
  92.  
  93. currentMenu = menu;
  94. currentMenu->children = augmentedChildren;
  95. currentMenu->childCount += 1;
  96. currentIndex = 0;
  97. renderMenu();
  98. }
  99. }
  100.  
  101. void handleEncoder() {
  102. static int lastPos = 0;
  103. if (encoderPos != lastPos) {
  104. int delta = encoderPos - lastPos;
  105. lastPos = encoderPos;
  106. currentIndex = (currentIndex + delta + currentMenu->childCount) % currentMenu->childCount;
  107. renderMenu();
  108. }
  109.  
  110. static bool lastButtonState = HIGH;
  111. bool buttonState = digitalRead(ENCODER_BUTTON);
  112. if (buttonState == LOW && lastButtonState == HIGH) {
  113. delay(50); // Debounce
  114. MenuNode* selected = currentMenu->children[currentIndex];
  115. if (selected == &backNode) {
  116. if (menuDepth > 0) {
  117. menuDepth--;
  118. currentMenu = menuStack[menuDepth];
  119. currentIndex = indexStack[menuDepth];
  120. renderMenu();
  121. }
  122. } else if (selected->childCount > 0) {
  123. enterMenu(selected);
  124. } else if (selected->action) {
  125. selected->action();
  126. renderMenu();
  127. }
  128. }
  129. lastButtonState = buttonState;
  130. }
  131.  
  132. void showStatus() {
  133. Serial.println("Status screen");
  134. }
  135.  
  136. void saveSettings() {
  137. Serial.println("Settings saved (placeholder)");
  138. }
  139.  
  140. // === Menu Structure ===
  141. MenuNode* inputItems[] = {
  142. new MenuNode{"Input 1 -> Relay Map", nullptr, nullptr, nullptr, 0},
  143. new MenuNode{"Input 2 -> Relay Map", nullptr, nullptr, nullptr, 0},
  144. new MenuNode{"Input 3 -> Relay Map", nullptr, nullptr, nullptr, 0},
  145. new MenuNode{"Input 4 -> Relay Map", nullptr, nullptr, nullptr, 0}
  146. };
  147. MenuNode inputConfig = {"Input Config", nullptr, nullptr, inputItems, 4};
  148.  
  149. MenuNode* rootItems[] = {
  150. new MenuNode{"View Status", showStatus, nullptr, nullptr, 0},
  151. &inputConfig,
  152. new MenuNode{"Save Settings", saveSettings, nullptr, nullptr, 0}
  153. };
  154. MenuNode root = {"Main Menu", nullptr, nullptr, rootItems, 3};
  155.  
  156. void setup() {
  157. Serial.begin(9600);
  158. for (uint8_t i = 0; i < 4; i++) {
  159. pinMode(INPUT_PINS[i], INPUT_PULLUP);
  160. pinMode(RELAY_PINS[i], OUTPUT);
  161. digitalWrite(RELAY_PINS[i], LOW);
  162. }
  163.  
  164. pinMode(ENCODER_PIN_A, INPUT_PULLUP);
  165. pinMode(ENCODER_PIN_B, INPUT_PULLUP);
  166. pinMode(ENCODER_BUTTON, INPUT);
  167.  
  168. if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
  169. Serial.println(F("OLED init failed"));
  170. while (true);
  171. }
  172. display.clearDisplay();
  173. display.setTextSize(1);
  174. display.setTextColor(SSD1306_WHITE);
  175. display.setCursor(0, 0);
  176. display.println(F("System Booting..."));
  177. display.display();
  178. delay(1000);
  179.  
  180. currentMenu = &root;
  181. renderMenu();
  182.  
  183. attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), encoderISR, CHANGE);
  184. }
  185.  
  186. void loop() {
  187. handleEncoder();
  188. }
  189.  
Advertisement
Add Comment
Please, Sign In to add comment