Fermentation processing in it's own function.

Sun, 07 Apr 2024 16:05:06 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 07 Apr 2024 16:05:06 +0200
changeset 665
66fae54fa7ba
parent 664
8adbc76fd122
child 666
48cc8868f9f4

Fermentation processing in it's own function.

thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
--- a/thermferm/server.c	Sun Apr 07 11:25:00 2024 +0200
+++ b/thermferm/server.c	Sun Apr 07 16:05:06 2024 +0200
@@ -2226,7 +2226,7 @@
 	 * descriptor, s, for that connection.
 	 */
 	s = accept(ls, (struct sockaddr *)&peeraddr_in, &addrlen);
-	syslog(LOG_NOTICE, "my_server_loop accept socket %d", s);
+//	syslog(LOG_NOTICE, "my_server_loop accept socket %d", s);
 	if (s == -1) {
 	    syslog(LOG_NOTICE, "my_server_loop accept failed %s", strerror(errno));
 	    break;
--- a/thermferm/thermferm.c	Sun Apr 07 11:25:00 2024 +0200
+++ b/thermferm/thermferm.c	Sun Apr 07 16:05:06 2024 +0200
@@ -36,6 +36,7 @@
 #include "xutil.h"
 #include "pid.h"
 #include "mqtt.h"
+#include "statetbl.h"
 
 
 int			my_shutdown = FALSE;
@@ -43,6 +44,7 @@
 static pid_t		pgrp, mypid;
 int			run_pause = FALSE;
 int			run_hold = FALSE;
+int			row;
 
 extern int		debug;
 extern sys_config	Config;
@@ -1027,20 +1029,638 @@
 }
 
 
