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.

AppCore.cpp 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include "AppCore.h"
  2. #define HIBERNATE_DELAY 5000
  3. #define SENSORS_CHECK_INTERVAL 2000
  4. #define SENSORS_REQUEST_DELAY 750
  5. #define TEMP_TRIGGER 10
  6. #define LCD_CHAR_SENSOR 1
  7. #define PIN_ONEWIRE 12
  8. #define PIN_BTN_MODE 11
  9. #define PIN_BTN_MINUS 10
  10. #define PIN_BTN_PLUS 9
  11. #define PIN_LCD_LED 8
  12. #define PIN_LCD_RS 7
  13. #define PIN_LCD_ENABLE 6
  14. #define PIN_LCD_D0 5
  15. #define PIN_LCD_D1 4
  16. #define PIN_LCD_D2 3
  17. #define PIN_LCD_D3 2
  18. #define BUTTONS_COUNT (sizeof(g_buttons) / sizeof(*g_buttons))
  19. #define MODE_SEQUENCE_COUNT (sizeof(m_modeSequence) / sizeof(*m_modeSequence))
  20. AppCore::AppCore()
  21. : m_appCoreState{
  22. .appState = {
  23. .lastSensorRequestMs = 0,
  24. .hasReadSensors = true,
  25. .water = {
  26. .current = TEMP_T_INVALID,
  27. .setting = 0,
  28. .isActive = false
  29. },
  30. .heater = {
  31. .current = TEMP_T_INVALID,
  32. .setting = 0,
  33. .isActive = false
  34. }
  35. },
  36. .uiState = {
  37. .state = Lighting,
  38. .lastOpMs = 0,
  39. .modeSequenceIndex = MODE_SEQUENCE_COUNT - 1,
  40. .isUpdateNeeded = true
  41. }
  42. }
  43. , m_modeSequence{WaterSetting, HeaterSetting, Lighting}
  44. , m_btnMode{PIN_BTN_MODE}
  45. , m_btnMinus{PIN_BTN_MINUS}
  46. , m_btnPlus{PIN_BTN_PLUS}
  47. , m_buttons{&m_btnMode, &m_btnMinus, &m_btnPlus}
  48. , m_lcd{PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3}
  49. , m_oneWire{PIN_ONEWIRE}
  50. , m_sensors{&m_oneWire}
  51. , m_sensor1{0}
  52. {
  53. }
  54. void AppCore::setup()
  55. {
  56. m_sensors.begin();
  57. m_sensors.getAddress(m_sensor1, 0);
  58. m_sensors.setWaitForConversion(false);
  59. for (auto& g_button : m_buttons)
  60. {
  61. g_button->begin();
  62. }
  63. pinMode(PIN_LCD_LED, OUTPUT);
  64. digitalWrite(PIN_LCD_LED, 1);
  65. m_lcd.begin(16, 2);
  66. byte sensorChar[8] = {
  67. B00100,
  68. B01110,
  69. B01110,
  70. B01110,
  71. B01110,
  72. B11111,
  73. B11111,
  74. B01110,
  75. };
  76. m_lcd.createChar(LCD_CHAR_SENSOR, sensorChar);
  77. Serial.begin(9600);
  78. bool allButtonsPressed = true;
  79. for (auto& g_button : m_buttons)
  80. {
  81. allButtonsPressed = allButtonsPressed && g_button->isPressed();
  82. }
  83. if (!allButtonsPressed)
  84. {
  85. m_storage.load(m_appCoreState);
  86. }
  87. else
  88. {
  89. m_storage.save(m_appCoreState);
  90. }
  91. }
  92. void AppCore::loop()
  93. {
  94. const auto& currentMs = millis();
  95. if (currentMs - m_appCoreState.appState.lastSensorRequestMs >= SENSORS_CHECK_INTERVAL)
  96. {
  97. m_appCoreState.appState.lastSensorRequestMs = currentMs;
  98. m_appCoreState.appState.hasReadSensors = false;
  99. m_sensors.requestTemperaturesByAddress(m_sensor1);
  100. }
  101. if (currentMs - m_appCoreState.appState.lastSensorRequestMs >= SENSORS_REQUEST_DELAY &&
  102. !m_appCoreState.appState.hasReadSensors)
  103. {
  104. m_appCoreState.appState.hasReadSensors = true;
  105. auto raw = m_sensors.getTempC(m_sensor1);
  106. temp_t temp = TEMP_T_INVALID;
  107. if (raw != DEVICE_DISCONNECTED_C)
  108. {
  109. temp = (temp_t) (raw * 2);
  110. }
  111. if (temp != m_appCoreState.appState.water.current)
  112. {
  113. m_appCoreState.appState.water.current = temp;
  114. m_appCoreState.uiState.isUpdateNeeded = true;
  115. }
  116. }
  117. for (auto& pButton : m_buttons)
  118. {
  119. pButton->read();
  120. if (pButton->isPressed())
  121. {
  122. m_appCoreState.uiState.lastOpMs = currentMs;
  123. break;
  124. }
  125. }
  126. if (m_appCoreState.uiState.state == Hibernate)
  127. {
  128. for (auto& pButton : m_buttons)
  129. {
  130. if (pButton->wasReleased())
  131. {
  132. setState(Lighting);
  133. break;
  134. }
  135. }
  136. }
  137. else
  138. {
  139. if (currentMs - m_appCoreState.uiState.lastOpMs >= HIBERNATE_DELAY)
  140. {
  141. setState(Hibernate);
  142. }
  143. else
  144. {
  145. if (m_btnMode.wasReleased())
  146. {
  147. m_appCoreState.uiState.modeSequenceIndex =
  148. (m_appCoreState.uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
  149. setState(m_modeSequence[m_appCoreState.uiState.modeSequenceIndex]);
  150. }
  151. else if (m_btnMinus.wasReleased() || m_btnPlus.wasReleased())
  152. {
  153. BoilerItemState* itemState = nullptr;
  154. if (m_appCoreState.uiState.state == WaterSetting)
  155. {
  156. itemState = &m_appCoreState.appState.water;
  157. }
  158. else if (m_appCoreState.uiState.state == HeaterSetting)
  159. {
  160. itemState = &m_appCoreState.appState.heater;
  161. }
  162. if (itemState)
  163. {
  164. if (m_btnMinus.wasReleased())
  165. {
  166. --itemState->setting;
  167. }
  168. else if (m_btnPlus.wasReleased())
  169. {
  170. ++itemState->setting;
  171. }
  172. m_appCoreState.uiState.isUpdateNeeded = true;
  173. }
  174. }
  175. }
  176. }
  177. checkBoilerItem(m_appCoreState.appState.water);
  178. checkBoilerItem(m_appCoreState.appState.heater);
  179. if (m_appCoreState.uiState.isUpdateNeeded)
  180. {
  181. printState();
  182. m_appCoreState.uiState.isUpdateNeeded = false;
  183. }
  184. }
  185. void AppCore::setState(
  186. UiStateEnum state
  187. )
  188. {
  189. char tmp[50];
  190. snprintf(tmp, sizeof(tmp), "Changing state %i => %i", m_appCoreState.uiState.state, state);
  191. Serial.println(tmp);
  192. if (state == Lighting)
  193. {
  194. digitalWrite(PIN_LCD_LED, 1);
  195. }
  196. else if (state == Hibernate)
  197. {
  198. digitalWrite(PIN_LCD_LED, 0);
  199. m_appCoreState.uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
  200. m_storage.save(m_appCoreState);
  201. }
  202. m_appCoreState.uiState.state = state;
  203. m_appCoreState.uiState.isUpdateNeeded = true;
  204. }
  205. void AppCore::checkBoilerItem(
  206. BoilerItemState& boilerItemState
  207. )
  208. {
  209. if (!boilerItemState.isActive && boilerItemState.current != TEMP_T_INVALID &&
  210. boilerItemState.current <= boilerItemState.setting - TEMP_TRIGGER)
  211. {
  212. boilerItemState.isActive = true;
  213. m_appCoreState.uiState.isUpdateNeeded = true;
  214. }
  215. else if (boilerItemState.isActive &&
  216. (boilerItemState.current == TEMP_T_INVALID || boilerItemState.current >= boilerItemState.setting))
  217. {
  218. boilerItemState.isActive = false;
  219. m_appCoreState.uiState.isUpdateNeeded = true;
  220. }
  221. }
  222. void AppCore::printState()
  223. {
  224. Serial.println("Updating display");
  225. m_lcd.setCursor(0, 0);
  226. printStateLine('S', m_appCoreState.appState.water, m_appCoreState.uiState.state == WaterSetting,
  227. m_appCoreState.appState.water.isActive);
  228. m_lcd.setCursor(0, 1);
  229. printStateLine('C', m_appCoreState.appState.heater, m_appCoreState.uiState.state == HeaterSetting,
  230. m_appCoreState.appState.heater.isActive);
  231. }
  232. void
  233. AppCore::printStateLine(
  234. char prefix
  235. , const BoilerItemState& boilerItemState
  236. , bool isModifying
  237. , bool isActive
  238. )
  239. {
  240. char curTmp[7], setTmp[7], tmp[17];
  241. tempToStr(curTmp, boilerItemState.current);
  242. tempToStr(setTmp, boilerItemState.setting);
  243. int count = snprintf(tmp, sizeof(tmp), "%c: %s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ',
  244. isActive ? LCD_CHAR_SENSOR : ' ');
  245. for (; count < 17; ++count)
  246. {
  247. tmp[count] = ' ';
  248. }
  249. tmp[count] = 0;
  250. m_lcd.print(tmp);
  251. }
  252. void AppCore::tempToStr(
  253. char* out
  254. , temp_t temp
  255. )
  256. {
  257. if (temp == TEMP_T_INVALID)
  258. {
  259. strcpy(out, " -- ");
  260. }
  261. else
  262. {
  263. char tmp[7];
  264. dtostrf(temp / 2.0f, 2, 1, tmp);
  265. auto len = 4 - strlen(tmp);
  266. for (auto i = 0; i < len; ++i)
  267. {
  268. out[i] = ' ';
  269. }
  270. strcpy(&out[len], tmp);
  271. }
  272. }