heatcontrol.ino 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. #ifdef FIRMWARE_VARIANT_HEATCONTROL
  2. void heatcontrol_switchPump(bool _state) {
  3. if (heatcontrol_out_pump && !_state) {
  4. // pump is running and is now switched off
  5. // -> set heatcontrol_pump_lastRunMillis
  6. heatcontrol_pump_lastRunMillis = millis();
  7. heatcontrol_pump_inForceRunMode = false;
  8. }
  9. heatcontrol_out_pump = _state;
  10. pcf8574.digitalWrite(P6, !_state); // output active low!
  11. char _statName[5];
  12. if (_state) sprintf(_statName, PGMStr_ON);
  13. else sprintf(_statName, PGMStr_OFF);
  14. sprintf_P(logBuf, PGMStr_heatc_pumpSwitchedTo, PGMStr_heatc_logName, _statName);
  15. sendLog(logBuf, LOGLEVEL_INFO);
  16. heatcontrol_publish_out_pump();
  17. }
  18. void heatcontrol_switchHeating(bool _state) {
  19. heatcontrol_out_heat = _state;
  20. pcf8574.digitalWrite(P7, !_state); // output active low!
  21. char _statName[5];
  22. if (_state) sprintf(_statName, PGMStr_ON);
  23. else sprintf(_statName, PGMStr_OFF);
  24. sprintf_P(logBuf, PGMStr_heatc_heatingSwitchedTo, PGMStr_heatc_logName, _statName);
  25. sendLog(logBuf, LOGLEVEL_INFO);
  26. heatcontrol_publish_out_heat();
  27. }
  28. // Pump Backlash
  29. void heatcontrol_pumpBacklash_begin() {
  30. heatcontrol_pumpBacklash_startMillis = millis();
  31. heatcontrol_state_pumpBacklash_active = true;
  32. sprintf_P(logBuf, PGMStr_heatc_pumpBacklashStarted, PGMStr_heatc_logName);
  33. sendLog(logBuf, LOGLEVEL_INFO);
  34. heatcontrol_publish_pumpBacklash();
  35. }
  36. void heatcontrol_pumpBacklash_cancel() {
  37. heatcontrol_pumpBacklash_startMillis = 0;
  38. heatcontrol_state_pumpBacklash_active = false;
  39. heatcontrol_publish_pumpBacklash();
  40. sprintf_P(logBuf, PGMStr_heatc_pumpBacklashCanceled, PGMStr_heatc_logName);
  41. sendLog(logBuf, LOGLEVEL_INFO);
  42. }
  43. void heatcontrol_pumpBacklash_handleTimeout() {
  44. // switch off pump after backlash time has exceeded
  45. if (heatcontrol_pumpBacklash_startMillis > 0) {
  46. unsigned long _pumpBacklashMillis;
  47. if (heatcontrol_pump_inForceRunMode) {
  48. _pumpBacklashMillis = confHeatc.pumpTime_forceRun * 60000;
  49. }
  50. else {
  51. _pumpBacklashMillis = confHeatc.pumpBacklash * 60000;
  52. }
  53. if ((millis() - heatcontrol_pumpBacklash_startMillis) > _pumpBacklashMillis) {
  54. sprintf_P(logBuf, PGMStr_heatc_pumpBacklashFinished, PGMStr_heatc_logName);
  55. sendLog(logBuf, LOGLEVEL_INFO);
  56. heatcontrol_switchPump(false); // pump OFF
  57. heatcontrol_pumpBacklash_startMillis = 0;
  58. heatcontrol_state_pumpBacklash_active = false;
  59. heatcontrol_publish_pumpBacklash();
  60. display_updateImmediately = true;
  61. }
  62. }
  63. }
  64. uint8_t heatcontrol_pumpBacklash_getRemainingMins() {
  65. if(heatcontrol_pumpBacklash_startMillis > 0) {
  66. unsigned long _pumpBacklashMillis = confHeatc.pumpBacklash * 60000;
  67. unsigned long _elapsedMillis = (millis() - heatcontrol_pumpBacklash_startMillis);
  68. unsigned long _remainingMinutes = (_pumpBacklashMillis - _elapsedMillis) / 60000;
  69. return _remainingMinutes + 1;
  70. }
  71. else return 0;
  72. }
  73. void heatcontrol_lockTime_begin() {
  74. if (heatcontrol_skipLocktimeOnce) {
  75. // if last turned on by test mode -> no lock time after that
  76. heatcontrol_skipLocktimeOnce = false;
  77. }
  78. else {
  79. uint8_t _lockTimeMinutes;
  80. pcf8574.digitalWrite(P1, LOW); // output active low!
  81. // get current lock time depending on outside temp
  82. int _tOut = (int)owTemp_out;
  83. if (_tOut <= -20) _lockTimeMinutes = confHeatc.heatLocktime[0];
  84. else if (_tOut <= -15) _lockTimeMinutes = confHeatc.heatLocktime[1];
  85. else if (_tOut <= -10) _lockTimeMinutes = confHeatc.heatLocktime[2];
  86. else if (_tOut <= -5) _lockTimeMinutes = confHeatc.heatLocktime[3];
  87. else if (_tOut <= 0) _lockTimeMinutes = confHeatc.heatLocktime[4];
  88. else if (_tOut <= 5) _lockTimeMinutes = confHeatc.heatLocktime[5];
  89. else if (_tOut <= 10) _lockTimeMinutes = confHeatc.heatLocktime[6];
  90. else if (_tOut <= 15) _lockTimeMinutes = confHeatc.heatLocktime[7];
  91. else if (_tOut <= 20) _lockTimeMinutes = confHeatc.heatLocktime[8];
  92. else if (_tOut > 20) _lockTimeMinutes = confHeatc.heatLocktime[8]; // also use value for <=20 for >20
  93. else _lockTimeMinutes = 0;
  94. #ifdef DEBUGMODE
  95. // i dont like to wait during development
  96. if (_lockTimeMinutes == 0) _lockTimeMinutes = 1;
  97. #endif
  98. heatcontrol_lockTime_startMillis = millis();
  99. heatcontrol_lockTime_totalMillis = _lockTimeMinutes * 60000;
  100. heatcontrol_lastState_lockActive = true;
  101. sprintf_P(logBuf, PGMStr_heatc_locktimeStart, PGMStr_heatc_logName, _lockTimeMinutes);
  102. sendLog(logBuf, LOGLEVEL_INFO);
  103. heatcontrol_publish_lockTime();
  104. heatcontrol_publish_lockActive();
  105. display_updateImmediately = true;
  106. }
  107. }
  108. void heatcontrol_lockTime_handleTimeout() {
  109. // handle lock time
  110. if (heatcontrol_lockTime_startMillis > 0) {
  111. if ((millis() - heatcontrol_lockTime_startMillis) > heatcontrol_lockTime_totalMillis) {
  112. // locktime is over
  113. heatcontrol_lockTime_startMillis = 0;
  114. heatcontrol_lockTime_totalMillis = 0;
  115. heatcontrol_lock_release();
  116. sprintf_P(logBuf, PGMStr_heatc_locktimeStop, PGMStr_heatc_logName);
  117. sendLog(logBuf, LOGLEVEL_INFO);
  118. heatcontrol_publish_lockTime();
  119. heatcontrol_publish_lockActive();
  120. display_updateImmediately = true;
  121. }
  122. }
  123. else {
  124. heatcontrol_lock_release();
  125. }
  126. }
  127. uint8_t heatcontrol_lockTime_getRemainingMins() {
  128. if(heatcontrol_lockTime_startMillis > 0) {
  129. unsigned long _elapsedMillis = (millis() - heatcontrol_lockTime_startMillis);
  130. unsigned long _remainingMinutes = (heatcontrol_lockTime_totalMillis - _elapsedMillis) / 60000;
  131. return _remainingMinutes + 1;
  132. }
  133. else return 0;
  134. }
  135. void heatcontrol_lock_release() {
  136. bool _currStat = heatcontrol_getLockActive();
  137. if(!_currStat) {
  138. pcf8574.digitalWrite(P1, HIGH); // output active low!
  139. if (_currStat != heatcontrol_lastState_lockActive) {
  140. heatcontrol_publish_lockActive();
  141. heatcontrol_lastState_lockActive = _currStat;
  142. }
  143. }
  144. }
  145. bool heatcontrol_getLockActive() {
  146. if (heatcontrol_lockTime_startMillis == 0 && !heatcontrol_lockTemp_active()) return false;
  147. else return true;
  148. }
  149. bool heatcontrol_lockTemp_active() {
  150. int _tFeed = (int)owTemp_feed;
  151. if (!confHeatc.useHeatCurve) return false;
  152. else if (_tFeed <= (heatcontrol_currentHeatCurveValue - confHeatc.hystereseOn)) {
  153. return false;
  154. }
  155. else {
  156. pcf8574.digitalWrite(P1, LOW); // output active low!
  157. // achtung published zu oft
  158. //////if (!heatcontrol_lastState_lockActive) {
  159. ////// heatcontrol_publish_lockActive();
  160. ////// heatcontrol_lastState_lockActive = true;
  161. //////}
  162. return true;
  163. }
  164. }
  165. void heatcontrol_mainFunction() {
  166. // stop testmode after timeout
  167. if (heatcontrol_testmode_startMillis > 0) {
  168. unsigned long _testmodeMillis = HEATCONTROL_TESTMODE_MINUTES * 60000;
  169. if ((millis() - heatcontrol_testmode_startMillis) > _testmodeMillis) {
  170. heatcontrol_testMode_off();
  171. //heatcontrol_testmode_startMillis = 0; // is already done in heatcontrol_testMode_off()
  172. }
  173. }
  174. // really start testmode if it was set
  175. // MUST be separate if and not included above, or it will instantly switch back on after timeout!!
  176. if (heatcontrol_testmode_startMillis > 0) {
  177. if ((millis() - heatcontrol_testmode_startMillis) > 2800 && !heatcontrol_state_testmode_active) {
  178. heatcontrol_testMode_reallyStart();
  179. //heatcontrol_testmode_startMillis = 0; // is already done in heatcontrol_testMode_reallyStart()
  180. }
  181. }
  182. heatcontrol_lockTime_handleTimeout();
  183. int _tOut = (int)owTemp_out;
  184. int _tFeed = (int)owTemp_feed;
  185. //int _tReturn = (int)owTemp_return;
  186. // heat curve - get current value if enabled
  187. if (confHeatc.useHeatCurve) {
  188. if (_tOut <= -20) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[0];
  189. else if (_tOut <= -15) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[1];
  190. else if (_tOut <= -10) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[2];
  191. else if (_tOut <= -5) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[3];
  192. else if (_tOut <= 0) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[4];
  193. else if (_tOut <= 5) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[5];
  194. else if (_tOut <= 10) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[6];
  195. else if (_tOut <= 15) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[7];
  196. else if (_tOut <= 20) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[8];
  197. else if (_tOut > 20) heatcontrol_currentHeatCurveValue = confHeatc.heatCurve[8]; // also use value for <=20 for >20
  198. else heatcontrol_currentHeatCurveValue = 0;
  199. }
  200. else heatcontrol_currentHeatCurveValue = 0;
  201. #ifdef DEBUGMODE
  202. // during development i prefer working in a heated room
  203. // so i need this to be able to do some tests ;-)
  204. if (confHeatc.useHeatCurve && heatcontrol_currentHeatCurveValue == 0) heatcontrol_currentHeatCurveValue = 35;
  205. #endif
  206. // switch heating and pump if requested
  207. // only if currently heating is inactive
  208. if (!heatcontrol_out_heat) {
  209. // check pre conditions
  210. bool _switchOnPreCondition = false;
  211. // no locktime is currently active (and usage of lock time is not disabled)
  212. if (heatcontrol_lockTime_startMillis == 0) _switchOnPreCondition = true;
  213. // overrule precondition to false if outside temp is above limit
  214. if ( _tOut >= confHeatc.disableAboveTOut) _switchOnPreCondition = false;
  215. // overrule precondition if testmode is requested
  216. if (heatcontrol_state_testmode_active) _switchOnPreCondition = true;
  217. if (_switchOnPreCondition) {
  218. // check if any switch-on condition is met
  219. bool _switchOnCondition = false;
  220. // test mode has been activated
  221. if (heatcontrol_state_testmode_active) {
  222. sprintf_P(logBuf, PGMStr_heatc_switchOnCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOnCondition_heatTestmode);
  223. sendLog(logBuf);
  224. _switchOnCondition = true;
  225. }
  226. // heat request, heat curve enabled AND temp_feed has fallen below treshold
  227. else if (heatcontrol_in_heat_request && confHeatc.useHeatCurve && !heatcontrol_lockTemp_active() ) {
  228. sprintf_P(logBuf, PGMStr_heatc_switchOnCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOnCondition_heatRequestHeatcurve);
  229. sendLog(logBuf);
  230. _switchOnCondition = true;
  231. }
  232. // heat request, heat curve disabled
  233. else if (heatcontrol_in_heat_request && !confHeatc.useHeatCurve) {
  234. sprintf_P(logBuf, PGMStr_heatc_switchOnCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOnCondition_heatRequest);
  235. sendLog(logBuf);
  236. _switchOnCondition = true;
  237. }
  238. // heat curve forced / heat request ignored
  239. // and current feed-temperature <= (currentHeatCurveValue - hystereseOn)
  240. else if ( confHeatc.useHeatCurve && confHeatc.forceHeatCurve && confHeatc.forceHeatCurveAlsoForSwitchOn && confHeatc.hystereseOn >= 3 && _tFeed <= (heatcontrol_currentHeatCurveValue - confHeatc.hystereseOn) ) {
  241. sprintf_P(logBuf, PGMStr_heatc_switchOnCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOnCondition_heatCurveForced);
  242. sendLog(logBuf);
  243. _switchOnCondition = true;
  244. }
  245. if(_switchOnCondition) {
  246. if (!heatcontrol_out_pump && !heatcontrol_in_sw_disableControl_heating) heatcontrol_switchPump(true); // pump ON
  247. if (!heatcontrol_out_heat && !heatcontrol_in_sw_disableControl_pump) heatcontrol_switchHeating(true); // heating ON
  248. if (!heatcontrol_in_sw_disableControl_pump) heatcontrol_pumpBacklash_cancel(); // if pump is in backlash - avoid it going off during heating is running
  249. display_updateImmediately = true;
  250. }
  251. }
  252. }
  253. // heating is currently switched on
  254. else if ( heatcontrol_out_heat ) {
  255. // check if any switch-off condition is met
  256. bool _switchOffCondition = false;
  257. // heat request and/or testmode has stopped and
  258. // useHeatCurve and/or at least forceHeatCurve is disabled
  259. // note - if confHeatc.forceHeatCurve == true switching off is ONLY handled when heat curve target is reached!
  260. if ( (!confHeatc.useHeatCurve || (confHeatc.useHeatCurve && !confHeatc.forceHeatCurve) ) && !heatcontrol_in_heat_request && !heatcontrol_state_testmode_active) {
  261. sprintf_P(logBuf, PGMStr_heatc_switchOffCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOffCondition_heatRequest);
  262. sendLog(logBuf);
  263. _switchOffCondition = true;
  264. }
  265. // test mode is NOT active,
  266. // heatcurve is enabled and
  267. // feed temperature exceeds heat curve value ( + confHeatc.hystereseOff)
  268. else if (!heatcontrol_state_testmode_active && confHeatc.useHeatCurve && _tFeed >= (heatcontrol_currentHeatCurveValue + confHeatc.hystereseOff) ) {
  269. sprintf_P(logBuf, PGMStr_heatc_switchOffCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOffCondition_heatCurve);
  270. sendLog(logBuf);
  271. _switchOffCondition = true;
  272. }
  273. // feed temperature exceeds safety limit
  274. else if ((int)owTemp_feed >= confHeatc.tempFeedLimit) {
  275. sprintf_P(logBuf, PGMStr_heatc_switchOffCondition, PGMStr_heatc_logName, PGMStr_heatc_switchOffCondition_tempFeedLimit);
  276. sendLog(logBuf);
  277. _switchOffCondition = true;
  278. }
  279. if (_switchOffCondition) {
  280. // --> switch heating off
  281. // --> switch pump in backlash
  282. heatcontrol_switchHeating(false); // heating OFF
  283. heatcontrol_pumpBacklash_begin(); // pump - enable backlash
  284. heatcontrol_lockTime_begin();
  285. display_updateImmediately = true;
  286. }
  287. }
  288. else {
  289. // just for safety --> switch heating off in all other cases
  290. if(heatcontrol_out_heat) heatcontrol_switchHeating(false); // heating OFF
  291. }
  292. // pump off if in backlash mode after timeout
  293. if (heatcontrol_state_pumpBacklash_active) {
  294. heatcontrol_pumpBacklash_handleTimeout();
  295. }
  296. }
  297. // TEST MODE
  298. void heatcontrol_testMode_toggle() {
  299. if(!heatcontrol_state_testmode_started) {
  300. heatcontrol_testMode_on();
  301. }
  302. else {
  303. heatcontrol_testMode_off();
  304. }
  305. }
  306. void heatcontrol_testMode_on() {
  307. heatcontrol_state_testmode_started = true;
  308. heatcontrol_state_testmode_active = false; // will be set by heatcontrol_testMode_reallyStart()
  309. heatcontrol_testmode_startMillis = millis();
  310. // Status-LED TESTMODE -> ON
  311. heatcontrol_out_stat_testmode = LOW; // LOW = LED ON
  312. pcf8574.digitalWrite(P0, heatcontrol_out_stat_testmode);
  313. heatcontrol_publish_testMode();
  314. char _dispMsg[17];
  315. snprintf_P(_dispMsg, 17, PGMStr_heatc_display_testModeON);
  316. display_showRow2OverlayMessage((char*)_dispMsg);
  317. }
  318. void heatcontrol_testMode_off() {
  319. heatcontrol_state_testmode_started = false;
  320. heatcontrol_state_testmode_active = false;
  321. heatcontrol_testmode_startMillis = 0;
  322. heatcontrol_skipLocktimeOnce = true;
  323. // Status-LED TESTMODE -> OFF
  324. heatcontrol_out_stat_testmode = HIGH; // HIGH = LED OFF
  325. pcf8574.digitalWrite(P0, heatcontrol_out_stat_testmode);
  326. heatcontrol_publish_testMode();
  327. //heatcontrol_switchPump(false);
  328. //heatcontrol_switchHeating(false);
  329. char _dispMsg[17];
  330. snprintf_P(_dispMsg, 17, PGMStr_heatc_display_testModeOFF);
  331. display_showRow2OverlayMessage((char*)_dispMsg);
  332. sprintf_P(logBuf, PGMStr_heatc_testmodeStop, PGMStr_heatc_logName);
  333. sendLog(logBuf);
  334. }
  335. void heatcontrol_testMode_reallyStart() {
  336. heatcontrol_state_testmode_started = true;
  337. heatcontrol_state_testmode_active = true;
  338. heatcontrol_testmode_startMillis = millis();
  339. display_updateImmediately = true;
  340. sprintf_P(logBuf, PGMStr_heatc_testmodeStart, PGMStr_heatc_logName);
  341. sendLog(logBuf);
  342. }
  343. uint8_t heatcontrol_testMode_getRemainingMins() {
  344. if(heatcontrol_testmode_startMillis > 0) {
  345. unsigned long _testmodeMillis = HEATCONTROL_TESTMODE_MINUTES * 60000;
  346. unsigned long _elapsedMillis = (millis() - heatcontrol_testmode_startMillis);
  347. unsigned long _remainingMinutes = (_testmodeMillis - _elapsedMillis) / 60000;
  348. return _remainingMinutes + 1;
  349. }
  350. else return 0;
  351. }
  352. void heatcontrol_pump_forceRunAfterTimeout() {
  353. if (!heatcontrol_pump_inForceRunMode) {
  354. bool _runCondition = false;
  355. #ifdef DEBUGMODE // use minutes rather than hours for testing
  356. // pump has not run since system start and forceRunInterval is exceeded
  357. if (heatcontrol_pump_lastRunMillis == 0 && millis() >= (confHeatc.pump_forceRunInterval * 60000) ) {
  358. _runCondition = true;
  359. }
  360. // pump last run longer ago than forceRunInterval
  361. if (heatcontrol_pump_lastRunMillis > 0 && (millis() - heatcontrol_pump_lastRunMillis) >= (confHeatc.pump_forceRunInterval * 60000) ) {
  362. _runCondition = true;
  363. }
  364. #else // normal operation - interval is set in hours
  365. // pump has not run since system start and forceRunInterval is exceeded
  366. if (heatcontrol_pump_lastRunMillis == 0 && millis() >= (confHeatc.pump_forceRunInterval * 3600000) ) {
  367. _runCondition = true;
  368. }
  369. // pump last run longer ago than forceRunInterval
  370. if (heatcontrol_pump_lastRunMillis > 0 && (millis() - heatcontrol_pump_lastRunMillis) >= (confHeatc.pump_forceRunInterval * 3600000) ) {
  371. _runCondition = true;
  372. }
  373. #endif
  374. bool _runCondition_inTimeWindow = false;
  375. if (_runCondition) {
  376. sprintf_P(logBuf, PGMStr_heatc_pumpForceRun, PGMStr_heatc_logName);
  377. sendLog(logBuf);
  378. #ifdef ENABLE_FEATURE_NTP_TIME
  379. if (confTime.ntpEnable && confHeatc.pump_forceRunTimeBegin != 0 && confHeatc.pump_forceRunTimeBegin != 0)
  380. {
  381. updateTime();
  382. if (lt.tm_year > 70) { // check if time has been synced
  383. // check if we are in the configured time window
  384. if (lt.tm_hour >= confHeatc.pump_forceRunTimeBegin && lt.tm_hour < confHeatc.pump_forceRunTimeBegin) {
  385. _runCondition_inTimeWindow = true;
  386. }
  387. }
  388. else {
  389. // NTP time is not synced so always assume we are in the time window
  390. _runCondition_inTimeWindow = true;
  391. }
  392. }
  393. else {
  394. // NTP is disabled in so always assume we are in the time window
  395. _runCondition_inTimeWindow = true;
  396. }
  397. #else
  398. // NTP is not compiled in so always assume we are in the time window
  399. _runCondition_inTimeWindow = true;
  400. #endif
  401. if (_runCondition_inTimeWindow) {
  402. heatcontrol_pump_inForceRunMode = true;
  403. heatcontrol_switchPump(true);
  404. heatcontrol_pumpBacklash_begin();
  405. }
  406. }
  407. }
  408. }
  409. #endif // FIRMWARE_VARIANT_HEATCONTROL