+void do_unit(units_list *unit, int LCDunit, int *seconds, int *minutes)
+{
+    time_t		now;
+    prof_step		*step;
+    int			rc, temp;
+    int			run_seconds, run_minutes, run_hours, tot_minutes;
+    int			current_step, valid_step, time_until_now, previous_fridge_mode;
+    float		previous_target_lo, previous_target_hi;
+    float		LCDair, LCDbeer, LCDspL, LCDspH;
+    unsigned char	LCDstatC, LCDstatH;
+
+    unit->mqtt_flag &= ~MQTT_FLAG_DATA;
+    unit->alarm_flag = 0;
+
+    if (unit->air_address) {
+	rc = device_in(unit->air_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (unit->air_temperature != temp) {
+		unit->mqtt_flag |= MQTT_FLAG_DATA;
+	    }
+	    unit->air_temperature = temp;
+	    unit->air_state = 0;
+	} else if (rc == DEVPRESENT_ERROR) {
+	    unit->air_state = 1;
+	} else {
+	    unit->air_state = 2;
+	}
+    }
+
+    if (unit->beer_address) {
+	rc = device_in(unit->beer_address, &temp);
+	if ((rc == DEVPRESENT_NO) && unit->beer_address2) {
+	    /* Read alternative sensor */
+	    rc = device_in(unit->beer_address2, &temp);
+	}
+	if (rc == DEVPRESENT_YES) {
+	    if (unit->beer_temperature != temp) {
+		unit->mqtt_flag |= MQTT_FLAG_DATA;
+	    }
+	    unit->beer_temperature = temp;
+	    unit->beer_state = 0;
+	} else if (rc == DEVPRESENT_ERROR) {
+	    unit->beer_state = 1;
+	} else {
+	    unit->beer_state = 2;
+	}
+    }
+
+    if (unit->chiller_address) {
+	rc = device_in(unit->chiller_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (unit->chiller_temperature != temp) {
+		unit->mqtt_flag |= MQTT_FLAG_DATA;
+	    }
+	    unit->chiller_temperature = temp;
+	    unit->chiller_state = 0;
+	} else if (rc == DEVPRESENT_ERROR) {
+	    unit->chiller_state = 1;
+	} else {
+	    unit->chiller_state = 2;
+	}
+    }
+
+    /*
+     * Unit door state, default is closed.
+     */
+    if (unit->door_address) {
+	rc = device_in(unit->door_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (temp) {
+		if (unit->door_state == 0) {
+		    syslog(LOG_NOTICE, "Unit `%s' door closed", unit->alias);
+		    unit->door_state = 1;
+		    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		}
+	    } else {
+		if (unit->door_state) {
+		    syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
+		    unit->door_state = 0;
+		    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		}
+		/*
+		 * If unit is active and the door is open
+		 */
+		if (unit->mode != UNITMODE_NONE) {
+		    unit->alarm_flag |= ALARM_FLAG_DOOR;
+		}
+	    }
+	} else {
+	    unit->door_state = 1;
+	}
+    } else {
+	unit->door_state = 1;
+    }
+
+    /*
+     * Unit PSU state
+     */
+    if (unit->psu_address) {
+	rc = device_in(unit->psu_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (temp) {
+		if (unit->psu_state == 0) {
+		    syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->alias);
+		    unit->psu_state = 1;
+		    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		}
+	    } else {
+		if (unit->psu_state) {
+		    syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->alias);
+		    unit->psu_state = 0;
+		    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		}
+		unit->alarm_flag |= ALARM_FLAG_PSU;
+	    }
+	} else {
+	    unit->psu_state = 1;
+	}
+    } else {
+	/*
+	 * No state available, assume Ok.
+	 */
+	unit->psu_state = 1;
+    }
+
+    /*
+     * Handle profile
+     */
+    if ((unit->mode == UNITMODE_PROFILE) && (unit->profile_uuid)) {
+	/*
+	 * unit->prof_started       - start time or 0 if not yet running.
+	 * unit->prof_state         - PROFILE_OFF|PROFILE_PAUSE|PROFILE_RUN|PROFILE_DONE
+	 * unit->prof_target        - Calculated target temperature.
+	 * unit->prof_paused        - Internal pause counter.
+	 * unit->prof_peak_abs      - Peak temperature of the beer.
+	 * unit->prof_peak_rel      - Peak temperature between beer and fridge.
+	 * unit->prof_primary_done  - time when primary fermentation was over the peak.
+	 */
+
+	/*
+	 * Safe defaults
+	 */
+	unit->prof_target_lo = unit->profile_inittemp_lo;
+	unit->prof_target_hi = unit->profile_inittemp_hi;
+	unit->prof_fridge_mode = 0;
+
+	switch (unit->prof_state) {
+	    case PROFILE_OFF:
+				unit->prof_percent = 0;
+				break;
+	    case PROFILE_PAUSE:
+				/*
+				 * Keep current temperature, measure pause time. For
+				 * temperature fall thru.
+				 */
+				unit->prof_paused++;
+	    case PROFILE_RUN:
+				/*
+				 * Calculate current profile step and desired temperature.
+				 * When all steps are done, set state to PROFILE_DONE.
+				 */
+				previous_target_lo = unit->profile_inittemp_lo;
+				previous_target_hi = unit->profile_inittemp_hi;
+				previous_fridge_mode = unit->profile_fridge_mode;
+				time_until_now = current_step = 0;
+				now = time(NULL);
+				run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
+				run_minutes = run_seconds / 60;
+				run_hours = run_minutes / 60;
+				if (debug)
+				    fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);
+
+				/*
+				 * Primary fermentation tests
+				 */
+				if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
+				    unit->prof_peak_abs = unit->beer_temperature / 1000.0;
+				if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
+				    unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
+				if (unit->prof_primary_done == 0) {
+				    if (unit->cooler_address) {
+					/*
+					 * There is a cooler. If the difference between the beer and air temperature
+					 * drops we assume the primary fermentation is done.
+					 */
+					if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
+					    unit->prof_primary_done = time(NULL);
+					    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", unit->profile_name);
+					    if (! unit->event_msg)
+						unit->event_msg = xstrcpy((char *)"Primary peak");
+					}
+				    } else {
+					/*
+					 * This method works if the unit has no cooling or if the profile allowed the
+					 * beer temperature to rise freely.
+					 */
+					if ((unit->beer_temperature / 1000.0) <  (unit->prof_peak_abs - 0.5)) {
+					    unit->prof_primary_done = time(NULL);
+					    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (free rise mode)", unit->profile_name);
+					    if (! unit->event_msg)
+						unit->event_msg = xstrcpy((char *)"Primary peak");
+					}
+				    }
+				}
+
+				/*
+				 * See how long this profile will take
+				 */
+				tot_minutes = 0;
+				for (step = unit->profile_steps; step; step = step->next) {
+				    tot_minutes += ((step->steptime + step->resttime) * 60);
+				}
+				if ((tot_minutes == 0) && unit->profile_totalsteps) {
+				    syslog(LOG_NOTICE, "Profile `%s' steps disappeared", unit->profile_name);
+				    unit->prof_state = PROFILE_OFF;
+				    break;
+				}
+
+				valid_step = FALSE;
+				for (step = unit->profile_steps; step; step = step->next) {
+				    /*
+				     * step->steptime
+				     * step->resttime
+				     * step->target
+				     */
+				    current_step++;
+				    if ((run_hours >= time_until_now) && (run_hours < (time_until_now + step->steptime + step->resttime))) {
+					/*
+					 * This is our current step
+					 */
+					valid_step = TRUE;
+					if ((run_hours - time_until_now) < step->steptime) {
+					    unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
+					    unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
+					    if (step->fridge_mode > previous_fridge_mode) {
+						unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
+					    } else if (step->fridge_mode < previous_fridge_mode) {
+						unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
+					    } else {
+						unit->prof_fridge_mode = step->fridge_mode;
+					    }
+					    if (debug)
+						fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
+								unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
+					} else {
+					    unit->prof_target_lo = step->target_lo;
+					    unit->prof_target_hi = step->target_hi;
+					    unit->prof_fridge_mode = step->fridge_mode;
+					}
+					break;
+				    }
+				    time_until_now += step->steptime + step->resttime;
+				    previous_target_lo = step->target_lo;
+				    previous_target_hi = step->target_hi;
+				    previous_fridge_mode = step->fridge_mode;
+				}
+
+				if (valid_step == TRUE) {
+				    unit->prof_percent = (100 * run_minutes) / tot_minutes;
+				    if (((*minutes == 10) || (*minutes == 40)) && (*seconds == 1)) {
+					syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees", 
+								unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
+								unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
+					unit->mqtt_flag |= MQTT_FLAG_DATA;
+				    }
+				} else {
+				    /*
+				     * No more steps to do
+				     */
+				    unit->prof_state = PROFILE_DONE;
+				    unit->prof_percent = 100;
+				    syslog(LOG_NOTICE, "Profile `%s' is done", unit->profile_name);
+				    unit->mqtt_flag |= MQTT_FLAG_DATA;
+				    if (! unit->event_msg)
+					unit->event_msg = xstrcpy((char *)"Profile finished");
+				}
+				break;
+
+	    case PROFILE_DONE:
+				/*
+				 * Keep this state, set target temperature to the last step.
+				 */
+				previous_target_lo = unit->profile_inittemp_lo;
+				previous_target_hi = unit->profile_inittemp_hi;
+				previous_fridge_mode = unit->profile_fridge_mode;
+				for (step = unit->profile_steps; step; step = step->next) {
+				    if ((step->steptime + step->resttime) == 0)
+					break;
+				    previous_target_lo = step->target_lo;
+				    previous_target_hi = step->target_hi;
+				    previous_fridge_mode = step->fridge_mode;
+				}
+				unit->prof_target_lo = previous_target_lo;
+				unit->prof_target_hi = previous_target_hi;
+				unit->prof_fridge_mode = previous_fridge_mode;
+				unit->prof_percent = 100;
+				break;
+	} /* switch */
+    } else {
+	/*
+	 * Set some sane values
+	 */
+	unit->prof_target_lo = 19.8;
+	unit->prof_target_hi = 20.2;
+	unit->prof_fridge_mode = 0;
+    }
+
+    /*
+     * Manual switching
+     */
+    if (unit->mode == UNITMODE_NONE) {
+	device_out(unit->heater_address, unit->heater_state);
+	device_out(unit->cooler_address, unit->cooler_state);
+	device_out(unit->fan_address, unit->fan_state);
+    }
+
+    /*
+     * Usage counters
+     */
+    if (unit->heater_address && unit->heater_state)
+	unit->heater_usage++;
+    if (unit->cooler_address && unit->cooler_state)
+	unit->cooler_usage++;
+    if (unit->fan_address && unit->fan_state)
+	unit->fan_usage++;
+    if (unit->light_address && unit->light_state)
+	unit->light_usage++;
+
+    /*
+     * Interior lights
+     */
+    if (unit->light_address) {
+	if (unit->light_timer) {
+	    unit->light_timer--;
+	}
+	if (unit->door_state && !unit->light_timer && unit->light_state) {
+	    if (unit->light_wait > 0) {
+		unit->light_wait--;
+	    } else {
+		unit->light_state = 0;
+		syslog(LOG_NOTICE, "Unit `%s' lights On => Off", unit->alias);
+		unit->mqtt_flag |= MQTT_FLAG_DATA;
+	    }
+	}
+	if ((!unit->door_state || unit->light_timer) && !unit->light_state) {
+	    unit->light_wait = unit->light_delay;   /* No delay to turn lights on   */
+	    unit->light_state = 1;
+	    unit->mqtt_flag |= MQTT_FLAG_DATA;
+	    syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->alias);
+	}
+	device_out(unit->light_address, unit->light_state);
+    }
+
+    /*
+     * Temperature control in this unit
+     */
+    if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
+
+    	/*
+    	 * Set both PID's to their input values.
+    	 */
+    	unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
+    	if (unit->mode == UNITMODE_FRIDGE) {
+	    unit->PID_cool->SetP = unit->fridge_set_hi;
+	    unit->PID_heat->SetP = unit->fridge_set_lo;
+	    unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
+	    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
+    	} else if (unit->mode == UNITMODE_BEER) {
+	    unit->PID_cool->SetP = unit->beer_set_hi;
+	    unit->PID_heat->SetP = unit->beer_set_lo;
+	    unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
+	    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
+    	} else if (unit->mode == UNITMODE_PROFILE) {
+	    double  usetemp;
+	    unit->PID_cool->SetP = unit->prof_target_hi;
+	    unit->PID_heat->SetP = unit->prof_target_lo;
+	    /*
+	     * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
+	     */
+	    usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
+	  		((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
+	    unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
+	    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
+    	}
+
+    	/*
+    	 * PID controller compute, simulate 100 mSec loops by running 10 times.
+    	 */
+    	for (int i = 0; i < 10; i++) {
+	    UpdatePID(unit->PID_heat);
+	    UpdatePID(unit->PID_cool);
+    	}
+    	/*
+    	 * Logging
+    	 */
+    	if (unit->heater_address) {
+	    /*
+	     * Prevent extreme heating
+	     */
+	    if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) {
+	    	unit->PID_heat->OutP = 0.0;
+	    }
+	    if (*seconds == 60) {
+	    	syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
+				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP);
+	    }
+    	} else {
+	    unit->PID_heat->OutP = 0.0;
+    	}
+    	if (unit->cooler_address) {
+	    /*
+	     * Prevent extreme cooling
+	     */
+	    if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) < (unit->PID_cool->Input - 8.0))) {
+	    	unit->PID_cool->OutP = 0.0;
+	    }
+	    /*
+	     * Prevent cooling if we use a chiller and the chiller temperature is not low enough.
+	     */
+	   if (unit->chiller_address && (unit->chiller_state == 0)) {
+	   	if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
+		    unit->PID_cool->OutP = 0.0;
+		    unit->alarm_flag |= ALARM_FLAG_CHILLER;
+		    if (*seconds == 60) {
+		    	syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
+		    }
+	    	}
+	    }
+	    if (*seconds == 60) {
+	    	syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
+	    	unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP);
+	    }
+    	} else {
+	    unit->PID_cool->OutP = 0.0;
+    	}
+
+    	/*
+    	 * Deadlock, kill lowest value.
+    	 */
+    	if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
+	    if (unit->PID_cool->OutP > unit->PID_heat->OutP)
+	    	unit->PID_heat->OutP = 0.0;
+	    else
+	    	unit->PID_cool->OutP = 0.0;
+        }
+
+        if (unit->heater_address && ! unit->cooler_state) {
+	    if (unit->PID_heat->OutP >= 50) {
+	    	if (unit->heater_wait < unit->heater_delay) {
+		    unit->heater_wait++;
+	    	} else {
+		    int     power = round(unit->PID_heat->OutP);
+		    if (unit->heater_state != power) {
+		    	syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->alias, unit->heater_state, power);
+		    	unit->heater_state = power;
+		    	if (unit->heater_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		    	}
+		    }
+	    	}
+	    } else {
+	    	if (unit->heater_wait > 0) {
+		    unit->heater_wait--;
+	    	} else {
+		    if (unit->heater_state) {
+		    	syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->alias);
+		    	unit->heater_state = 0;
+		    	if (unit->heater_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+		    	}
+		    }
+	    	}
+	    }
+	    if (unit->door_state) {
+	    	device_out(unit->heater_address, unit->heater_state);
+	    } else {
+	    	device_out(unit->heater_address, 0);
+	    }
+    	}
+
+    	if (unit->cooler_address && ! unit->heater_state) {
+	    if (unit->PID_cool->OutP >= 50) {
+		if (unit->cooler_wait < unit->cooler_delay) {
+		    unit->cooler_wait++;
+		} else {
+		    int     power = round(unit->PID_cool->OutP);
+		    if (unit->cooler_state != power) {
+			syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->alias, unit->cooler_state, power);
+			unit->cooler_state = power;
+			if (unit->cooler_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+			}
+		    }
+		}
+	    } else {
+		if (unit->cooler_wait > 0) {
+		    unit->cooler_wait--;
+		} else {
+		    if (unit->cooler_state) {
+			syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->alias);
+			unit->cooler_state = 0;
+			if (unit->cooler_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+			}
+		    }
+		}
+	    }
+	    if (unit->door_state) {
+		device_out(unit->cooler_address, unit->cooler_state);
+	    } else {
+		device_out(unit->cooler_address, 0);
+	    }
+    	}
+
+    	/*
+    	 * If there is a fan, and the unit door is closed, and the unit should be doing
+    	 * something, then turn on the global fan.
+    	 * But if there is a chiller, do not turn it on if cooling.
+    	 */
+    	if (unit->fan_address) {
+	    if ((unit->door_state) && (unit->cooler_state == 0)) {
+		if (unit->fan_wait < unit->fan_delay) {
+		    unit->fan_wait++;
+		} else {
+		    if (! unit->fan_state) {
+			syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->alias);
+			unit->fan_state = 100;
+			if (unit->fan_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+			}
+		    }
+		}
+	    } else {
+		if (unit->fan_wait > 0) {
+		    unit->fan_wait--;
+		} else {
+		    if (unit->fan_state) {
+			syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->alias);
+			unit->fan_state = 0;
+			if (unit->fan_address) {
+			    unit->mqtt_flag |= MQTT_FLAG_DATA;
+			}
+		    }
+		}
+	    }
+	    device_out(unit->fan_address, unit->fan_state);
+	}
+
+    } else {
+	unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
+    } /* fridge beer or profile mode */
+
+    /*
+     * Now everything is set and done, update the LCD display
+     */
+    LCDair = unit->air_temperature / 1000.0;
+    LCDbeer = unit->beer_temperature / 1000.0;
+    LCDstatC = LCDstatH = ' ';
+    if (unit->heater_address) {
+	if (unit->heater_state)
+	    LCDstatH = '\6';
+	else
+	    LCDstatH = '\5';
+    }
+    if (unit->cooler_address) {
+	if (unit->cooler_state)
+	    LCDstatC = '\4';
+	else
+	    LCDstatC = '\3';
+    }
+    LCDspH = LCDspL = 0.0;
+    if (unit->mode == UNITMODE_BEER) {
+	LCDspH = unit->beer_set_hi;
+	LCDspL = unit->beer_set_lo;
+    } else if (unit->mode == UNITMODE_FRIDGE) {
+	LCDspH = unit->fridge_set_hi;
+	LCDspL = unit->fridge_set_lo;
+    } else if (unit->mode == UNITMODE_PROFILE) {
+	if (unit->prof_state != PROFILE_OFF) {
+	    LCDspL = unit->prof_target_lo;
+	    LCDspH = unit->prof_target_hi;
+	}
+    }
+    if (*seconds == 60) {
+	unit->mqtt_flag |= MQTT_FLAG_DATA;
+    }
+    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+    /*
+     * Write 4 rows to the LCD to display the unit state
+     */
+    lcd_buf_write(row++, "Unit %d: %s          ", LCDunit, UNITMODE[unit->mode]);
+    lcd_buf_write(row++, "%s                   ", unit->product_name);
+    lcd_buf_write(row++, "%c%5.1f\2 A%6.2f\1    ", LCDstatC, LCDspH, LCDair);
+    lcd_buf_write(row++, "%c%5.1f\2 B%6.2f\1    ", LCDstatH, LCDspL, LCDbeer);
+    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+
+    /*
+     * Publish MQTT messages set in flag
+     */
+    if (unit->mqtt_flag) {
+	if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
+	    publishDBirth(unit);
+	    unit->mqtt_flag &= ~MQTT_FLAG_BIRTH;
+	} else {
+	    publishDData(unit);
+	    unit->mqtt_flag &= ~MQTT_FLAG_DATA;
+	}
+	if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
+	    publishDDeath(unit);
+	    unit->mqtt_flag &= ~MQTT_FLAG_DEATH;
+	}
+    }
+
+    /*
+     * Handle changed alarms
+     */
+    if (unit->alarm_flag != unit->alarm_last) {
+	syslog(LOG_NOTICE, "Unit `%s' Alarm %d => %d", unit->alias, unit->alarm_last, unit->alarm_flag);
+	unit->alarm_last = unit->alarm_flag;
+    }
+}
+
+
 
 int server(void)
 {
     time_t		now, last = (time_t)0, ndata = (time_t)0;;
     units_list		*unit;
-    prof_step		*step;
-    int			row, rc, run = 1, seconds = 0, minutes = 0, temp;
-    int			run_seconds, run_minutes, run_hours, tot_minutes, key;
+    int			rc, run = 1, seconds = 0, minutes = 0, temp;
+    int			key;
     struct tm		*tm;
     long		t = 0;
-    int			current_step, valid_step, time_until_now, previous_fridge_mode;
-    float		previous_target_lo, previous_target_hi;
-    float		LCDair, LCDbeer, LCDspL, LCDspH;
-    unsigned char	LCDstatC, LCDstatH;
     int			LCDunit;
 
     syslog(LOG_NOTICE, "Server process started");
@@ -1245,618 +1865,8 @@
 	    LCDunit = 0;
 	    for (unit = Config.units; unit; unit = unit->next) {
 		LCDunit++;
-		unit->mqtt_flag &= ~MQTT_FLAG_DATA;
-		unit->alarm_flag = 0;
-
-		if (unit->air_address) {
-		    rc = device_in(unit->air_address, &temp);
-		    if (rc == DEVPRESENT_YES) {
-			if (unit->air_temperature != temp) {
-			    unit->mqtt_flag |= MQTT_FLAG_DATA;
-			}
-			unit->air_temperature = temp;
-			unit->air_state = 0;
-		    } else if (rc == DEVPRESENT_ERROR) {
-			unit->air_state = 1;
-		    } else {
-			unit->air_state = 2;
-		    }
-		}
-
-		if (unit->beer_address) {
-		    rc = device_in(unit->beer_address, &temp);
-		    if ((rc == DEVPRESENT_NO) && unit->beer_address2) {
-			/* Read alternative sensor */
-			rc = device_in(unit->beer_address2, &temp);
-		    }
-		    if (rc == DEVPRESENT_YES) {
-			if (unit->beer_temperature != temp) {
-			    unit->mqtt_flag |= MQTT_FLAG_DATA;
-			}
-    			unit->beer_temperature = temp;
-			unit->beer_state = 0;
-		    } else if (rc == DEVPRESENT_ERROR) {
-			unit->beer_state = 1;
-		    } else {
-			unit->beer_state = 2;
-		    }
-		}
-
-		if (unit->chiller_address) {
-		    rc = device_in(unit->chiller_address, &temp);
-		    if (rc == DEVPRESENT_YES) {
-			if (unit->chiller_temperature != temp) {
-			    unit->mqtt_flag |= MQTT_FLAG_DATA;
-			}
-			unit->chiller_temperature = temp;
-			unit->chiller_state = 0;
-		    } else if (rc == DEVPRESENT_ERROR) {
-			unit->chiller_state = 1;
-		    } else {
-			unit->chiller_state = 2;
-		    }
-		}
-
-		/*
-		 * Unit door state, default is closed.
-		 */
-		if (unit->door_address) {
-		    rc = device_in(unit->door_address, &temp);
-		    if (rc == DEVPRESENT_YES) {
-			if (temp) {
-			    if (unit->door_state == 0) {
-			    	syslog(LOG_NOTICE, "Unit `%s' door closed", unit->alias);
-			    	unit->door_state = 1;
-				unit->mqtt_flag |= MQTT_FLAG_DATA;
-			    }
-			} else {
-			    if (unit->door_state) {
-			    	syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
-			    	unit->door_state = 0;
-				unit->mqtt_flag |= MQTT_FLAG_DATA;
-			    }
-			    /*
-			     * If unit is active and the door is open
-			     */
-			    if (unit->mode != UNITMODE_NONE) {
-				unit->alarm_flag |= ALARM_FLAG_DOOR;
-			    }
-			}
-		    } else {
-			unit->door_state = 1;
-		    }
-		} else {
-		    unit->door_state = 1;
-		}
-
-		/*
-		 * Unit PSU state
-		 */
-		if (unit->psu_address) {
-		    rc = device_in(unit->psu_address, &temp);
-		    if (rc == DEVPRESENT_YES) {
-			if (temp) {
-			    if (unit->psu_state == 0) {
-				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->alias);
-				unit->psu_state = 1;
-				unit->mqtt_flag |= MQTT_FLAG_DATA;
-			    }
-			} else {
-			    if (unit->psu_state) {
-				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->alias);
-				unit->psu_state = 0;
-				unit->mqtt_flag |= MQTT_FLAG_DATA;
-			    }
-			    unit->alarm_flag |= ALARM_FLAG_PSU;
-			}
-		    } else {
-			unit->psu_state = 1;
-		    }
-		} else {
-		    /*
-		     * No state available, assume Ok.
-		     */
-		    unit->psu_state = 1;
-		}
-
-		/*
-		 * Handle profile
-		 */
-		if ((unit->mode == UNITMODE_PROFILE) && (unit->profile_uuid)) {
-		    /*
-		     * unit->prof_started	- start time or 0 if not yet running.
-		     * unit->prof_state		- PROFILE_OFF|PROFILE_PAUSE|PROFILE_RUN|PROFILE_DONE
-		     * unit->prof_target	- Calculated target temperature.
-		     * unit->prof_paused	- Internal pause counter.
-		     * unit->prof_peak_abs	- Peak temperature of the beer.
-		     * unit->prof_peak_rel	- Peak temperature between beer and fridge.
-		     * unit->prof_primary_done	- time when primary fermentation was over the peak.
-		     */
-
-		    /*
-		     * Safe defaults
-		     */
-		    unit->prof_target_lo = unit->profile_inittemp_lo;
-		    unit->prof_target_hi = unit->profile_inittemp_hi;
-		    unit->prof_fridge_mode = 0;
-
-		    switch (unit->prof_state) {
-			case PROFILE_OFF:
-					unit->prof_percent = 0;
-					break;
-			case PROFILE_PAUSE:
-					/*
-					 * Keep current temperature, measure pause time. For
-					 * temperature fall thru.
-					 */
-					unit->prof_paused++;
-			case PROFILE_RUN:
-					/*
-					 * Calculate current profile step and desired temperature.
-					 * When all steps are done, set state to PROFILE_DONE.
-					 */
-					previous_target_lo = unit->profile_inittemp_lo;
-					previous_target_hi = unit->profile_inittemp_hi;
-					previous_fridge_mode = unit->profile_fridge_mode;
-					time_until_now = current_step = 0;
-					run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
-					run_minutes = run_seconds / 60;
-					run_hours = run_minutes / 60;
-					if (debug)
-					    fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);
-
-					/*
-					 * Primary fermentation tests
-					 */
-					if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
-					    unit->prof_peak_abs = unit->beer_temperature / 1000.0;
-					if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
-					    unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
-					if (unit->prof_primary_done == 0) {
-					    if (unit->cooler_address) {
-						/*
-						 * There is a cooler. If the difference between the beer and air temperature
-						 * drops we assume the primary fermentation is done.
-						 */
-						if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
-						    unit->prof_primary_done = time(NULL);
-						    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", unit->profile_name);
-						    if (! unit->event_msg)
-							unit->event_msg = xstrcpy((char *)"Primary peak");
-						}
-					    } else {
-					    	/*
-					    	 * This method works if the unit has no cooling or if the profile allowed the
-					    	 * beer temperature to rise freely.
-					    	 */
-					    	if ((unit->beer_temperature / 1000.0) <  (unit->prof_peak_abs - 0.5)) {
-						    unit->prof_primary_done = time(NULL);
-						    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (free rise mode)", unit->profile_name);
-						    if (! unit->event_msg)
-							unit->event_msg = xstrcpy((char *)"Primary peak");
-					    	}
-					    }
-					}
-
-					/*
-					 * See how long this profile will take
-					 */
-					tot_minutes = 0;
-					for (step = unit->profile_steps; step; step = step->next) {
-					    tot_minutes += ((step->steptime + step->resttime) * 60);
-					}
-					if ((tot_minutes == 0) && unit->profile_totalsteps) {
-					    syslog(LOG_NOTICE, "Profile `%s' steps disappeared", unit->profile_name);
-					    unit->prof_state = PROFILE_OFF;
-					    break;
-					}
-
-					valid_step = FALSE;
-					for (step = unit->profile_steps; step; step = step->next) {
-					    /*
-					     * step->steptime
-					     * step->resttime
-					     * step->target
-					     */
-					    current_step++;
-					    if ((run_hours >= time_until_now) && (run_hours < (time_until_now + step->steptime + step->resttime))) {
-						/*
-						 * This is our current step
-						 */
-						valid_step = TRUE;
-						if ((run_hours - time_until_now) < step->steptime) {
-						    unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
-						    unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
-						    if (step->fridge_mode > previous_fridge_mode) {
-						    	unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
-						    } else if (step->fridge_mode < previous_fridge_mode) {
-							unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
-						    } else {
-							unit->prof_fridge_mode = step->fridge_mode;
-						    }
-						    if (debug)
-							fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
-									unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
-						} else {
-						    unit->prof_target_lo = step->target_lo;
-						    unit->prof_target_hi = step->target_hi;
-						    unit->prof_fridge_mode = step->fridge_mode;
-						}
-						break;
-					    }
-					    time_until_now += step->steptime + step->resttime;
-					    previous_target_lo = step->target_lo;
-					    previous_target_hi = step->target_hi;
-					    previous_fridge_mode = step->fridge_mode;
-					}
-
-					if (valid_step == TRUE) {
-					    unit->prof_percent = (100 * run_minutes) / tot_minutes;
-					    if (((minutes == 10) || (minutes == 40)) && (seconds == 1)) {
-					    	syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees", 
-							unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
-							unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
-						unit->mqtt_flag |= MQTT_FLAG_DATA;
-					    }
-					} else {
-					    /*
-					     * No more steps to do
-					     */
-					    unit->prof_state = PROFILE_DONE;
-					    unit->prof_percent = 100;
-					    syslog(LOG_NOTICE, "Profile `%s' is done", unit->profile_name);
-					    unit->mqtt_flag |= MQTT_FLAG_DATA;
-					    if (! unit->event_msg)
-						unit->event_msg = xstrcpy((char *)"Profile finished");
-					}
-					break;
-
-			case PROFILE_DONE:
-					/*
-					 * Keep this state, set target temperature to the last step.
-					 */
-					previous_target_lo = unit->profile_inittemp_lo;
-					previous_target_hi = unit->profile_inittemp_hi;
-					previous_fridge_mode = unit->profile_fridge_mode;
-					for (step = unit->profile_steps; step; step = step->next) {
-					    if ((step->steptime + step->resttime) == 0)
-						break;
-					    previous_target_lo = step->target_lo;
-					    previous_target_hi = step->target_hi;
-					    previous_fridge_mode = step->fridge_mode;
-					}
-					unit->prof_target_lo = previous_target_lo;
-					unit->prof_target_hi = previous_target_hi;
-					unit->prof_fridge_mode = previous_fridge_mode;
-					unit->prof_percent = 100;
-					break;
-		    } /* switch */
-		} else {
-		    /*
-		     * Set some sane values
-		     */
-		    unit->prof_target_lo = 19.8;
-		    unit->prof_target_hi = 20.2;
-		    unit->prof_fridge_mode = 0;
-		}
-
-		/*
-		 * Manual switching
-		 */
-		if (unit->mode == UNITMODE_NONE) {
-		    device_out(unit->heater_address, unit->heater_state);
-		    device_out(unit->cooler_address, unit->cooler_state);
-		    device_out(unit->fan_address, unit->fan_state);
-		}
+		do_unit(unit, LCDunit, &seconds, &minutes);
 
