thermferm/thermferm.c

Sat, 16 May 2015 17:39:30 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 16 May 2015 17:39:30 +0200
changeset 362
c92651a54969
parent 361
308f6a436779
child 363
468ec0d96cce
permissions
-rw-r--r--

Made the client-server protocol more robust. When a change to a unit is made using the web interface, the main process is stopped during the update. Splitted the PID in two PID's, one for heating and one for cooling. Adjusted the web edit scrreen for this, but there are still rough edges. Replaced the PID code, maybe this one works better for our purpose. The simulator air temperature changes on the simulator heater and cooler, but it is not realistic at all. This is a development version, do not use in production. The version is 0.3.0

/*****************************************************************************
 * Copyright (C) 2014-2015
 *   
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of the mbsePi-apps
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * mbsePi-apps is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "lock.h"
#include "logger.h"
#include "rdconfig.h"
#include "devices.h"
#include "server.h"
#include "thermferm.h"
#include "simulator.h"
#include "lcd-pcf8574.h"
#include "lcd-buffer.h"
#include "panel.h"
#include "futil.h"
#include "xutil.h"
#include "pid.h"


int			my_shutdown = FALSE;
static pid_t		pgrp, mypid;
int			run_pause = FALSE;
int			run_hold = FALSE;

extern int		debug;
extern sys_config	Config;
#ifdef HAVE_WIRINGPI_H
extern int		lcdHandle;
int			setupmenu = MENU_NONE;
units_list		*current_unit = NULL;		/* In panel editor this points to the current unit. */
profiles_list		*current_profile = NULL;
float			temp_temp = 20.0;
#endif

#ifndef HAVE_WIRINGPI_H
pthread_t		threads[5];
#endif
extern const char       UNITMODE[5][8];
extern const char	PROFSTATE[4][6];


int  server(void);
void help(void);
void die(int);
#ifdef HAVE_WIRINGPI_H
void sendRCswitch(char *, int);
void stopLCD(void);
void stopRCswitch(void);
#endif



void help(void)
{
    fprintf(stdout, "mbsePi-apps thermferm v%s starting\n\n", VERSION);
    fprintf(stdout, "Usage: thermferm [-d] [-h]\n");
    fprintf(stdout, "  -d --debug              Debug and run in foreground\n");
    fprintf(stdout, "  -h --help               Display this help\n");
}



void die(int onsig)
{
    switch (onsig) {
	case SIGHUP:	syslog(LOG_NOTICE, "Got SIGHUP, shutting down");
			break;
	case SIGINT:	syslog(LOG_NOTICE, "Keyboard interrupt, shutting down");
			break;
	case SIGTERM:	syslog(LOG_NOTICE, "Got SIGTERM, shutting down");
			break;
	case SIGSEGV:	syslog(LOG_NOTICE, "Got SIGSEGV, shutting down");
			my_shutdown = TRUE;
			exit(SIGSEGV);
			break;
	default:	syslog(LOG_NOTICE, "die() on signal %d", onsig);
    }

    my_shutdown = TRUE;
}



#ifdef HAVE_WIRINGPI_H
void show_mode(void)
{
    char	buf[21];

    snprintf(buf, 20, "Old mode %s", UNITMODE[current_unit->mode]);
    lcdPuts(lcdHandle, buf);
    lcdPosition(lcdHandle, 0, 1);
}



