thermferm/thermferm.c

changeset 665
66fae54fa7ba
parent 661
8c1e7a52e24f
child 666
48cc8868f9f4
equal deleted inserted replaced
664:8adbc76fd122 665:66fae54fa7ba
34 #include "one-wire.h" 34 #include "one-wire.h"
35 #include "futil.h" 35 #include "futil.h"
36 #include "xutil.h" 36 #include "xutil.h"
37 #include "pid.h" 37 #include "pid.h"
38 #include "mqtt.h" 38 #include "mqtt.h"
39 #include "statetbl.h"
39 40
40 41
41 int my_shutdown = FALSE; 42 int my_shutdown = FALSE;
42 int my_reboot = FALSE; 43 int my_reboot = FALSE;
43 static pid_t pgrp, mypid; 44 static pid_t pgrp, mypid;
44 int run_pause = FALSE; 45 int run_pause = FALSE;
45 int run_hold = FALSE; 46 int run_hold = FALSE;
47 int row;
46 48
47 extern int debug; 49 extern int debug;
48 extern sys_config Config; 50 extern sys_config Config;
49 extern int lcdHandle; 51 extern int lcdHandle;
50 extern int slcdHandle; 52 extern int slcdHandle;
1025 fprintf(stdout, "Finished, rc=%d\n", rc); 1027 fprintf(stdout, "Finished, rc=%d\n", rc);
1026 return rc; 1028 return rc;
1027 } 1029 }
1028 1030
1029 1031
1032 void do_unit(units_list *unit, int LCDunit, int *seconds, int *minutes)
1033 {
1034 time_t now;
1035 prof_step *step;
1036 int rc, temp;
1037 int run_seconds, run_minutes, run_hours, tot_minutes;
1038 int current_step, valid_step, time_until_now, previous_fridge_mode;
1039 float previous_target_lo, previous_target_hi;
1040 float LCDair, LCDbeer, LCDspL, LCDspH;
1041 unsigned char LCDstatC, LCDstatH;
1042
1043 unit->mqtt_flag &= ~MQTT_FLAG_DATA;
1044 unit->alarm_flag = 0;
1045
1046 if (unit->air_address) {
1047 rc = device_in(unit->air_address, &temp);
1048 if (rc == DEVPRESENT_YES) {
1049 if (unit->air_temperature != temp) {
1050 unit->mqtt_flag |= MQTT_FLAG_DATA;
1051 }
1052 unit->air_temperature = temp;
1053 unit->air_state = 0;
1054 } else if (rc == DEVPRESENT_ERROR) {
1055 unit->air_state = 1;
1056 } else {
1057 unit->air_state = 2;
1058 }
1059 }
1060
1061 if (unit->beer_address) {
1062 rc = device_in(unit->beer_address, &temp);
1063 if ((rc == DEVPRESENT_NO) && unit->beer_address2) {
1064 /* Read alternative sensor */
1065 rc = device_in(unit->beer_address2, &temp);
1066 }
1067 if (rc == DEVPRESENT_YES) {
1068 if (unit->beer_temperature != temp) {
1069 unit->mqtt_flag |= MQTT_FLAG_DATA;
1070 }
1071 unit->beer_temperature = temp;
1072 unit->beer_state = 0;
1073 } else if (rc == DEVPRESENT_ERROR) {
1074 unit->beer_state = 1;
1075 } else {
1076 unit->beer_state = 2;
1077 }
1078 }
1079
1080 if (unit->chiller_address) {
1081 rc = device_in(unit->chiller_address, &temp);
1082 if (rc == DEVPRESENT_YES) {
1083 if (unit->chiller_temperature != temp) {
1084 unit->mqtt_flag |= MQTT_FLAG_DATA;
1085 }
1086 unit->chiller_temperature = temp;
1087 unit->chiller_state = 0;
1088 } else if (rc == DEVPRESENT_ERROR) {
1089 unit->chiller_state = 1;
1090 } else {
1091 unit->chiller_state = 2;
1092 }
1093 }
1094
1095 /*
1096 * Unit door state, default is closed.
1097 */
1098 if (unit->door_address) {
1099 rc = device_in(unit->door_address, &temp);
1100 if (rc == DEVPRESENT_YES) {
1101 if (temp) {
1102 if (unit->door_state == 0) {
1103 syslog(LOG_NOTICE, "Unit `%s' door closed", unit->alias);
1104 unit->door_state = 1;
1105 unit->mqtt_flag |= MQTT_FLAG_DATA;
1106 }
1107 } else {
1108 if (unit->door_state) {
1109 syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
1110 unit->door_state = 0;
1111 unit->mqtt_flag |= MQTT_FLAG_DATA;
1112 }
1113 /*
1114 * If unit is active and the door is open
1115 */
1116 if (unit->mode != UNITMODE_NONE) {
1117 unit->alarm_flag |= ALARM_FLAG_DOOR;
1118 }
1119 }
1120 } else {
1121 unit->door_state = 1;
1122 }
1123 } else {
1124 unit->door_state = 1;
1125 }
1126
1127 /*
1128 * Unit PSU state
1129 */
1130 if (unit->psu_address) {
1131 rc = device_in(unit->psu_address, &temp);
1132 if (rc == DEVPRESENT_YES) {
1133 if (temp) {
1134 if (unit->psu_state == 0) {
1135 syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->alias);
1136 unit->psu_state = 1;
1137 unit->mqtt_flag |= MQTT_FLAG_DATA;
1138 }
1139 } else {
1140 if (unit->psu_state) {
1141 syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->alias);
1142 unit->psu_state = 0;
1143 unit->mqtt_flag |= MQTT_FLAG_DATA;
1144 }
1145 unit->alarm_flag |= ALARM_FLAG_PSU;
1146 }
1147 } else {
1148 unit->psu_state = 1;
1149 }
1150 } else {
1151 /*
1152 * No state available, assume Ok.
1153 */
1154 unit->psu_state = 1;
1155 }
1156
1157 /*
1158 * Handle profile
1159 */
1160 if ((unit->mode == UNITMODE_PROFILE) && (unit->profile_uuid)) {
1161 /*
1162 * unit->prof_started - start time or 0 if not yet running.
1163 * unit->prof_state - PROFILE_OFF|PROFILE_PAUSE|PROFILE_RUN|PROFILE_DONE
1164 * unit->prof_target - Calculated target temperature.
1165 * unit->prof_paused - Internal pause counter.
1166 * unit->prof_peak_abs - Peak temperature of the beer.
1167 * unit->prof_peak_rel - Peak temperature between beer and fridge.
1168 * unit->prof_primary_done - time when primary fermentation was over the peak.
1169 */
1170
1171 /*
1172 * Safe defaults
1173 */
1174 unit->prof_target_lo = unit->profile_inittemp_lo;
1175 unit->prof_target_hi = unit->profile_inittemp_hi;
1176 unit->prof_fridge_mode = 0;
1177
1178 switch (unit->prof_state) {
1179 case PROFILE_OFF:
1180 unit->prof_percent = 0;
1181 break;
1182 case PROFILE_PAUSE:
1183 /*
1184 * Keep current temperature, measure pause time. For
1185 * temperature fall thru.
1186 */
1187 unit->prof_paused++;
1188 case PROFILE_RUN:
1189 /*
1190 * Calculate current profile step and desired temperature.
1191 * When all steps are done, set state to PROFILE_DONE.
1192 */
1193 previous_target_lo = unit->profile_inittemp_lo;
1194 previous_target_hi = unit->profile_inittemp_hi;
1195 previous_fridge_mode = unit->profile_fridge_mode;
1196 time_until_now = current_step = 0;
1197 now = time(NULL);
1198 run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
1199 run_minutes = run_seconds / 60;
1200 run_hours = run_minutes / 60;
1201 if (debug)
1202 fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);
1203
1204 /*
1205 * Primary fermentation tests
1206 */
1207 if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
1208 unit->prof_peak_abs = unit->beer_temperature / 1000.0;
1209 if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
1210 unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
1211 if (unit->prof_primary_done == 0) {
1212 if (unit->cooler_address) {
1213 /*
1214 * There is a cooler. If the difference between the beer and air temperature
1215 * drops we assume the primary fermentation is done.
1216 */
1217 if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
1218 unit->prof_primary_done = time(NULL);
1219 syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", unit->profile_name);
1220 if (! unit->event_msg)
1221 unit->event_msg = xstrcpy((char *)"Primary peak");
1222 }
1223 } else {
1224 /*
1225 * This method works if the unit has no cooling or if the profile allowed the
1226 * beer temperature to rise freely.
1227 */
1228 if ((unit->beer_temperature / 1000.0) < (unit->prof_peak_abs - 0.5)) {
1229 unit->prof_primary_done = time(NULL);
1230 syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (free rise mode)", unit->profile_name);
1231 if (! unit->event_msg)
1232 unit->event_msg = xstrcpy((char *)"Primary peak");
1233 }
1234 }
1235 }
1236
1237 /*
1238 * See how long this profile will take
1239 */
1240 tot_minutes = 0;
1241 for (step = unit->profile_steps; step; step = step->next) {
1242 tot_minutes += ((step->steptime + step->resttime) * 60);
1243 }
1244 if ((tot_minutes == 0) && unit->profile_totalsteps) {
1245 syslog(LOG_NOTICE, "Profile `%s' steps disappeared", unit->profile_name);
1246 unit->prof_state = PROFILE_OFF;
1247 break;
1248 }
1249
1250 valid_step = FALSE;
1251 for (step = unit->profile_steps; step; step = step->next) {
1252 /*
1253 * step->steptime
1254 * step->resttime
1255 * step->target
1256 */
1257 current_step++;
1258 if ((run_hours >= time_until_now) && (run_hours < (time_until_now + step->steptime + step->resttime))) {
1259 /*
1260 * This is our current step
1261 */
1262 valid_step = TRUE;
1263 if ((run_hours - time_until_now) < step->steptime) {
1264 unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
1265 unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
1266 if (step->fridge_mode > previous_fridge_mode) {
1267 unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
1268 } else if (step->fridge_mode < previous_fridge_mode) {
1269 unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
1270 } else {
1271 unit->prof_fridge_mode = step->fridge_mode;
1272 }
1273 if (debug)
1274 fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
1275 unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
1276 } else {
1277 unit->prof_target_lo = step->target_lo;
1278 unit->prof_target_hi = step->target_hi;
1279 unit->prof_fridge_mode = step->fridge_mode;
1280 }
1281 break;
1282 }
1283 time_until_now += step->steptime + step->resttime;
1284 previous_target_lo = step->target_lo;
1285 previous_target_hi = step->target_hi;
1286 previous_fridge_mode = step->fridge_mode;
1287 }
1288
1289 if (valid_step == TRUE) {
1290 unit->prof_percent = (100 * run_minutes) / tot_minutes;
1291 if (((*minutes == 10) || (*minutes == 40)) && (*seconds == 1)) {
1292 syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees",
1293 unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step,
1294 unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
1295 unit->mqtt_flag |= MQTT_FLAG_DATA;
1296 }
1297 } else {
1298 /*
1299 * No more steps to do
1300 */
1301 unit->prof_state = PROFILE_DONE;
1302 unit->prof_percent = 100;
1303 syslog(LOG_NOTICE, "Profile `%s' is done", unit->profile_name);
1304 unit->mqtt_flag |= MQTT_FLAG_DATA;
1305 if (! unit->event_msg)
1306 unit->event_msg = xstrcpy((char *)"Profile finished");
1307 }
1308 break;
1309
1310 case PROFILE_DONE:
1311 /*
1312 * Keep this state, set target temperature to the last step.
1313 */
1314 previous_target_lo = unit->profile_inittemp_lo;
1315 previous_target_hi = unit->profile_inittemp_hi;
1316 previous_fridge_mode = unit->profile_fridge_mode;
1317 for (step = unit->profile_steps; step; step = step->next) {
1318 if ((step->steptime + step->resttime) == 0)
1319 break;
1320 previous_target_lo = step->target_lo;
1321 previous_target_hi = step->target_hi;
1322 previous_fridge_mode = step->fridge_mode;
1323 }
1324 unit->prof_target_lo = previous_target_lo;
1325 unit->prof_target_hi = previous_target_hi;
1326 unit->prof_fridge_mode = previous_fridge_mode;
1327 unit->prof_percent = 100;
1328 break;
1329 } /* switch */
1330 } else {
1331 /*
1332 * Set some sane values
1333 */
1334 unit->prof_target_lo = 19.8;
1335 unit->prof_target_hi = 20.2;
1336 unit->prof_fridge_mode = 0;
1337 }
1338
1339 /*
1340 * Manual switching
1341 */
1342 if (unit->mode == UNITMODE_NONE) {
1343 device_out(unit->heater_address, unit->heater_state);
1344 device_out(unit->cooler_address, unit->cooler_state);
1345 device_out(unit->fan_address, unit->fan_state);
1346 }
1347
1348 /*
1349 * Usage counters
1350 */
1351 if (unit->heater_address && unit->heater_state)
1352 unit->heater_usage++;
1353 if (unit->cooler_address && unit->cooler_state)
1354 unit->cooler_usage++;
1355 if (unit->fan_address && unit->fan_state)
1356 unit->fan_usage++;
1357 if (unit->light_address && unit->light_state)
1358 unit->light_usage++;
1359
1360 /*
1361 * Interior lights
1362 */
1363 if (unit->light_address) {
1364 if (unit->light_timer) {
1365 unit->light_timer--;
1366 }
1367 if (unit->door_state && !unit->light_timer && unit->light_state) {
1368 if (unit->light_wait > 0) {
1369 unit->light_wait--;
1370 } else {
1371 unit->light_state = 0;
1372 syslog(LOG_NOTICE, "Unit `%s' lights On => Off", unit->alias);
1373 unit->mqtt_flag |= MQTT_FLAG_DATA;
1374 }
1375 }
1376 if ((!unit->door_state || unit->light_timer) && !unit->light_state) {
1377 unit->light_wait = unit->light_delay; /* No delay to turn lights on */
1378 unit->light_state = 1;
1379 unit->mqtt_flag |= MQTT_FLAG_DATA;
1380 syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->alias);
1381 }
1382 device_out(unit->light_address, unit->light_state);
1383 }
1384
1385 /*
1386 * Temperature control in this unit
1387 */
1388 if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
1389
1390 /*
1391 * Set both PID's to their input values.
1392 */
1393 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
1394 if (unit->mode == UNITMODE_FRIDGE) {
1395 unit->PID_cool->SetP = unit->fridge_set_hi;
1396 unit->PID_heat->SetP = unit->fridge_set_lo;
1397 unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
1398 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
1399 } else if (unit->mode == UNITMODE_BEER) {
1400 unit->PID_cool->SetP = unit->beer_set_hi;
1401 unit->PID_heat->SetP = unit->beer_set_lo;
1402 unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
1403 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
1404 } else if (unit->mode == UNITMODE_PROFILE) {
1405 double usetemp;
1406 unit->PID_cool->SetP = unit->prof_target_hi;
1407 unit->PID_heat->SetP = unit->prof_target_lo;
1408 /*
1409 * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
1410 */
1411 usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
1412 ((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
1413 unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
1414 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
1415 }
1416
1417 /*
1418 * PID controller compute, simulate 100 mSec loops by running 10 times.
1419 */
1420 for (int i = 0; i < 10; i++) {
1421 UpdatePID(unit->PID_heat);
1422 UpdatePID(unit->PID_cool);
1423 }
1424 /*
1425 * Logging
1426 */
1427 if (unit->heater_address) {
1428 /*
1429 * Prevent extreme heating
1430 */
1431 if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) {
1432 unit->PID_heat->OutP = 0.0;
1433 }
1434 if (*seconds == 60) {
1435 syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
1436 unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP);
1437 }
1438 } else {
1439 unit->PID_heat->OutP = 0.0;
1440 }
1441 if (unit->cooler_address) {
1442 /*
1443 * Prevent extreme cooling
1444 */
1445 if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) < (unit->PID_cool->Input - 8.0))) {
1446 unit->PID_cool->OutP = 0.0;
1447 }
1448 /*
1449 * Prevent cooling if we use a chiller and the chiller temperature is not low enough.
1450 */
1451 if (unit->chiller_address && (unit->chiller_state == 0)) {
1452 if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
1453 unit->PID_cool->OutP = 0.0;
1454 unit->alarm_flag |= ALARM_FLAG_CHILLER;
1455 if (*seconds == 60) {
1456 syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
1457 }
1458 }
1459 }
1460 if (*seconds == 60) {
1461 syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
1462 unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP);
1463 }
1464 } else {
1465 unit->PID_cool->OutP = 0.0;
1466 }
1467
1468 /*
1469 * Deadlock, kill lowest value.
1470 */
1471 if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
1472 if (unit->PID_cool->OutP > unit->PID_heat->OutP)
1473 unit->PID_heat->OutP = 0.0;
1474 else
1475 unit->PID_cool->OutP = 0.0;
1476 }
1477
1478 if (unit->heater_address && ! unit->cooler_state) {
1479 if (unit->PID_heat->OutP >= 50) {
1480 if (unit->heater_wait < unit->heater_delay) {
1481 unit->heater_wait++;
1482 } else {
1483 int power = round(unit->PID_heat->OutP);
1484 if (unit->heater_state != power) {
1485 syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->alias, unit->heater_state, power);
1486 unit->heater_state = power;
1487 if (unit->heater_address) {
1488 unit->mqtt_flag |= MQTT_FLAG_DATA;
1489 }
1490 }
1491 }
1492 } else {
1493 if (unit->heater_wait > 0) {
1494 unit->heater_wait--;
1495 } else {
1496 if (unit->heater_state) {
1497 syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->alias);
1498 unit->heater_state = 0;
1499 if (unit->heater_address) {
1500 unit->mqtt_flag |= MQTT_FLAG_DATA;
1501 }
1502 }
1503 }
1504 }
1505 if (unit->door_state) {
1506 device_out(unit->heater_address, unit->heater_state);
1507 } else {
1508 device_out(unit->heater_address, 0);
1509 }
1510 }
1511
1512 if (unit->cooler_address && ! unit->heater_state) {
1513 if (unit->PID_cool->OutP >= 50) {
1514 if (unit->cooler_wait < unit->cooler_delay) {
1515 unit->cooler_wait++;
1516 } else {
1517 int power = round(unit->PID_cool->OutP);
1518 if (unit->cooler_state != power) {
1519 syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->alias, unit->cooler_state, power);
1520 unit->cooler_state = power;
1521 if (unit->cooler_address) {
1522 unit->mqtt_flag |= MQTT_FLAG_DATA;
1523 }
1524 }
1525 }
1526 } else {
1527 if (unit->cooler_wait > 0) {
1528 unit->cooler_wait--;
1529 } else {
1530 if (unit->cooler_state) {
1531 syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->alias);
1532 unit->cooler_state = 0;
1533 if (unit->cooler_address) {
1534 unit->mqtt_flag |= MQTT_FLAG_DATA;
1535 }
1536 }
1537 }
1538 }
1539 if (unit->door_state) {
1540 device_out(unit->cooler_address, unit->cooler_state);
1541 } else {
1542 device_out(unit->cooler_address, 0);
1543 }
1544 }
1545
1546 /*
1547 * If there is a fan, and the unit door is closed, and the unit should be doing
1548 * something, then turn on the global fan.
1549 * But if there is a chiller, do not turn it on if cooling.
1550 */
1551 if (unit->fan_address) {
1552 if ((unit->door_state) && (unit->cooler_state == 0)) {
1553 if (unit->fan_wait < unit->fan_delay) {
1554 unit->fan_wait++;
1555 } else {
1556 if (! unit->fan_state) {
1557 syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->alias);
1558 unit->fan_state = 100;
1559 if (unit->fan_address) {
1560 unit->mqtt_flag |= MQTT_FLAG_DATA;
1561 }
1562 }
1563 }
1564 } else {
1565 if (unit->fan_wait > 0) {
1566 unit->fan_wait--;
1567 } else {
1568 if (unit->fan_state) {
1569 syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->alias);
1570 unit->fan_state = 0;
1571 if (unit->fan_address) {
1572 unit->mqtt_flag |= MQTT_FLAG_DATA;
1573 }
1574 }
1575 }
1576 }
1577 device_out(unit->fan_address, unit->fan_state);
1578 }
1579
1580 } else {
1581 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
1582 } /* fridge beer or profile mode */
1583
1584 /*
1585 * Now everything is set and done, update the LCD display
1586 */
1587 LCDair = unit->air_temperature / 1000.0;
1588 LCDbeer = unit->beer_temperature / 1000.0;
1589 LCDstatC = LCDstatH = ' ';
1590 if (unit->heater_address) {
1591 if (unit->heater_state)
1592 LCDstatH = '\6';
1593 else
1594 LCDstatH = '\5';
1595 }
1596 if (unit->cooler_address) {
1597 if (unit->cooler_state)
1598 LCDstatC = '\4';
1599 else
1600 LCDstatC = '\3';
1601 }
1602 LCDspH = LCDspL = 0.0;
1603 if (unit->mode == UNITMODE_BEER) {
1604 LCDspH = unit->beer_set_hi;
1605 LCDspL = unit->beer_set_lo;
1606 } else if (unit->mode == UNITMODE_FRIDGE) {
1607 LCDspH = unit->fridge_set_hi;
1608 LCDspL = unit->fridge_set_lo;
1609 } else if (unit->mode == UNITMODE_PROFILE) {
1610 if (unit->prof_state != PROFILE_OFF) {
1611 LCDspL = unit->prof_target_lo;
1612 LCDspH = unit->prof_target_hi;
1613 }
1614 }
1615 if (*seconds == 60) {
1616 unit->mqtt_flag |= MQTT_FLAG_DATA;
1617 }
1618 pthread_mutex_lock(&mutexes[LOCK_LCD]);
1619 /*
1620 * Write 4 rows to the LCD to display the unit state
1621 */
1622 lcd_buf_write(row++, "Unit %d: %s ", LCDunit, UNITMODE[unit->mode]);
1623 lcd_buf_write(row++, "%s ", unit->product_name);
1624 lcd_buf_write(row++, "%c%5.1f\2 A%6.2f\1 ", LCDstatC, LCDspH, LCDair);
1625 lcd_buf_write(row++, "%c%5.1f\2 B%6.2f\1 ", LCDstatH, LCDspL, LCDbeer);
1626 pthread_mutex_unlock(&mutexes[LOCK_LCD]);
1627
1628 /*
1629 * Publish MQTT messages set in flag
1630 */
1631 if (unit->mqtt_flag) {
1632 if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
1633 publishDBirth(unit);
1634 unit->mqtt_flag &= ~MQTT_FLAG_BIRTH;
1635 } else {
1636 publishDData(unit);
1637 unit->mqtt_flag &= ~MQTT_FLAG_DATA;
1638 }
1639 if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
1640 publishDDeath(unit);
1641 unit->mqtt_flag &= ~MQTT_FLAG_DEATH;
1642 }
1643 }
1644
1645 /*
1646 * Handle changed alarms
1647 */
1648 if (unit->alarm_flag != unit->alarm_last) {
1649 syslog(LOG_NOTICE, "Unit `%s' Alarm %d => %d", unit->alias, unit->alarm_last, unit->alarm_flag);
1650 unit->alarm_last = unit->alarm_flag;
1651 }
1652 }
1653
1654
1030 1655
1031 int server(void) 1656 int server(void)
1032 { 1657 {
1033 time_t now, last = (time_t)0, ndata = (time_t)0;; 1658 time_t now, last = (time_t)0, ndata = (time_t)0;;
1034 units_list *unit; 1659 units_list *unit;
1035 prof_step *step; 1660 int rc, run = 1, seconds = 0, minutes = 0, temp;
1036 int row, rc, run = 1, seconds = 0, minutes = 0, temp; 1661 int key;
1037 int run_seconds, run_minutes, run_hours, tot_minutes, key;
1038 struct tm *tm; 1662 struct tm *tm;
1039 long t = 0; 1663 long t = 0;
1040 int current_step, valid_step, time_until_now, previous_fridge_mode;
1041 float previous_target_lo, previous_target_hi;
1042 float LCDair, LCDbeer, LCDspL, LCDspH;
1043 unsigned char LCDstatC, LCDstatH;
1044 int LCDunit; 1664 int LCDunit;
1045 1665
1046 syslog(LOG_NOTICE, "Server process started"); 1666 syslog(LOG_NOTICE, "Server process started");
1047 my_shutdown = my_reboot = FALSE; 1667 my_shutdown = my_reboot = FALSE;
1048 if (lockprog((char *)"thermferm")) { 1668 if (lockprog((char *)"thermferm")) {
1243 } 1863 }
1244 1864
1245 LCDunit = 0; 1865 LCDunit = 0;
1246 for (unit = Config.units; unit; unit = unit->next) { 1866 for (unit = Config.units; unit; unit = unit->next) {
1247 LCDunit++; 1867 LCDunit++;
1248 unit->mqtt_flag &= ~MQTT_FLAG_DATA; 1868 do_unit(unit, LCDunit, &seconds, &minutes);
1249 unit->alarm_flag = 0; 1869
1250
1251 if (unit->air_address) {
1252 rc = device_in(unit->air_address, &temp);
1253 if (rc == DEVPRESENT_YES) {
1254 if (unit->air_temperature != temp) {
1255 unit->mqtt_flag |= MQTT_FLAG_DATA;
1256 }
1257 unit->air_temperature = temp;
1258 unit->air_state = 0;
1259 } else if (rc == DEVPRESENT_ERROR) {
1260 unit->air_state = 1;
1261 } else {
1262 unit->air_state = 2;
1263 }
1264 }
1265
1266 if (unit->beer_address) {
1267 rc = device_in(unit->beer_address, &temp);
1268 if ((rc == DEVPRESENT_NO) && unit->beer_address2) {
1269 /* Read alternative sensor */
1270 rc = device_in(unit->beer_address2, &temp);
1271 }
1272 if (rc == DEVPRESENT_YES) {
1273 if (unit->beer_temperature != temp) {
1274 unit->mqtt_flag |= MQTT_FLAG_DATA;
1275 }
1276 unit->beer_temperature = temp;
1277 unit->beer_state = 0;
1278 } else if (rc == DEVPRESENT_ERROR) {
1279 unit->beer_state = 1;
1280 } else {
1281 unit->beer_state = 2;
1282 }
1283 }
1284
1285 if (unit->chiller_address) {
1286 rc = device_in(unit->chiller_address, &temp);
1287 if (rc == DEVPRESENT_YES) {
1288 if (unit->chiller_temperature != temp) {
1289 unit->mqtt_flag |= MQTT_FLAG_DATA;
1290 }
1291 unit->chiller_temperature = temp;
1292 unit->chiller_state = 0;
1293 } else if (rc == DEVPRESENT_ERROR) {
1294 unit->chiller_state = 1;
1295 } else {
1296 unit->chiller_state = 2;
1297 }
1298 }
1299
1300 /*
1301 * Unit door state, default is closed.
1302 */
1303 if (unit->door_address) {
1304 rc = device_in(unit->door_address, &temp);
1305 if (rc == DEVPRESENT_YES) {
1306 if (temp) {
1307 if (unit->door_state == 0) {
1308 syslog(LOG_NOTICE, "Unit `%s' door closed", unit->alias);
1309 unit->door_state = 1;
1310 unit->mqtt_flag |= MQTT_FLAG_DATA;
1311 }
1312 } else {
1313 if (unit->door_state) {
1314 syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
1315 unit->door_state = 0;
1316 unit->mqtt_flag |= MQTT_FLAG_DATA;
1317 }
1318 /*
1319 * If unit is active and the door is open
1320 */
1321 if (unit->mode != UNITMODE_NONE) {
1322 unit->alarm_flag |= ALARM_FLAG_DOOR;
1323 }
1324 }
1325 } else {
1326 unit->door_state = 1;
1327 }
1328 } else {
1329 unit->door_state = 1;
1330 }
1331
1332 /*
1333 * Unit PSU state
1334 */
1335 if (unit->psu_address) {
1336 rc = device_in(unit->psu_address, &temp);
1337 if (rc == DEVPRESENT_YES) {
1338 if (temp) {
1339 if (unit->psu_state == 0) {
1340 syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->alias);
1341 unit->psu_state = 1;
1342 unit->mqtt_flag |= MQTT_FLAG_DATA;
1343 }
1344 } else {
1345 if (unit->psu_state) {
1346 syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->alias);
1347 unit->psu_state = 0;
1348 unit->mqtt_flag |= MQTT_FLAG_DATA;
1349 }
1350 unit->alarm_flag |= ALARM_FLAG_PSU;
1351 }
1352 } else {
1353 unit->psu_state = 1;
1354 }
1355 } else {
1356 /*
1357 * No state available, assume Ok.
1358 */
1359 unit->psu_state = 1;
1360 }
1361
1362 /*
1363 * Handle profile
1364 */
1365 if ((unit->mode == UNITMODE_PROFILE) && (unit->profile_uuid)) {
1366 /*
1367 * unit->prof_started - start time or 0 if not yet running.
1368 * unit->prof_state - PROFILE_OFF|PROFILE_PAUSE|PROFILE_RUN|PROFILE_DONE
1369 * unit->prof_target - Calculated target temperature.
1370 * unit->prof_paused - Internal pause counter.
1371 * unit->prof_peak_abs - Peak temperature of the beer.
1372 * unit->prof_peak_rel - Peak temperature between beer and fridge.
1373 * unit->prof_primary_done - time when primary fermentation was over the peak.
1374 */
1375
1376 /*
1377 * Safe defaults
1378 */
1379 unit->prof_target_lo = unit->profile_inittemp_lo;
1380 unit->prof_target_hi = unit->profile_inittemp_hi;
1381 unit->prof_fridge_mode = 0;
1382
1383 switch (unit->prof_state) {
1384 case PROFILE_OFF:
1385 unit->prof_percent = 0;
1386 break;
1387 case PROFILE_PAUSE:
1388 /*
1389 * Keep current temperature, measure pause time. For
1390 * temperature fall thru.
1391 */
1392 unit->prof_paused++;
1393 case PROFILE_RUN:
1394 /*
1395 * Calculate current profile step and desired temperature.
1396 * When all steps are done, set state to PROFILE_DONE.
1397 */
1398 previous_target_lo = unit->profile_inittemp_lo;
1399 previous_target_hi = unit->profile_inittemp_hi;
1400 previous_fridge_mode = unit->profile_fridge_mode;
1401 time_until_now = current_step = 0;
1402 run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
1403 run_minutes = run_seconds / 60;
1404 run_hours = run_minutes / 60;
1405 if (debug)
1406 fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);
1407
1408 /*
1409 * Primary fermentation tests
1410 */
1411 if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
1412 unit->prof_peak_abs = unit->beer_temperature / 1000.0;
1413 if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
1414 unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
1415 if (unit->prof_primary_done == 0) {
1416 if (unit->cooler_address) {
1417 /*
1418 * There is a cooler. If the difference between the beer and air temperature
1419 * drops we assume the primary fermentation is done.
1420 */
1421 if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
1422 unit->prof_primary_done = time(NULL);
1423 syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", unit->profile_name);
1424 if (! unit->event_msg)
1425 unit->event_msg = xstrcpy((char *)"Primary peak");
1426 }
1427 } else {
1428 /*
1429 * This method works if the unit has no cooling or if the profile allowed the
1430 * beer temperature to rise freely.
1431 */
1432 if ((unit->beer_temperature / 1000.0) < (unit->prof_peak_abs - 0.5)) {
1433 unit->prof_primary_done = time(NULL);
1434 syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (free rise mode)", unit->profile_name);
1435 if (! unit->event_msg)
1436 unit->event_msg = xstrcpy((char *)"Primary peak");
1437 }
1438 }
1439 }
1440
1441 /*
1442 * See how long this profile will take
1443 */
1444 tot_minutes = 0;
1445 for (step = unit->profile_steps; step; step = step->next) {
1446 tot_minutes += ((step->steptime + step->resttime) * 60);
1447 }
1448 if ((tot_minutes == 0) && unit->profile_totalsteps) {
1449 syslog(LOG_NOTICE, "Profile `%s' steps disappeared", unit->profile_name);
1450 unit->prof_state = PROFILE_OFF;
1451 break;
1452 }
1453
1454 valid_step = FALSE;
1455 for (step = unit->profile_steps; step; step = step->next) {
1456 /*
1457 * step->steptime
1458 * step->resttime
1459 * step->target
1460 */
1461 current_step++;
1462 if ((run_hours >= time_until_now) && (run_hours < (time_until_now + step->steptime + step->resttime))) {
1463 /*
1464 * This is our current step
1465 */
1466 valid_step = TRUE;
1467 if ((run_hours - time_until_now) < step->steptime) {
1468 unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
1469 unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
1470 if (step->fridge_mode > previous_fridge_mode) {
1471 unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
1472 } else if (step->fridge_mode < previous_fridge_mode) {
1473 unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
1474 } else {
1475 unit->prof_fridge_mode = step->fridge_mode;
1476 }
1477 if (debug)
1478 fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
1479 unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
1480 } else {
1481 unit->prof_target_lo = step->target_lo;
1482 unit->prof_target_hi = step->target_hi;
1483 unit->prof_fridge_mode = step->fridge_mode;
1484 }
1485 break;
1486 }
1487 time_until_now += step->steptime + step->resttime;
1488 previous_target_lo = step->target_lo;
1489 previous_target_hi = step->target_hi;
1490 previous_fridge_mode = step->fridge_mode;
1491 }
1492
1493 if (valid_step == TRUE) {
1494 unit->prof_percent = (100 * run_minutes) / tot_minutes;
1495 if (((minutes == 10) || (minutes == 40)) && (seconds == 1)) {
1496 syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees",
1497 unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step,
1498 unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
1499 unit->mqtt_flag |= MQTT_FLAG_DATA;
1500 }
1501 } else {
1502 /*
1503 * No more steps to do
1504 */
1505 unit->prof_state = PROFILE_DONE;
1506 unit->prof_percent = 100;
1507 syslog(LOG_NOTICE, "Profile `%s' is done", unit->profile_name);
1508 unit->mqtt_flag |= MQTT_FLAG_DATA;
1509 if (! unit->event_msg)
1510 unit->event_msg = xstrcpy((char *)"Profile finished");
1511 }
1512 break;
1513
1514 case PROFILE_DONE:
1515 /*
1516 * Keep this state, set target temperature to the last step.
1517 */
1518 previous_target_lo = unit->profile_inittemp_lo;
1519 previous_target_hi = unit->profile_inittemp_hi;
1520 previous_fridge_mode = unit->profile_fridge_mode;
1521 for (step = unit->profile_steps; step; step = step->next) {
1522 if ((step->steptime + step->resttime) == 0)
1523 break;
1524 previous_target_lo = step->target_lo;
1525 previous_target_hi = step->target_hi;
1526 previous_fridge_mode = step->fridge_mode;
1527 }
1528 unit->prof_target_lo = previous_target_lo;
1529 unit->prof_target_hi = previous_target_hi;
1530 unit->prof_fridge_mode = previous_fridge_mode;
1531 unit->prof_percent = 100;
1532 break;
1533 } /* switch */
1534 } else {
1535 /*
1536 * Set some sane values
1537 */
1538 unit->prof_target_lo = 19.8;
1539 unit->prof_target_hi = 20.2;
1540 unit->prof_fridge_mode = 0;
1541 }
1542
1543 /*
1544 * Manual switching
1545 */
1546 if (unit->mode == UNITMODE_NONE) {
1547 device_out(unit->heater_address, unit->heater_state);
1548 device_out(unit->cooler_address, unit->cooler_state);
1549 device_out(unit->fan_address, unit->fan_state);
1550 }
1551
1552 /*
1553 * Usage counters
1554 */
1555 if (unit->heater_address && unit->heater_state)
1556 unit->heater_usage++;
1557 if (unit->cooler_address && unit->cooler_state)
1558 unit->cooler_usage++;
1559 if (unit->fan_address && unit->fan_state)
1560 unit->fan_usage++;
1561 if (unit->light_address && unit->light_state)
1562 unit->light_usage++;
1563
1564 /*
1565 * Interior lights
1566 */
1567 if (unit->light_address) {
1568 if (unit->light_timer) {
1569 unit->light_timer--;
1570 }
1571 if (unit->door_state && !unit->light_timer && unit->light_state) {
1572 if (unit->light_wait > 0) {
1573 unit->light_wait--;
1574 } else {
1575 unit->light_state = 0;
1576 syslog(LOG_NOTICE, "Unit `%s' lights On => Off", unit->alias);
1577 unit->mqtt_flag |= MQTT_FLAG_DATA;
1578 }
1579 }
1580 if ((!unit->door_state || unit->light_timer) && !unit->light_state) {
1581 unit->light_wait = unit->light_delay; /* No delay to turn lights on */
1582 unit->light_state = 1;
1583 unit->mqtt_flag |= MQTT_FLAG_DATA;
1584 syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->alias);
1585 }
1586 device_out(unit->light_address, unit->light_state);
1587 }
1588
1589 /*
1590 * Temperature control in this unit
1591 */
1592 if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
1593
1594 /*
1595 * Set both PID's to their input values.
1596 */
1597 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
1598 if (unit->mode == UNITMODE_FRIDGE) {
1599 unit->PID_cool->SetP = unit->fridge_set_hi;
1600 unit->PID_heat->SetP = unit->fridge_set_lo;
1601 unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
1602 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
1603 } else if (unit->mode == UNITMODE_BEER) {
1604 unit->PID_cool->SetP = unit->beer_set_hi;
1605 unit->PID_heat->SetP = unit->beer_set_lo;
1606 unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
1607 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
1608 } else if (unit->mode == UNITMODE_PROFILE) {
1609 double usetemp;
1610 unit->PID_cool->SetP = unit->prof_target_hi;
1611 unit->PID_heat->SetP = unit->prof_target_lo;
1612 /*
1613 * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
1614 */
1615 usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
1616 ((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
1617 unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
1618 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
1619 }
1620
1621 /*
1622 * PID controller compute, simulate 100 mSec loops.
1623 */
1624 for (int i = 0; i < 10; i++) {
1625 UpdatePID(unit->PID_heat);
1626 UpdatePID(unit->PID_cool);
1627 }
1628
1629 /*
1630 * Logging
1631 */
1632 if (unit->heater_address) {
1633 /*
1634 * Prevent extreme heating
1635 */
1636 if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) {
1637 unit->PID_heat->OutP = 0.0;
1638 }
1639 if (seconds == 60) {
1640 syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
1641 unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP);
1642 }
1643 } else {
1644 unit->PID_heat->OutP = 0.0;
1645 }
1646 if (unit->cooler_address) {
1647 /*
1648 * Prevent extreme cooling
1649 */
1650 if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) < (unit->PID_cool->Input - 8.0))) {
1651 unit->PID_cool->OutP = 0.0;
1652 }
1653 /*
1654 * Prevent cooling if we use a chiller and the chiller temperature is not low enough.
1655 */
1656 if (unit->chiller_address && (unit->chiller_state == 0)) {
1657 if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
1658 unit->PID_cool->OutP = 0.0;
1659 unit->alarm_flag |= ALARM_FLAG_CHILLER;
1660 if (seconds == 60) {
1661 syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
1662 }
1663 }
1664 }
1665 if (seconds == 60) {
1666 syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
1667 unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP);
1668 }
1669 } else {
1670 unit->PID_cool->OutP = 0.0;
1671 }
1672
1673 /*
1674 * Deadlock, kill lowest value.
1675 */
1676 if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
1677 if (unit->PID_cool->OutP > unit->PID_heat->OutP)
1678 unit->PID_heat->OutP = 0.0;
1679 else
1680 unit->PID_cool->OutP = 0.0;
1681 }
1682
1683 if (unit->heater_address && ! unit->cooler_state) {
1684 if (unit->PID_heat->OutP >= 50) {
1685 if (unit->heater_wait < unit->heater_delay) {
1686 unit->heater_wait++;
1687 } else {
1688 int power = round(unit->PID_heat->OutP);
1689 if (unit->heater_state != power) {
1690 syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->alias, unit->heater_state, power);
1691 unit->heater_state = power;
1692 if (unit->heater_address) {
1693 unit->mqtt_flag |= MQTT_FLAG_DATA;
1694 }
1695 }
1696 }
1697 } else {
1698 if (unit->heater_wait > 0) {
1699 unit->heater_wait--;
1700 } else {
1701 if (unit->heater_state) {
1702 syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->alias);
1703 unit->heater_state = 0;
1704 if (unit->heater_address) {
1705 unit->mqtt_flag |= MQTT_FLAG_DATA;
1706 }
1707 }
1708 }
1709 }
1710 if (unit->door_state) {
1711 device_out(unit->heater_address, unit->heater_state);
1712 } else {
1713 device_out(unit->heater_address, 0);
1714 }
1715 }
1716
1717 if (unit->cooler_address && ! unit->heater_state) {
1718 if (unit->PID_cool->OutP >= 50) {
1719 if (unit->cooler_wait < unit->cooler_delay) {
1720 unit->cooler_wait++;
1721 } else {
1722 int power = round(unit->PID_cool->OutP);
1723 if (unit->cooler_state != power) {
1724 syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->alias, unit->cooler_state, power);
1725 unit->cooler_state = power;
1726 if (unit->cooler_address) {
1727 unit->mqtt_flag |= MQTT_FLAG_DATA;
1728 }
1729 }
1730 }
1731 } else {
1732 if (unit->cooler_wait > 0) {
1733 unit->cooler_wait--;
1734 } else {
1735 if (unit->cooler_state) {
1736 syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->alias);
1737 unit->cooler_state = 0;
1738 if (unit->cooler_address) {
1739 unit->mqtt_flag |= MQTT_FLAG_DATA;
1740 }
1741 }
1742 }
1743 }
1744 if (unit->door_state) {
1745 device_out(unit->cooler_address, unit->cooler_state);
1746 } else {
1747 device_out(unit->cooler_address, 0);
1748 }
1749 }
1750
1751 /*
1752 * If there is a fan, and the unit door is closed, and the unit should be doing
1753 * something, then turn on the global fan.
1754 * But if there is a chiller, do not turn it on if cooling.
1755 */
1756 if (unit->fan_address) {
1757 if ((unit->door_state) && (unit->cooler_state == 0)) {
1758 if (unit->fan_wait < unit->fan_delay) {
1759 unit->fan_wait++;
1760 } else {
1761 if (! unit->fan_state) {
1762 syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->alias);
1763 unit->fan_state = 100;
1764 if (unit->fan_address) {
1765 unit->mqtt_flag |= MQTT_FLAG_DATA;
1766 }
1767 }
1768 }
1769 } else {
1770 if (unit->fan_wait > 0) {
1771 unit->fan_wait--;
1772 } else {
1773 if (unit->fan_state) {
1774 syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->alias);
1775 unit->fan_state = 0;
1776 if (unit->fan_address) {
1777 unit->mqtt_flag |= MQTT_FLAG_DATA;
1778 }
1779 }
1780 }
1781 }
1782 device_out(unit->fan_address, unit->fan_state);
1783 }
1784
1785 } else {
1786 unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
1787 } /* fridge beer or profile mode */
1788
1789 /*
1790 * Now everything is set and done, update the LCD display
1791 */
1792 LCDair = unit->air_temperature / 1000.0;
1793 LCDbeer = unit->beer_temperature / 1000.0;
1794 LCDstatC = LCDstatH = ' ';
1795 if (unit->heater_address) {
1796 if (unit->heater_state)
1797 LCDstatH = '\6';
1798 else
1799 LCDstatH = '\5';
1800 }
1801 if (unit->cooler_address) {
1802 if (unit->cooler_state)
1803 LCDstatC = '\4';
1804 else
1805 LCDstatC = '\3';
1806 }
1807 LCDspH = LCDspL = 0.0;
1808 if (unit->mode == UNITMODE_BEER) {
1809 LCDspH = unit->beer_set_hi;
1810 LCDspL = unit->beer_set_lo;
1811 } else if (unit->mode == UNITMODE_FRIDGE) {
1812 LCDspH = unit->fridge_set_hi;
1813 LCDspL = unit->fridge_set_lo;
1814 } else if (unit->mode == UNITMODE_PROFILE) {
1815 if (unit->prof_state != PROFILE_OFF) {
1816 LCDspL = unit->prof_target_lo;
1817 LCDspH = unit->prof_target_hi;
1818 }
1819 }
1820 // if ((seconds == 60) && ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE))) {
1821 // unit->mqtt_flag |= MQTT_FLAG_DATA;
1822 // }
1823 if (seconds == 60) {
1824 unit->mqtt_flag |= MQTT_FLAG_DATA;
1825 }
1826 pthread_mutex_lock(&mutexes[LOCK_LCD]);
1827 /*
1828 * Write 4 rows to the LCD to display the unit state
1829 */
1830 lcd_buf_write(row++, "Unit %d: %s ", LCDunit, UNITMODE[unit->mode]);
1831 lcd_buf_write(row++, "%s ", unit->product_name);
1832 lcd_buf_write(row++, "%c%5.1f\2 A%6.2f\1 ", LCDstatC, LCDspH, LCDair);
1833 lcd_buf_write(row++, "%c%5.1f\2 B%6.2f\1 ", LCDstatH, LCDspL, LCDbeer);
1834 pthread_mutex_unlock(&mutexes[LOCK_LCD]);
1835
1836 /*
1837 * Publish MQTT messages set in flag
1838 */
1839 if (unit->mqtt_flag) {
1840 if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
1841 publishDBirth(unit);
1842 unit->mqtt_flag &= ~MQTT_FLAG_BIRTH;
1843 } else {
1844 publishDData(unit);
1845 unit->mqtt_flag &= ~MQTT_FLAG_DATA;
1846 }
1847 if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
1848 publishDDeath(unit);
1849 unit->mqtt_flag &= ~MQTT_FLAG_DEATH;
1850 }
1851 }
1852
1853 /*
1854 * Handle changed alarms
1855 */
1856 if (unit->alarm_flag != unit->alarm_last) {
1857 syslog(LOG_NOTICE, "Unit `%s' Alarm %d => %d", unit->alias, unit->alarm_last, unit->alarm_flag);
1858 unit->alarm_last = unit->alarm_flag;
1859 }
1860 } /* for units */ 1870 } /* for units */
1861 1871
1862 pthread_mutex_lock(&mutexes[LOCK_MENU]); 1872 pthread_mutex_lock(&mutexes[LOCK_MENU]);
1863 if (setupmenu == MENU_NONE) { 1873 if (setupmenu == MENU_NONE) {
1864 pthread_mutex_lock(&mutexes[LOCK_LCD]); 1874 pthread_mutex_lock(&mutexes[LOCK_LCD]);

mercurial