You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.ino 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #include <LiquidCrystal.h>
  2. #include <JC_Button.h>
  3. #include "Boiler.h"
  4. #define HIBERNATE_DELAY 5000
  5. #define TEMP_TRIGGER 10
  6. #define LCD_CHAR_SENSOR 1
  7. #define PIN_BTN_MODE 11
  8. #define PIN_BTN_MINUS 10
  9. #define PIN_BTN_PLUS 9
  10. #define PIN_LCD_LED 8
  11. #define PIN_LCD_RS 7
  12. #define PIN_LCD_ENABLE 6
  13. #define PIN_LCD_D0 5
  14. #define PIN_LCD_D1 4
  15. #define PIN_LCD_D2 3
  16. #define PIN_LCD_D3 2
  17. byte g_sensorChar[8] = {
  18. B00100,
  19. B01110,
  20. B01110,
  21. B01110,
  22. B01110,
  23. B11111,
  24. B11111,
  25. B01110,
  26. };
  27. Button g_btnMode(PIN_BTN_MODE);
  28. Button g_btnMinus(PIN_BTN_MINUS);
  29. Button g_btnPlus(PIN_BTN_PLUS);
  30. Button* g_buttons[3] = {&g_btnMode, &g_btnMinus, &g_btnPlus};
  31. #define BUTTONS_COUNT (sizeof(g_buttons) / sizeof(*g_buttons))
  32. LiquidCrystal g_lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3);
  33. UiStateEnum g_modeSequence[3] = {WaterSetting, HeaterSetting, Lighting};
  34. #define MODE_SEQUENCE_COUNT (sizeof(g_modeSequence) / sizeof(*g_modeSequence))
  35. GlobalState g_globalState = {
  36. .appState = {
  37. .water = {
  38. .current = 44,
  39. .setting = 44,
  40. .isActive = false
  41. },
  42. .heater = {
  43. .current = 44,
  44. .setting = 44,
  45. .isActive = false
  46. }
  47. },
  48. .uiState = {
  49. .state = Lighting,
  50. .lastOpMs = 0,
  51. .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1,
  52. .isUpdateNeeded = true
  53. }
  54. };
  55. void printStateLine(char prefix, const BoilerItemState& boilerItemState, bool isModifying, bool isActive, LiquidCrystal lcd)
  56. {
  57. char curTmp[5], setTmp[5], tmp[17];
  58. dtostrf(boilerItemState.current / 2.0f, 2, 1, curTmp);
  59. dtostrf(boilerItemState.setting / 2.0f, 2, 1, setTmp);
  60. int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ', isActive ? LCD_CHAR_SENSOR : ' ');
  61. for (; count < 17; ++count)
  62. {
  63. tmp[count] = ' ';
  64. }
  65. tmp[count] = 0;
  66. lcd.print(tmp);
  67. }
  68. void printState(const GlobalState& globalState, LiquidCrystal lcd)
  69. {
  70. Serial.println("Updating display");
  71. lcd.setCursor(0, 0);
  72. printStateLine('S', globalState.appState.water, globalState.uiState.state == WaterSetting, globalState.appState.water.isActive, lcd);
  73. lcd.setCursor(0, 1);
  74. printStateLine('C', globalState.appState.heater, globalState.uiState.state == HeaterSetting, globalState.appState.heater.isActive, lcd);
  75. }
  76. void setState(GlobalState& boilerState, UiStateEnum state)
  77. {
  78. char tmp[50];
  79. snprintf(tmp, sizeof(tmp), "Changing state %i => %i", g_globalState.uiState.state, state);
  80. Serial.println(tmp);
  81. if (state == Lighting)
  82. {
  83. digitalWrite(PIN_LCD_LED, 1);
  84. }
  85. else if (state == Hibernate)
  86. {
  87. digitalWrite(PIN_LCD_LED, 0);
  88. g_globalState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
  89. }
  90. g_globalState.uiState.state = state;
  91. g_globalState.uiState.isUpdateNeeded = true;
  92. }
  93. void checkBoilerItem(BoilerItemState& boilerItemState, UiState uiState)
  94. {
  95. if (!boilerItemState.isActive && (boilerItemState.current <= boilerItemState.setting - TEMP_TRIGGER))
  96. {
  97. boilerItemState.isActive = true;
  98. uiState.isUpdateNeeded = true;
  99. }
  100. else if (boilerItemState.isActive && (boilerItemState.current >= boilerItemState.setting))
  101. {
  102. boilerItemState.isActive = false;
  103. uiState.isUpdateNeeded = true;
  104. }
  105. }
  106. void setup()
  107. {
  108. for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
  109. {
  110. g_buttons[i]->begin();
  111. }
  112. pinMode(PIN_LCD_LED, OUTPUT);
  113. digitalWrite(PIN_LCD_LED, 1);
  114. g_lcd.begin(16, 2);
  115. g_lcd.createChar(LCD_CHAR_SENSOR, g_sensorChar);
  116. Serial.begin(9600);
  117. }
  118. void loop()
  119. {
  120. const auto& currentMs = millis();
  121. for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
  122. {
  123. g_buttons[i]->read();
  124. if (g_buttons[i]->isPressed())
  125. {
  126. g_globalState.uiState.lastOpMs = currentMs;
  127. break;
  128. }
  129. }
  130. if (g_globalState.uiState.state == Hibernate)
  131. {
  132. for (unsigned i = 0; i < BUTTONS_COUNT; ++i)
  133. {
  134. if (g_buttons[i]->wasReleased())
  135. {
  136. setState(g_globalState, Lighting);
  137. break;
  138. }
  139. }
  140. }
  141. else
  142. {
  143. if (currentMs - g_globalState.uiState.lastOpMs >= HIBERNATE_DELAY)
  144. {
  145. setState(g_globalState, Hibernate);
  146. }
  147. else
  148. {
  149. if (g_btnMode.wasReleased())
  150. {
  151. g_globalState.uiState.modeSequenceIndex = (g_globalState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
  152. setState(g_globalState, g_modeSequence[g_globalState.uiState.modeSequenceIndex]);
  153. }
  154. else if (g_btnMinus.wasReleased() || g_btnPlus.wasReleased())
  155. {
  156. BoilerItemState* itemState = NULL;
  157. if (g_globalState.uiState.state == WaterSetting)
  158. {
  159. itemState = &g_globalState.appState.water;
  160. }
  161. else if (g_globalState.uiState.state == HeaterSetting)
  162. {
  163. itemState = &g_globalState.appState.heater;
  164. }
  165. if (itemState)
  166. {
  167. if (g_btnMinus.wasReleased())
  168. {
  169. --itemState->setting;
  170. }
  171. else if (g_btnPlus.wasReleased())
  172. {
  173. ++itemState->setting;
  174. }
  175. g_globalState.uiState.isUpdateNeeded = true;
  176. }
  177. }
  178. }
  179. }
  180. checkBoilerItem(g_globalState.appState.water, g_globalState.uiState);
  181. checkBoilerItem(g_globalState.appState.heater, g_globalState.uiState);
  182. if (g_globalState.uiState.isUpdateNeeded)
  183. {
  184. printState(g_globalState, g_lcd);
  185. g_globalState.uiState.isUpdateNeeded = false;
  186. }
  187. }