void go_menu(int menu)
{
    char		buf[21];

    piLock(LOCK_LCD);
    piLock(LOCK_MENU);
    lcdClear(lcdHandle);
    lcdPosition(lcdHandle, 0, 0);
    syslog(LOG_NOTICE, "from menu %d to menu %d", setupmenu, menu);
    setupmenu = menu;

    switch (menu) {
	case MENU_NONE:		lcd_buf_show();
				break;

	case MENU_TOP_DEFAULT:	lcdPuts(lcdHandle, "Setup mode.");
				lcdPosition(lcdHandle, 0, 1);
				lcdPuts(lcdHandle, "Up&Down = Escape");
				break;

	case MENU_TOP_UNITS:	lcdPuts(lcdHandle, "Select units");
				break;

	case MENU_UNITS:	lcdPuts(lcdHandle, "Choose unit:");
				lcdPosition(lcdHandle, 0, 1);
				lcdPuts(lcdHandle, current_unit->name);
				break;

	case MENU_MODE_OFF:	show_mode();
				lcdPuts(lcdHandle, "New mode OFF");
				break;

	case MENU_MODE_NONE:	show_mode();
				lcdPuts(lcdHandle, "New mode NONE");
				break;

	case MENU_NONE_HEAT:	snprintf(buf, Config.lcd_cols, "Set heater %s", current_unit->heater_state ? "OFF":"ON");
				lcdPuts(lcdHandle, buf);
				break;

	case MENU_NONE_COOL:	snprintf(buf, Config.lcd_cols, "Set cooler %s", current_unit->cooler_state ? "OFF":"ON");
				lcdPuts(lcdHandle, buf);
				break;

	case MENU_NONE_FAN:	snprintf(buf, Config.lcd_cols, "Set fan %s", current_unit->fan_state ? "OFF":"ON");
				lcdPuts(lcdHandle, buf);
				break;

	case MENU_MODE_BEER:	show_mode();
				lcdPuts(lcdHandle, "New mode BEER");
				break;

	case MENU_BEER_TEMP:	lcdPuts(lcdHandle, "Set beer temp");
				lcdPosition(lcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
				lcdPuts(lcdHandle, buf);
				break;

	case MENU_MODE_FRIDGE:	show_mode();
				lcdPuts(lcdHandle, "New mode FRIDGE");
				break;

	case MENU_FRIDGE_TEMP:	lcdPuts(lcdHandle, "Set fridge temp");
				lcdPosition(lcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
				lcdPuts(lcdHandle, buf);
				break;

	case MENU_MODE_PROFILE:	show_mode();
				lcdPuts(lcdHandle, "New mode PROFILE");
				break;

	case MENU_PROFILE_SELECT:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Select profile");
					break;
	
	case MENU_PROFILE_START:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Start profile");
					break;
	
	case MENU_PROFILE_PAUSE:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Pause profile");
					break;

	case MENU_PROFILE_ABORT:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Abort profile");
					break;

	case MENU_PROFILE_RESUME:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Resume profile");
					break;

	case MENU_PROFILE_GOOFF:	snprintf(buf, Config.lcd_cols, "%s", current_profile->name);
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Set profile OFF");
					break;

	case MENU_TOP_SYS:	lcdPuts(lcdHandle, "System menu");
				break;

	case MENU_SYS_HALT:	lcdPuts(lcdHandle, "Halt system");
				break;

	case MENU_SYS_REBOOT:	lcdPuts(lcdHandle, "Reboot system");
				break;
    }

    piUnlock(LOCK_MENU);
    piUnlock(LOCK_LCD);
}



void stopLCD(void)
{
    piLock(LOCK_LCD);
    lcdClear(lcdHandle);
    setBacklight(0);
    piUnlock(LOCK_LCD);
}



/*
 * Change mode of current_unit
 */
void change_mode(int mode)
{
    if ((current_unit->mode == UNITMODE_OFF) && (mode != UNITMODE_OFF))
	initlog(current_unit->name);
    syslog(LOG_NOTICE, "Mode from %s to %s via panel interface", UNITMODE[current_unit->mode], UNITMODE[mode]);
    current_unit->mode = mode;
    /* Allways turn everything off after a mode change */
    current_unit->PID_iState = current_unit->PID_dState = 0.0;
    current_unit->heater_state = current_unit->cooler_state = current_unit->fan_state = 0;
    current_unit->heater_wait = current_unit->cooler_wait = current_unit->fan_wait = 0;
    device_out(current_unit->heater_address, current_unit->heater_state);
    device_out(current_unit->cooler_address, current_unit->cooler_state);
    device_out(current_unit->fan_address, current_unit->fan_state);
    if (current_unit->mode == UNITMODE_PROFILE) {
	/*
	 * Set a sane default until it will be overruled by the
	 * main processing loop.
	 */
	current_unit->prof_target = 20.0;
    }
}


#endif



int read_sensor(char *address, int *val)
{
    devices_list        *device;
    int                 tmp;

    for (device = Config.devices; device; device = device->next) {
	if (strcmp(address, device->uuid) == 0) {
#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_DEVICES);
#endif
	    tmp = device->value + device->offset;
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_DEVICES);
#endif
	    *val = tmp;
	    return device->present;
	}
    }

    return DEVPRESENT_NO;
}



/*
 * Handle panel key events
 */
#ifdef HAVE_WIRINGPI_H
void panel_key_events(int key)
{
    units_list		*unit;
    profiles_list	*profile;
    int			rc;

    switch (setupmenu) {
	case MENU_NONE:
		    	if ((key == KEY_DOWN) || (key == KEY_UP))
	    		    lcd_buf_step(key);
			if ((key == KEY_CONFIRM) && (setupmenu == MENU_NONE))
			    go_menu(MENU_TOP_DEFAULT);
			break;

	case MENU_TOP_DEFAULT:
	    		if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_SYS);
			break;

	case MENU_TOP_UNITS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_SYS);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_DEFAULT);
			if ((key == KEY_ENTER) && Config.units) {
			    /*
			     * Start with the first unit
			     */
			    current_unit = Config.units;
			    go_menu(MENU_UNITS);
			}
			break;

	case MENU_UNITS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_DOWN) {
			    if (current_unit->next) {
				current_unit = current_unit->next;
				go_menu(MENU_UNITS);
			    }
			}
			if (key == KEY_UP) {
			    for (unit = Config.units; unit; unit = unit->next) {
				if (unit->next && (unit->next == current_unit)) {
				    current_unit = unit;
				    go_menu(MENU_UNITS);
				    break;
				}
			    }
			}
			if (key == KEY_ENTER) {
			    /*
			     * Drop into the current mode
			     */
			    switch (current_unit->mode) {
				case UNITMODE_OFF:	go_menu(MENU_MODE_OFF);
							break;
				case UNITMODE_NONE:	go_menu(MENU_MODE_NONE);
							break;
				case UNITMODE_FRIDGE:	go_menu(MENU_MODE_FRIDGE);
							break;
				case UNITMODE_BEER:	go_menu(MENU_MODE_BEER);
							break;
				case UNITMODE_PROFILE:	go_menu(MENU_MODE_PROFILE);
							break;
			    }
			}
			break;

	case MENU_MODE_OFF:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_ENTER) {
			    change_mode(UNITMODE_OFF);
			    go_menu(MENU_MODE_OFF);
			}
			break;

	case MENU_MODE_NONE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_OFF);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_NONE)
				go_menu(MENU_NONE_HEAT);
			    else {
			    	change_mode(UNITMODE_NONE);
				go_menu(MENU_MODE_NONE);
			    }
			}
			break;

	case MENU_NONE_HEAT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_COOL);
			if (key == KEY_UP)
			    go_menu(MENU_NONE_FAN);
			if (key == KEY_ENTER) {
			    if (current_unit->heater_state)
				current_unit->heater_state = 0;
			    else
				current_unit->heater_state = 100;
			    go_menu(MENU_NONE_HEAT);
			}
			break;

	case MENU_NONE_COOL:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_FAN);
			if (key == KEY_UP)      
			    go_menu(MENU_NONE_HEAT);
			if (key == KEY_ENTER) {
			    if (current_unit->cooler_state)
				current_unit->cooler_state = 0;
			    else
				current_unit->cooler_state = 100;
			    go_menu(MENU_NONE_COOL);
			}
			break;

	case MENU_NONE_FAN:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_HEAT);
			if (key == KEY_UP)      
			    go_menu(MENU_NONE_COOL);
			if (key == KEY_ENTER) {
			    if (current_unit->fan_state)
				current_unit->fan_state = 0;
			    else
				current_unit->fan_state = 100;
			    go_menu(MENU_NONE_FAN);
			}
			break;

	case MENU_MODE_FRIDGE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_BEER);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_FRIDGE) {
				temp_temp = current_unit->fridge_set;
				go_menu(MENU_FRIDGE_TEMP);
			    } else {
			    	change_mode(UNITMODE_FRIDGE);
			    	go_menu(MENU_MODE_FRIDGE);
			    }
			}
			break;

	case MENU_FRIDGE_TEMP:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_FRIDGE_TEMP);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_FRIDGE_TEMP);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->fridge_set) {
				syslog(LOG_NOTICE, "Fridge temperature changed from %.1f to %.1f degrees from the panel", current_unit->fridge_set, temp_temp);
				current_unit->fridge_set = temp_temp;
			    }
			    go_menu(MENU_MODE_FRIDGE);
			}
			break;

	case MENU_MODE_BEER:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_BEER) {
				temp_temp = current_unit->beer_set;
				go_menu(MENU_BEER_TEMP);
			    } else {
			    	change_mode(UNITMODE_BEER);
			    	go_menu(MENU_MODE_BEER);
			    }
			}
			break;

	case MENU_BEER_TEMP:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_BEER);
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_BEER_TEMP);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_BEER_TEMP);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->beer_set) {
				syslog(LOG_NOTICE, "Beer temperature changed from %.1f to %.1f degrees from the panel", current_unit->beer_set, temp_temp);
				current_unit->beer_set = temp_temp;
			    }
			    go_menu(MENU_MODE_BEER);			    
			}
			break;

	case MENU_MODE_PROFILE:
			if (current_unit->profile) {
			    for (current_profile = Config.profiles; current_profile; current_profile = current_profile->next) {
				if (strcmp(current_profile->uuid, current_unit->profile) == 0)
				    break;
			    }
			} else {
			    current_profile = NULL;
			}
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_OFF);     
			if (key == KEY_UP)
			    go_menu(MENU_MODE_BEER);                                
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_PROFILE) {
				switch (current_unit->prof_state) {
				    case PROFILE_OFF:	if (current_unit->profile)
							    go_menu(MENU_PROFILE_START);
							else
					    		    go_menu(MENU_PROFILE_SELECT);
							break;
				    case PROFILE_PAUSE:	go_menu(MENU_PROFILE_RESUME);
							break;
				    case PROFILE_RUN:	go_menu(MENU_PROFILE_PAUSE);
							break;
				    case PROFILE_DONE:	go_menu(MENU_PROFILE_GOOFF);
							break;
				}
			    } else {
			    	change_mode(UNITMODE_PROFILE);
			    	go_menu(MENU_MODE_PROFILE);
			    }
			}
			break;

	case MENU_PROFILE_SELECT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_DOWN) {
			    if (current_profile->next) {
			    	current_profile = current_profile->next;
			    	go_menu(MENU_PROFILE_SELECT);
			    } else {
			    	go_menu(MENU_PROFILE_START);
			    }
			}
			if (key == KEY_UP) {
			    for (profile = Config.profiles; profile; profile = profile->next) {
				if (profile->next && profile->next == current_profile) {
				    current_profile = profile;
				    go_menu(MENU_PROFILE_SELECT);
				    break;
				}
			    }
			    go_menu(MENU_PROFILE_START);
			}
			if (key == KEY_ENTER) {
			    current_unit->profile = current_profile->uuid;
			    syslog(LOG_NOTICE, "Profile %s selected from panel", current_profile->name);
			    go_menu(MENU_PROFILE_START);
			}
			break;

	case MENU_PROFILE_START:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_PROFILE_SELECT);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_RUN;
			    current_unit->prof_started = time(NULL);
			    current_unit->prof_paused = current_unit->prof_primary_done = 0;
			    current_unit->prof_peak_abs = current_unit->prof_peak_rel = 0.0;
			    syslog(LOG_NOTICE, "Profile started from the panel");
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_PAUSE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP)) 
			    go_menu(MENU_PROFILE_ABORT);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_PAUSE;
			    syslog(LOG_NOTICE, "Profile pause from the panel");
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_ABORT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP)) {
			    if (current_unit->prof_state == PROFILE_PAUSE)
				go_menu(MENU_PROFILE_RESUME);
			    else if (current_unit->prof_state == PROFILE_RUN)
				go_menu(MENU_PROFILE_PAUSE);
			}
			if ((key == KEY_ENTER) && ((current_unit->prof_state == PROFILE_RUN) || (current_unit->prof_state == PROFILE_PAUSE))) {
			    current_unit->prof_state = PROFILE_OFF;
			    current_unit->prof_started = 0;
			    syslog(LOG_NOTICE, "Profile aborted from the panel");
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_RESUME:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_PROFILE_ABORT);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_RUN;
			    syslog(LOG_NOTICE, "Profile resume from the panel");
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_GOOFF:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_ENTER) {
			    if (current_unit->prof_state == PROFILE_DONE) {
				current_unit->prof_state = PROFILE_OFF;
				syslog(LOG_NOTICE, "Profile from done to off from the panel");
			    }
			}
			break;

	case MENU_TOP_SYS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_DEFAULT);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_ENTER)
			    go_menu(MENU_SYS_HALT);
			break;

	case MENU_SYS_HALT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_SYS_REBOOT);
			if (key == KEY_CONFIRM) {
			    rc = system("/sbin/halt");
			    syslog(LOG_NOTICE, "System halt from panel: /sbin/halt rc=%d", rc);
			    go_menu(MENU_NONE);
			}
			break;

	case MENU_SYS_REBOOT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_SYS_HALT);
			if (key == KEY_CONFIRM) {
			    rc = system("/sbin/reboot");
			    syslog(LOG_NOTICE, "System reboot from panel: /sbin/reboot rc=%d", rc);
			    go_menu(MENU_NONE);
			}
			break;
    }
}
#endif




