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 9.3KB

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