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.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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 12
  10. #define PIN_BTN_MODE 11
  11. #define PIN_BTN_MINUS 10
  12. #define PIN_BTN_PLUS 9
  13. #define PIN_LCD_LED 8
  14. #define PIN_LCD_RS 7
  15. #define PIN_LCD_ENABLE 6
  16. #define PIN_LCD_D0 5
  17. #define PIN_LCD_D1 4
  18. #define PIN_LCD_D2 3
  19. #define PIN_LCD_D3 2
  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_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3}
  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. allButtonsPressed = allButtonsPressed && g_button->isPressed();
  88. }
  89. if (!allButtonsPressed)
  90. {
  91. LOG(5, "%s: Loading settings", __FUNCTION__);
  92. m_storage.load(*m_appCoreState);
  93. }
  94. else
  95. {
  96. LOG(5, "%s: Resetting settings", __FUNCTION__);
  97. m_storage.save(*m_appCoreState);
  98. }
  99. LOG_FN_END(1);
  100. }
  101. void AppCore::loop()
  102. {
  103. LOG_FN_BEGIN(50);
  104. const auto& currentMs = millis();
  105. if (currentMs - m_appCoreState->appState.lastSensorRequestMs >= SENSORS_CHECK_INTERVAL)
  106. {
  107. m_appCoreState->appState.lastSensorRequestMs = currentMs;
  108. m_appCoreState->appState.hasReadSensors = false;
  109. m_sensors.requestTemperaturesByAddress(m_sensor1);
  110. m_sensors.requestTemperaturesByAddress(m_sensor2);
  111. }
  112. if (currentMs - m_appCoreState->appState.lastSensorRequestMs >= SENSORS_REQUEST_DELAY &&
  113. !m_appCoreState->appState.hasReadSensors)
  114. {
  115. m_appCoreState->appState.hasReadSensors = true;
  116. readAndUpdateSensors(&m_appCoreState->appState.water, m_sensor1);
  117. readAndUpdateSensors(&m_appCoreState->appState.heater, m_sensor2);
  118. checkBoilerItem(&m_appCoreState->appState.water);
  119. checkBoilerItem(&m_appCoreState->appState.heater);
  120. }
  121. for (auto& pButton : m_buttons)
  122. {
  123. pButton->read();
  124. if (pButton->isPressed())
  125. {
  126. m_appCoreState->uiState.lastOpMs = currentMs;
  127. break;
  128. }
  129. }
  130. if (m_appCoreState->uiState.state == Hibernate)
  131. {
  132. for (auto& pButton : m_buttons)
  133. {
  134. if (pButton->wasReleased())
  135. {
  136. setState(Lighting);
  137. break;
  138. }
  139. }
  140. }
  141. else
  142. {
  143. if (currentMs - m_appCoreState->uiState.lastOpMs >= HIBERNATE_DELAY)
  144. {
  145. setState(Hibernate);
  146. }
  147. else
  148. {
  149. if (m_btnMode.wasReleased())
  150. {
  151. m_appCoreState->uiState.modeSequenceIndex =
  152. (m_appCoreState->uiState.modeSequenceIndex + 1) % MODE_SEQUENCE_COUNT;
  153. setState(m_modeSequence[m_appCoreState->uiState.modeSequenceIndex]);
  154. }
  155. else if (m_btnMinus.wasReleased() || m_btnPlus.wasReleased())
  156. {
  157. BoilerItemState* itemState = nullptr;
  158. if (m_appCoreState->uiState.state == WaterSetting)
  159. {
  160. itemState = &m_appCoreState->appState.water;
  161. }
  162. else if (m_appCoreState->uiState.state == HeaterSetting)
  163. {
  164. itemState = &m_appCoreState->appState.heater;
  165. }
  166. if (itemState)
  167. {
  168. if (m_btnMinus.wasReleased())
  169. {
  170. itemState->setting -= TEMP_INTERVAL;
  171. }
  172. else if (m_btnPlus.wasReleased())
  173. {
  174. itemState->setting += TEMP_INTERVAL;
  175. }
  176. LOG(1, "Setting temp to %i (%i)", itemState->setting, TEMP_INTERVAL);
  177. m_appCoreState->uiState.isUpdateNeeded = true;
  178. }
  179. }
  180. }
  181. }
  182. if (m_appCoreState->uiState.isUpdateNeeded)
  183. {
  184. printState();
  185. }
  186. LOG_FN_END(50);
  187. }
  188. void AppCore::setState(
  189. UiStateEnum state
  190. )
  191. {
  192. LOG_FN_BEGIN(1);
  193. LOG(5, "Changing state %i => %i", m_appCoreState->uiState.state, state);
  194. if (state == Lighting)
  195. {
  196. digitalWrite(PIN_LCD_LED, 1);
  197. }
  198. else if (state == Hibernate)
  199. {
  200. digitalWrite(PIN_LCD_LED, 0);
  201. m_appCoreState->uiState.modeSequenceIndex = MODE_SEQUENCE_COUNT - 1;
  202. m_storage.save(*m_appCoreState);
  203. }
  204. m_appCoreState->uiState.state = state;
  205. m_appCoreState->uiState.isUpdateNeeded = true;
  206. LOG_FN_END(1);
  207. }
  208. void AppCore::checkBoilerItem(
  209. BoilerItemState* boilerItemState
  210. )
  211. {
  212. LOG_FN_BEGIN(2);
  213. if (!boilerItemState->isActive && boilerItemState->current != TEMP_T_INVALID &&
  214. boilerItemState->current <= boilerItemState->setting - TEMP_TRIGGER)
  215. {
  216. boilerItemState->isActive = true;
  217. m_appCoreState->uiState.isUpdateNeeded = true;
  218. }
  219. else if (boilerItemState->isActive &&
  220. (boilerItemState->current == TEMP_T_INVALID || boilerItemState->current >= boilerItemState->setting))
  221. {
  222. boilerItemState->isActive = false;
  223. m_appCoreState->uiState.isUpdateNeeded = true;
  224. }
  225. LOG_FN_END(2);
  226. }
  227. void AppCore::readAndUpdateSensors(
  228. BoilerItemState* boilerItemState
  229. , const uint8_t* sensor
  230. )
  231. {
  232. LOG_FN_BEGIN(2);
  233. auto raw = m_sensors.getTempC(sensor);
  234. temp_t temp = TEMP_T_INVALID;
  235. if (raw != DEVICE_DISCONNECTED_C)
  236. {
  237. temp = (temp_t) (raw * 10);
  238. }
  239. if (temp != boilerItemState->current)
  240. {
  241. boilerItemState->current = temp;
  242. m_appCoreState->uiState.isUpdateNeeded = true;
  243. }
  244. LOG_FN_END(2);
  245. }
  246. void AppCore::printState()
  247. {
  248. LOG_FN_BEGIN(2);
  249. m_lcd.setCursor(0, 0);
  250. printStateLine('S', &m_appCoreState->appState.water, m_appCoreState->uiState.state == WaterSetting,
  251. m_appCoreState->appState.water.isActive);
  252. m_lcd.setCursor(0, 1);
  253. printStateLine('C', &m_appCoreState->appState.heater, m_appCoreState->uiState.state == HeaterSetting,
  254. m_appCoreState->appState.heater.isActive);
  255. m_appCoreState->uiState.isUpdateNeeded = false;
  256. LOG_FN_END(2);
  257. }
  258. void
  259. AppCore::printStateLine(
  260. char prefix
  261. , const BoilerItemState* boilerItemState
  262. , bool isModifying
  263. , bool isActive
  264. )
  265. {
  266. LOG_FN_BEGIN(2);
  267. char curTmp[7], setTmp[7], tmp[17];
  268. tempToStr(curTmp, boilerItemState->current, 5);
  269. tempToStr(setTmp, boilerItemState->setting, 4);
  270. int count = snprintf(tmp, sizeof(tmp), "%c:%s [%s]%c%c", prefix, curTmp, setTmp, isModifying ? '<' : ' ',
  271. isActive ? LCD_CHAR_SENSOR : ' ');
  272. for (; count < 17; ++count)
  273. {
  274. tmp[count] = ' ';
  275. }
  276. tmp[count] = 0;
  277. m_lcd.print(tmp);
  278. LOG_FN_END(2);
  279. }
  280. void AppCore::tempToStr(
  281. char* out
  282. , temp_t temp
  283. , signed char width
  284. )
  285. {
  286. LOG_FN_BEGIN(2);
  287. LOG(5, "%s: temp=%i", __FUNCTION__, (int)temp);
  288. if (temp == TEMP_T_INVALID)
  289. {
  290. strcpy(out, " --.-");
  291. }
  292. else
  293. {
  294. dtostrf(temp / 10.0f, width, 1, out);
  295. }
  296. LOG_FN_END(2);
  297. }