int main(int argc, char *argv[])
{
    int		rc, c, i;
    pid_t	frk;

    while (1) {
	int option_index = 0;
	static struct option long_options[] = {
	    {"debug", 0, 0, 'c'},
	    {"help", 0, 0, 'h'},
	    {0, 0, 0, 0}
	};

	c = getopt_long(argc, argv, "dh", long_options, &option_index);
	if (c == -1)
	    break;

	switch (c) {
	    case 'd':	debug = TRUE;
			break;
	    case 'h':	help();
			return 1;
	}
    }

    openlog("thermferm", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER);
    syslog(LOG_NOTICE, "mbsePi-apps thermferm v%s starting", VERSION);
    if (debug)
	fprintf(stdout, "mbsePi-apps thermferm v%s starting\n", VERSION);

    if (rdconfig()) {
	fprintf(stderr, "Error reading configuration\n");
	syslog(LOG_NOTICE, "Error reading configuration: halted");
	return 1;
    }

    /*
     *  Catch all the signals we can, and ignore the rest. Note that SIGKILL can't be ignored
     *  but that's live. This daemon should only be stopped by SIGTERM.
     *  Don't catch SIGCHLD.
     */
    for (i = 0; i < NSIG; i++) {
    	if ((i != SIGCHLD) && (i != SIGKILL) && (i != SIGSTOP))
	    signal(i, (void (*))die);
    }

#ifdef HAVE_WIRINGPI_H
    if (wiringPiSetup () )
	return 1;

    if ((rc = initLCD (Config.lcd_cols, Config.lcd_rows))) {
	fprintf(stderr, "Cannot initialize LCD display, rc=%d\n", rc);
	return 1;
    }
#endif

    if (debug) {
	/*
	 * For debugging run in foreground.
	 */
	rc = server();
    } else {
	/*
	 * Server initialization is complete. Now we can fork the 
	 * daemon and return to the user. We need to do a setpgrp
	 * so that the daemon will no longer be assosiated with the
	 * users control terminal. This is done before the fork, so
	 * that the child will not be a process group leader. Otherwise,
	 * if the child were to open a terminal, it would become
	 * associated with that terminal as its control terminal.
	 */
	if ((pgrp = setpgid(0, 0)) == -1) {
	    syslog(LOG_NOTICE, "setpgpid failed");
	}

	frk = fork();
	switch (frk) {
	    case -1:	
		    	syslog(LOG_NOTICE, "Daemon fork failed: %s", strerror(errno));
#ifdef HAVE_WIRINGPI_H
			stopLCD();
#endif
			exit(1);
	    case 0:	/*
			 * Run the daemon
			 */
			fclose(stdin);
			if (open("/dev/null", O_RDONLY) != 0) {
			    syslog(LOG_NOTICE, "Reopen of stdin to /dev/null failed");
			    _exit(2);
			}
			fclose(stdout);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 1) {
			    syslog(LOG_NOTICE, "Reopen of stdout to /dev/null failed");
			    _exit(2);
			}
			fclose(stderr);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 2) {
			    syslog(LOG_NOTICE, "Reopen of stderr to /dev/null failed");
			    _exit(2);
			}
			mypid = getpid();
			rc = server();
			break;
			/* Not reached */
	    default:
			/*
			 * Here we detach this process and let the child
			 * run the deamon process.
			 */
			syslog(LOG_NOTICE, "Starting daemon with pid %d", frk);
			exit(0);
	}
    }

    syslog(LOG_NOTICE, "Finished, rc=%d", rc);
    return rc;
}