-		/*
-		 * Usage counters
-		 */
-		if (unit->heater_address && unit->heater_state)
-		    unit->heater_usage++;
-		if (unit->cooler_address && unit->cooler_state)
-		    unit->cooler_usage++;
-		if (unit->fan_address && unit->fan_state)
-		    unit->fan_usage++;
-		if (unit->light_address && unit->light_state)
-		    unit->light_usage++;
-
-		/*
-		 * Interior lights
-		 */
-		if (unit->light_address) {
-		    if (unit->light_timer) {
-			unit->light_timer--;
-		    }
-		    if (unit->door_state && !unit->light_timer && unit->light_state) {
-			if (unit->light_wait > 0) {
-			    unit->light_wait--;
-			} else {
-			    unit->light_state = 0;
-			    syslog(LOG_NOTICE, "Unit `%s' lights On => Off", unit->alias);
-			    unit->mqtt_flag |= MQTT_FLAG_DATA;
-			}
-		    }
-		    if ((!unit->door_state || unit->light_timer) && !unit->light_state) {
-			unit->light_wait = unit->light_delay;   /* No delay to turn lights on   */
-			unit->light_state = 1;
-			unit->mqtt_flag |= MQTT_FLAG_DATA;
-			syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->alias);
-		    }
-		    device_out(unit->light_address, unit->light_state);
-		}
-
-	    	/*
-	    	 * Temperature control in this unit
-	    	 */
-		if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
-
-		    /*
-		     * Set both PID's to their input values.
-		     */
-		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
-		    if (unit->mode == UNITMODE_FRIDGE) {
-			unit->PID_cool->SetP = unit->fridge_set_hi;
-			unit->PID_heat->SetP = unit->fridge_set_lo;
-			unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
-			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
-		    } else if (unit->mode == UNITMODE_BEER) {
-			unit->PID_cool->SetP = unit->beer_set_hi;
-			unit->PID_heat->SetP = unit->beer_set_lo;
-			unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
-			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
-		    } else if (unit->mode == UNITMODE_PROFILE) {
-			double  usetemp;
-			unit->PID_cool->SetP = unit->prof_target_hi;
-			unit->PID_heat->SetP = unit->prof_target_lo;
-			/*
-			 * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
-			 */
-			usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
-			          ((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
-			unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
-			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
-		    }
-
-		    /*
-		     * PID controller compute, simulate 100 mSec loops.
-		     */
-		    for (int i = 0; i < 10; i++) {
-		    	UpdatePID(unit->PID_heat);
-		    	UpdatePID(unit->PID_cool);
-		    }
-
-		    /*
-		     * Logging
-		     */
-		    if (unit->heater_address) {
-			/*
-			 * Prevent extreme heating
-			 */
-			if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) {
-			    unit->PID_heat->OutP = 0.0;
-			}
-			if (seconds == 60) {
-			    syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
-				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP);
-			}
-		    } else {
-			unit->PID_heat->OutP = 0.0;
-		    }
-		    if (unit->cooler_address) {
-			/*
-			 * Prevent extreme cooling
-			 */
-			if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) < (unit->PID_cool->Input - 8.0))) {
-			    unit->PID_cool->OutP = 0.0;
-			}
-			/*
-			 * Prevent cooling if we use a chiller and the chiller temperature is not low enough.
-			 */
-			if (unit->chiller_address && (unit->chiller_state == 0)) {
-			    if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
-			    	unit->PID_cool->OutP = 0.0;
-				unit->alarm_flag |= ALARM_FLAG_CHILLER;
-				if (seconds == 60) {
-				    syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
-				}
-			    }
-			}
-		    	if (seconds == 60) {
-			    syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
-				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP);
-		    	}
-		    } else {
-			unit->PID_cool->OutP = 0.0;
-		    }
-
-		    /*
-		     * Deadlock, kill lowest value.
-		     */
-		    if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
-			if (unit->PID_cool->OutP > unit->PID_heat->OutP)
-			    unit->PID_heat->OutP = 0.0;
-			else
-			    unit->PID_cool->OutP = 0.0;
-		    }
-
-		    if (unit->heater_address && ! unit->cooler_state) {
-			if (unit->PID_heat->OutP >= 50) {
-			    if (unit->heater_wait < unit->heater_delay) {
-				unit->heater_wait++;
-			    } else {
-				int	power = round(unit->PID_heat->OutP);
-				if (unit->heater_state != power) {
-				    syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->alias, unit->heater_state, power);
-				    unit->heater_state = power;
-				    if (unit->heater_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-				}
-			    }
-			} else {
-			    if (unit->heater_wait > 0) {
-				unit->heater_wait--;
-			    } else {
-				if (unit->heater_state) {
-				    syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->alias);
-				    unit->heater_state = 0;
-				    if (unit->heater_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-				}
-			    }
-			}
-			if (unit->door_state) {
-			    device_out(unit->heater_address, unit->heater_state);
-			} else {
-			    device_out(unit->heater_address, 0);
-			}
-		    }
-
-		    if (unit->cooler_address && ! unit->heater_state) {
-			if (unit->PID_cool->OutP >= 50) {
-			    if (unit->cooler_wait < unit->cooler_delay) {
-				unit->cooler_wait++;
-			    } else {
-				int	power = round(unit->PID_cool->OutP); 
-				if (unit->cooler_state != power) {
-				    syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->alias, unit->cooler_state, power);
-				    unit->cooler_state = power;
-				    if (unit->cooler_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-				}
-			    }
-			} else {
-			    if (unit->cooler_wait > 0) {
-				unit->cooler_wait--;
-			    } else {
-				if (unit->cooler_state) {
-				    syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->alias);
-				    unit->cooler_state = 0;
-				    if (unit->cooler_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-				}
-			    }
-			}
-			if (unit->door_state) {
-			    device_out(unit->cooler_address, unit->cooler_state);
-			} else {
-			    device_out(unit->cooler_address, 0);
-			}
-		    }
-
-		    /*
-		     * If there is a fan, and the unit door is closed, and the unit should be doing
-		     * something, then turn on the global fan.
-		     * But if there is a chiller, do not turn it on if cooling.
-		     */
-		    if (unit->fan_address) {
-			if ((unit->door_state) && (unit->cooler_state == 0)) {
-			    if (unit->fan_wait < unit->fan_delay) {
-			    	unit->fan_wait++;
-			    } else {
-			    	if (! unit->fan_state) {
-				    syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->alias);
-				    unit->fan_state = 100;
-				    if (unit->fan_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-			    	}
-			    }
-			} else {
-			    if (unit->fan_wait > 0) {
-				unit->fan_wait--;
-			    } else {
-			    	if (unit->fan_state) {
-				    syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->alias);
-			    	    unit->fan_state = 0;
-				    if (unit->fan_address) {
-					unit->mqtt_flag |= MQTT_FLAG_DATA;
-				    }
-				}
-			    }
-			}
-			device_out(unit->fan_address, unit->fan_state);
-		    }
-
-		} else {
-		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
-		} /* fridge beer or profile mode */
-
-		/*
-		 * Now everything is set and done, update the LCD display
-		 */
-		LCDair = unit->air_temperature / 1000.0;
-		LCDbeer = unit->beer_temperature / 1000.0;
-		LCDstatC = LCDstatH = ' ';
-		if (unit->heater_address) {
-		    if (unit->heater_state) 
-			LCDstatH = '\6';
-		    else
-			LCDstatH = '\5';
-		}
-		if (unit->cooler_address) {
-		    if (unit->cooler_state) 
-			LCDstatC = '\4';
-		    else
-			LCDstatC = '\3';
-		}
-		LCDspH = LCDspL = 0.0;
-		if (unit->mode == UNITMODE_BEER) {
-		    LCDspH = unit->beer_set_hi;
-		    LCDspL = unit->beer_set_lo;
-		} else if (unit->mode == UNITMODE_FRIDGE) {
-		    LCDspH = unit->fridge_set_hi;
-		    LCDspL = unit->fridge_set_lo;
-		} else if (unit->mode == UNITMODE_PROFILE) {
-		    if (unit->prof_state != PROFILE_OFF) {
-			LCDspL = unit->prof_target_lo;
-			LCDspH = unit->prof_target_hi;
-		    }
-		}
-//		if ((seconds == 60) && ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE))) {
-//		    unit->mqtt_flag |= MQTT_FLAG_DATA;
-//		}
-		if (seconds == 60) {
-		    unit->mqtt_flag |= MQTT_FLAG_DATA;
-		}
-		pthread_mutex_lock(&mutexes[LOCK_LCD]);
-		/*
-		 * Write 4 rows to the LCD to display the unit state
-		 */
-		lcd_buf_write(row++, "Unit %d: %s          ", LCDunit, UNITMODE[unit->mode]);
-		lcd_buf_write(row++, "%s                   ", unit->product_name);
-		lcd_buf_write(row++, "%c%5.1f\2 A%6.2f\1    ", LCDstatC, LCDspH, LCDair);
-		lcd_buf_write(row++, "%c%5.1f\2 B%6.2f\1    ", LCDstatH, LCDspL, LCDbeer);
-		pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-
-		/*
-		 * Publish MQTT messages set in flag
-		 */
-		if (unit->mqtt_flag) {
-		    if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
-			publishDBirth(unit);
-			unit->mqtt_flag &= ~MQTT_FLAG_BIRTH;
-		    } else {
-			publishDData(unit);
-			unit->mqtt_flag &= ~MQTT_FLAG_DATA;
-		    }
-		    if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
-			publishDDeath(unit);
-			unit->mqtt_flag &= ~MQTT_FLAG_DEATH;
-		    }
-		}
-
-		/*
-		 * Handle changed alarms
-		 */
-		if (unit->alarm_flag != unit->alarm_last) {
-		    syslog(LOG_NOTICE, "Unit `%s' Alarm %d => %d", unit->alias, unit->alarm_last, unit->alarm_flag);
-		    unit->alarm_last = unit->alarm_flag;
-		}
 	    } /* for units */
 
 	    pthread_mutex_lock(&mutexes[LOCK_MENU]);

mercurial