int server(void)
{
    char                buf[1024], *filename, target[40], heater[40], cooler[40], fan[40], door[40];
    char		use_heater[40], use_cooler[40], use_fan[40], room_temp[40];
    time_t		now, last = (time_t)0;
    units_list		*unit;
    profiles_list	*profile;
    prof_step		*step;
    int			rc, run = 1, seconds = 0, minutes = 0, temp, deviation;
    int			run_seconds, run_minutes, run_hours, tot_minutes;
#ifdef HAVE_WIRINGPI_H
    struct tm		*tm;
    int			row, key;
#else
    long		t = 0;
#endif
    int			current_step, valid_step, time_until_now;
    float		previous_target;


    if (lockprog((char *)"thermferm")) {
	syslog(LOG_NOTICE, "Can't lock");
	return 1;
    }

    if ((rc = devices_detect())) {
	syslog(LOG_NOTICE, "Detected %d new devices", rc);
	wrconfig();
    }

#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_devices_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_devices_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_devices_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_devices_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }

#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_server_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_server_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_server_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_server_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }

#ifdef HAVE_WIRINGPI_H
    if ((rc = piThreadCreate(my_panel_loop))) {
	fprintf(stderr, "my_panel_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_panel_loop thread didn't start rc=%d", rc);
    }
#endif


#ifdef USE_SIMULATOR
#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_simulator_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_simulator_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_simulator_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_simulator_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }
#endif

    /*
     * Initialize units for processing
     */
    for (unit = Config.units; unit; unit = unit->next) {
	/*
	 * Safety, turn everything off
	 */
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->light_state = 0;
	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
	if (unit->mode == UNITMODE_PROFILE) {
	    if (!unit->profile)
		syslog(LOG_NOTICE, "Starting unit `%s' in profile mode, no profile defined.", unit->name);
	    else
	    	syslog(LOG_NOTICE, "Starting unit `%s' in profile state %s.", unit->name, PROFSTATE[unit->prof_state]);
	} else if (unit->mode == UNITMODE_BEER) {
	    syslog(LOG_NOTICE, "Starting unit `%s' beer cooler at %.1f degrees", unit->name, unit->beer_set);
	} else if (unit->mode == UNITMODE_FRIDGE) {
	    syslog(LOG_NOTICE, "Starting unit `%s' as refridgerator at %.1f degrees", unit->name, unit->fridge_set);
	} else if (unit->mode == UNITMODE_NONE) {
	    syslog(LOG_NOTICE, "Starting unit `%s' in inactive state", unit->name);
	} else {
	    syslog(LOG_NOTICE, "Starting unit `%s' in off state", unit->name);
	}
    }

#ifdef HAVE_WIRINGPI_H
    piLock(LOCK_LCD);
    lcd_buf_write(1, (char *)"   ThermFerm    ");
    lcd_buf_write(2, (char *)" Version %s     ", VERSION);
    piUnlock(LOCK_LCD);
#endif

    /*
     * Initialize logfiles for each unit
     */
    for (unit = Config.units; unit; unit = unit->next) {
	if (unit->mode != UNITMODE_OFF) {
	    initlog(unit->name);
	}
    }

    do {
	if (my_shutdown)
	    run = 0;

	/*
	 * Use to stop processing units. Should be used when a unit is
	 * added or removed.
	 */
	if (run_pause) {
	    run_hold = TRUE;
	    syslog(LOG_NOTICE, "run_pause: entering hold state");
	    for (;;) {
		usleep(100000);
		if (! run_pause)
		    break;
	    }
	    syslog(LOG_NOTICE, "run_pause: leaving hold state");
	    run_hold = FALSE;
#ifdef HAVE_WIRINGPI_H
	    /*
	     * In case the LCD buffers were cleared, setup the first page.
	     */
	    piLock(LOCK_LCD);
	    lcd_buf_write(1, (char *)"   ThermFerm    ");
	    lcd_buf_write(2, (char *)" Version %s     ", VERSION);
	    piUnlock(LOCK_LCD);
#endif
	}

	now = time(NULL);
	if (now != last) {
	    /*
	     * Each second
	     */
	    last = now;
	    seconds++;

#ifdef HAVE_WIRINGPI_H
	    row = 3;
	    tm = localtime(&now);
	    piLock(LOCK_LCD);
	    lcd_buf_write(row++, "   %02d-%02d-%04d   ", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
	    lcd_buf_write(row++, "    %02d:%02d:%02d    ", tm->tm_hour, tm->tm_min, tm->tm_sec);
	    piUnlock(LOCK_LCD);
#endif

            if (Config.temp_address) {
		rc = read_sensor(Config.temp_address, &temp);
		if (rc == DEVPRESENT_YES) {
		    Config.temp_value = temp;
		    Config.temp_state = 0;
#ifdef HAVE_WIRINGPI_H
		    piLock(LOCK_LCD);
		    lcd_buf_write(row++, "Room temp %.1f %cC    ", Config.temp_value / 1000.0, 0xdf);
		    piUnlock(LOCK_LCD);
#endif
		} else if (rc == DEVPRESENT_ERROR) {
		    Config.temp_state = 1;
		} else {
		    Config.temp_state = 2;
		}
	    }

	    if (Config.hum_address) {
		rc = read_sensor(Config.hum_address, &temp);
		if (rc == DEVPRESENT_YES) {
		    Config.hum_value = temp;
		    Config.hum_state = 0;
#ifdef HAVE_WIRINGPI_H
		    piLock(LOCK_LCD);
		    lcd_buf_write(row++, " Humidity %.1f %%    ", Config.hum_value / 1000.0, 0xdf);
		    piUnlock(LOCK_LCD);
#endif
		} else if (rc == DEVPRESENT_ERROR) {
		    Config.hum_state = 1;
		} else {
		    Config.hum_state = 2;
		}
	    }

	    for (unit = Config.units; unit; unit = unit->next) {
#ifdef HAVE_WIRINGPI_H
		piLock(LOCK_LCD);
		lcd_buf_write(row++, "Unit %s              ", unit->name);
		lcd_buf_write(row++, "Mode %s              ", UNITMODE[unit->mode]);
		piUnlock(LOCK_LCD);
#endif

		if (unit->air_address) {
		    rc = read_sensor(unit->air_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			/*
			 * It is possible to have read errors or extreme values.
			 * This can happen with bad connections so we compare the
			 * value with the previous one. If the difference is too
			 * much, we don't send that value. That also means that if
			 * the next value is ok again, it will be marked invalid too.
			 * Maximum error is 40 degrees for now.
			 */
			deviation = 40000;
			if ((unit->air_temperature == 0) ||
			    (unit->air_temperature && (temp > (int)unit->air_temperature - deviation) && (temp < ((int)unit->air_temperature + deviation)))) {
			    unit->air_temperature = temp;
			    unit->air_state = 0;
#ifdef HAVE_WIRINGPI_H
			    piLock(LOCK_LCD);
			    lcd_buf_write(row++, " Air %.3f %cC         ", unit->air_temperature / 1000.0, 0xdf);
			    piUnlock(LOCK_LCD);
#endif
			} else {
			    syslog(LOG_NOTICE, "deviation error deviation=%d, old=%d new=%d", deviation, unit->air_temperature, temp);
			    if (debug) {
				fprintf(stdout, "deviation error deviation=%d, old=%d new=%d\n", deviation, unit->air_temperature, temp);
			    }
			}
		    } else if (rc == DEVPRESENT_ERROR) {
			unit->air_state = 1;
		    } else {
			unit->air_state = 2;
		    }
		}

		if (unit->beer_address) {
		    rc = read_sensor(unit->beer_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			deviation = 40000;
			if ((unit->beer_temperature == 0) ||
			    (unit->beer_temperature && (temp > (int)unit->beer_temperature - deviation) && (temp < ((int)unit->beer_temperature + deviation)))) {
			    unit->beer_temperature = temp;
			    unit->beer_state = 0;
#ifdef HAVE_WIRINGPI_H
			    piLock(LOCK_LCD);
			    lcd_buf_write(row++, "Beer %.3f %cC         ", unit->beer_temperature / 1000.0, 0xdf);
			    piUnlock(LOCK_LCD);
#endif
			} else {
			    syslog(LOG_NOTICE, "deviation error deviation=%d, old=%d new=%d", deviation, unit->beer_temperature, temp);
			    if (debug) {
				fprintf(stdout, "deviation error deviation=%d, old=%d new=%d\n", deviation, unit->beer_temperature, temp);
			    }
			}
		    } else if (rc == DEVPRESENT_ERROR) {
			unit->beer_state = 1;
		    } else {
			unit->beer_state = 2;
		    }
		}

		if (unit->door_address) {
		    rc = read_sensor(unit->door_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (temp) {
			    if (unit->door_state == 0) {
			    	syslog(LOG_NOTICE, "Unit `%s' door closed", unit->name);
			    	unit->door_state = 1;
			    }
			} else {
			    if (unit->door_state) {
			    	syslog(LOG_NOTICE, "Unit `%s' door opened", unit->name);
			    	unit->door_state = 0;
			    }
			}
		    } else {
			unit->door_state = 1;
		    }
		} else {
		    /*
		     * No door switch, say door is closed.
		     */
		    unit->door_state = 1;
		}

		if (unit->psu_address) {
		    rc = read_sensor(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->name);
				unit->psu_state = 1;
			    }
			} else {
			    if (unit->psu_state) {
				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->name);
				unit->psu_state = 0;
			    }
			}
		    } else {
			unit->psu_state = 1;
		    }
		} else {
		    unit->psu_state = 1;
		}

		/*
		 * Handle profile
		 */
		if ((unit->mode == UNITMODE_PROFILE) && (unit->profile)) {
		    /*
		     * unit->profile		- uuid of the selected profile.
		     * 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.
		     */
		    for (profile = Config.profiles; profile; profile = profile->next) {
			if (strcmp(unit->profile, profile->uuid) == 0) {

			    switch (unit->prof_state) {
				case PROFILE_OFF:
						unit->prof_target = profile->inittemp;
						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 = profile->inittemp;
						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)", profile->name);
							}
						    } else {
						    	/*
						    	 * This method works if the unit has no cooling or if the profile allowd 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)", profile->name);
						    	}
						    }
						}

						/*
						 * See how long this profile will take
						 */
						tot_minutes = 0;
						for (step = profile->steps; step; step = step->next) {
						    tot_minutes += ((step->steptime + step->resttime) * 60);
						}

						valid_step = FALSE;
						for (step = 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 (debug)
							    fprintf(stdout, "step=%d step_pos=%d step=%d/%d target=%.1f ", 
									    current_step, run_hours - time_until_now,
									    step->steptime, step->resttime, step->target);
							if ((run_hours - time_until_now) < step->steptime) {
							    unit->prof_target = previous_target + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target - previous_target));
							    if (debug)
								fprintf(stdout, "tempshift=%.1f  minutes=%d duration=%d temp_move=%.3f ", 
										step->target - previous_target, run_minutes - (time_until_now * 60), 
										step->steptime * 60, unit->prof_target);
							} else {
							    unit->prof_target = step->target;
							    fprintf(stdout, "resting target=%.1f ", step->target);
							}
							break;
						    }
						    time_until_now += step->steptime + step->resttime;
						    previous_target = step->target;
						}
						if (debug)
						    fprintf(stdout, " %s %02d:%02d\n", valid_step ? "TRUE":"FALSE", minutes, seconds);

						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, target %.3f degrees", 
								profile->name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
								unit->prof_percent, unit->prof_target);
						    }
						} else {
						    /*
						     * No more steps to do
						     */
						    unit->prof_state = PROFILE_DONE;
						    unit->prof_percent = 100;
						    syslog(LOG_NOTICE, "Profile `%s' is done", profile->name);
						}
						break;

				case PROFILE_DONE:
						/*
						 * Keep this state, set target temperature to the last step.
						 */
						previous_target = profile->inittemp;
						for (step = profile->steps; step; step = step->next) {
						    if (step->steptime == 0)
							break;
						    previous_target = step->target;
						}
						unit->prof_target = previous_target;
						unit->prof_percent = 100;
						break;
			    } /* switch */
			}
		    }
		}

		/*
		 * 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++;

#ifdef HAVE_WIRINGPI_H
		if (unit->heater_address && unit->cooler_address) {
		    piLock(LOCK_LCD);
		    if (unit->heater_state) {
			lcd_buf_write(row++, "Heater On          ");
		    } else if (unit->cooler_state) {
			lcd_buf_write(row++, "Cooler On          ");
		    } else {
			lcd_buf_write(row++, "Standby            ");
		    }
		    piUnlock(LOCK_LCD);
		    piLock(LOCK_LCD);
		    switch (unit->mode) {
			case UNITMODE_BEER:	lcd_buf_write(row++, "Target %.1f %cC     ", unit->beer_set, 0xdf);
						break;
			case UNITMODE_FRIDGE:	lcd_buf_write(row++, "Target %.1f %cC     ", unit->fridge_set, 0xdf);
						break;
			case UNITMODE_PROFILE:	if (unit->prof_state != PROFILE_OFF)
						    lcd_buf_write(row++, "Target %.1f %cC     ", unit->prof_target, 0xdf);
						else
						    lcd_buf_write(row++, "Target not set     ");
						break;
			default:		lcd_buf_write(row++, "Target not set     ");
		    }
		    piUnlock(LOCK_LCD);
		} else {
		    piLock(LOCK_LCD);
		    if (unit->heater_address) {
		    	lcd_buf_write(row++, "Heat %s            ", unit->heater_state ? "On ":"Off");
		    }
		    if (unit->cooler_address) {
		    	lcd_buf_write(row++, "Cool %s            ", unit->cooler_state ? "On ":"Off");
		    }
		    piUnlock(LOCK_LCD);
		}
#endif

		/*
		 * Interior lights
		 */
		if (unit->light_address) {
		    if (unit->door_state  && 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->name);
			}
		    }
		    if (!unit->door_state && !unit->light_state) {
			unit->light_wait = unit->light_delay;   /* No delay to turn lights on   */
			unit->light_state = 1;
			syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->name);
		    }
		    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->PID_heat->SetP = unit->fridge_set;
			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->PID_heat->SetP = unit->beer_set;
			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) {
			unit->PID_cool->SetP = unit->PID_heat->SetP = unit->prof_target;
			unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
		    }

		    /*
		     * PID controller compute
		     */
		    if (unit->heater_address) {
			UpdatePID(unit->PID_heat);

			if (debug)
			    fprintf(stdout, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n",
				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP);
			if (((unit->PID_heat->OutP >= 1) && unit->heater_address) || (seconds == 60) || unit->heater_state) {
			    syslog(LOG_NOTICE, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f",
				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP);
			}
		    }
		    if (unit->cooler_address) {
		    	UpdatePID(unit->PID_cool);

		    	if (debug)
			    fprintf(stdout, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n",
				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP);
		    	if (((unit->PID_cool->OutP >= 1) && unit->cooler_address) || (seconds == 60) || unit->cooler_state) {
			    syslog(LOG_NOTICE, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f",
				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP);
		    	}
		    }

		    if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
			syslog(LOG_NOTICE, "Heat and Cool lockdown");
			unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
		    }

		    if (unit->heater_address && ! unit->cooler_state) {
			if (unit->PID_heat->OutP >= 1) {
			    if (unit->heater_wait < unit->heater_delay) {
				unit->heater_wait++;
//				syslog(LOG_NOTICE, "heater_wait + %d/%d", unit->heater_wait, unit->heater_delay);
			    } else {
				int	power = round(unit->PID_heat->OutP);
				if (unit->heater_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->name, unit->heater_state, power);
				    unit->heater_state = power;
				}
			    }
			} else {
			    if (unit->heater_wait > 0) {
				unit->heater_wait--;
//				syslog(LOG_NOTICE, "heater_wait - %d/%d", unit->heater_wait, unit->heater_delay);
			    } else {
				if (unit->heater_state) {
				    syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->name);
				    unit->heater_state = 0;
				}
			    }
			}
			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 >= 1) {
			    if (unit->cooler_wait < unit->cooler_delay) {
				unit->cooler_wait++;
//				syslog(LOG_NOTICE, "cooler_wait + %d/%d", unit->cooler_wait, unit->cooler_delay);
			    } else {
				int	power = round(unit->PID_cool->OutP); 
				if (unit->cooler_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->name, unit->cooler_state, power);
				    unit->cooler_state = power;
				}
			    }
			} else {
			    if (unit->cooler_wait > 0) {
				unit->cooler_wait--;
//				syslog(LOG_NOTICE, "cooler_wait - %d/%d", unit->cooler_wait, unit->cooler_delay);
			    } else {
				if (unit->cooler_state) {
				    syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->name);
				    unit->cooler_state = 0;
				}
			    }
			}
			if (unit->door_state)
			    device_out(unit->cooler_address, unit->cooler_state);
			else
			    device_out(unit->cooler_address, 0);
		    }

		    if ((unit->heater_address || unit->cooler_address) && unit->fan_address) {
			/*
			 * If there is a heater or cooler and we have a fan, turn fan on if
			 * cooling or heating. The Fan has a start/stop delay.
			 */
			if ((unit->heater_address && unit->heater_state) || (unit->cooler_address && unit->cooler_state)) {
			    if (unit->fan_wait < unit->fan_delay) {
				unit->fan_wait++;
			    } else {
				if (! unit->fan_state) {
				    syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->name);
			    	    unit->fan_state = 100;
				}
			    }
			} else {
			    if (unit->fan_wait > 0) {
				unit->fan_wait--;
			    } else {
			    	if (unit->fan_state) {
				    syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->name);
			    	    unit->fan_state = 0;
				}
			    }
			}
			if (unit->door_state)
			    device_out(unit->fan_address, unit->fan_state);
			else
			    device_out(unit->fan_address, 0);
		    }
		} else {
		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
		} /* fridge beer or profile mode */
	    } /* for units */

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_MENU);
	    if (setupmenu == MENU_NONE) {
		piLock(LOCK_LCD);
	    	lcd_buf_show();
		piUnlock(LOCK_LCD);
	    }
	    piUnlock(LOCK_MENU);
#endif

	    if (seconds == 60) {
		seconds = 0;

		/*
		 * Log temperature and status every minute if unit is active.
		 */
		for (unit = Config.units; unit; unit = unit->next) {
		    if (unit->mode != UNITMODE_OFF) {

			snprintf(target, 39, "NA");
			snprintf(heater, 39, "NA");
			snprintf(cooler, 39, "NA");
			snprintf(fan, 39, "NA");
			snprintf(door, 39, "NA");
			snprintf(use_heater, 39, "NA");
			snprintf(use_cooler, 39, "NA");
			snprintf(use_fan, 39, "NA");
			snprintf(room_temp, 39, "NA");

			if (unit->mode == UNITMODE_BEER)
			    snprintf(target, 39, "%.1f", unit->beer_set);
			else if (unit->mode == UNITMODE_FRIDGE)
			    snprintf(target, 39, "%.1f", unit->fridge_set);
			else if (unit->mode == UNITMODE_PROFILE)
			    snprintf(target, 39, "%.1f", unit->prof_target);

			if (unit->heater_address) {
			    snprintf(heater, 39, "%d", unit->heater_state);
			    snprintf(use_heater, 39, "%d", unit->heater_usage);
			}
			if (unit->cooler_address) {
			    snprintf(cooler, 39, "%d", unit->cooler_state);
			    snprintf(use_cooler, 39, "%d", unit->cooler_usage);
			}
			if (unit->fan_address) {
			    snprintf(fan, 39, "%d", unit->fan_state);
			    snprintf(use_fan, 39, "%d", unit->fan_usage);
			}
			if (unit->door_address) {
			    snprintf(door, 39, "%d", unit->door_state);
			}
			if (Config.temp_address) {
			    snprintf(room_temp, 39, "%.3f", Config.temp_value / 1000.0);
			}

			snprintf(buf, 1023, "%s,%.3f,%.3f,%s,%s,%s,%s,%s,%s,%s,%s,%s", 
					UNITMODE[unit->mode], unit->air_temperature / 1000.0, 
					unit->beer_temperature / 1000.0, target, heater, cooler, fan, door, use_heater, use_cooler, use_fan, room_temp);
			filename = xstrcpy(unit->name);
			filename = xstrcat(filename, (char *)".log");
			logger(filename, buf);
			free(filename);
			filename = NULL;
		    }
		}

		minutes++;
		if (minutes == 60) {
		    minutes = 0;
		    /*
		     * Log usage counters every hour
		     */
		    for (unit = Config.units; unit; unit = unit->next) {
		    	syslog(LOG_NOTICE, "Unit `%s' usage heater=%d cooler=%d fan=%d", unit->name, unit->heater_usage, unit->cooler_usage, unit->fan_usage);
		    }
		}

		/*
		 * Save the configuration each half hour.
		 */
		if ((minutes == 15) || (minutes == 45))
		    wrconfig();
	    }
	}

#ifdef HAVE_WIRINGPI_H
	key = keycheck();
	if (key != KEY_NONE)
	    panel_key_events(key);
#endif

	usleep(100000);

    } while (run);

    /*
     * Stop units processing in a neat way
     */
    for (unit = Config.units; unit; unit = unit->next) {
	/*
	 * Turn everything off
	 */
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->light_state = 0;
	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
	device_out(unit->heater_address, unit->heater_state);
	device_out(unit->cooler_address, unit->cooler_state);
	device_out(unit->fan_address, unit->fan_state);
	device_out(unit->light_address, unit->light_state);
	syslog(LOG_NOTICE, "Unit `%s' stopped in mode %s", unit->name, UNITMODE[unit->mode]);
    }

    if (debug)
	fprintf(stdout, (char *)"Out of loop\n");

    /*
     * Give threads time to cleanup
     */
    usleep(1500000);

#ifdef HAVE_WIRINGPI_H
    stopLCD();
#endif

    wrconfig();

    ulockprog((char *)"thermferm");

    if (debug)
	fprintf(stdout, "Goodbye\n");

    return 0;
}

mercurial