thermferm/thermferm.c

Sun, 15 Feb 2015 18:06:21 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 15 Feb 2015 18:06:21 +0100
changeset 307
249e7d506069
parent 302
3d2bd47f35b4
child 308
876a8420c75a
permissions
-rw-r--r--

PID old error was not set to the previous value but was always zero.

/*****************************************************************************
 * Copyright (C) 2014
 *   
 * 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"


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;
	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_I_err = current_unit->PID_err_old = 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];
    time_t		now, last = (time_t)0;
    units_list		*unit;
    profiles_list	*profile;
    prof_step		*step;
    int			rc, run = 1, seconds = 0, minutes = 0, piddelay = 0, temp, deviation;
    int			run_seconds, run_minutes, run_hours, tot_minutes;
    float		err = 0.0, sp, pv, P_err, D_err, Out;
#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;
    }

    rc = devices_detect();
    if (rc) {
	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
    rc = piThreadCreate(my_panel_loop);
    if (rc) {
	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 = 0;
	unit->heater_wait = unit->cooler_wait = unit->fan_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

    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;
		    }
		}

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

#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
	    }

	    piddelay++;
	    if (piddelay == 15) {
		piddelay = 0;

		for (unit = Config.units; unit; unit = unit->next) {
		    if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
			/*
			 * PID controller
			 */
			sp = unit->beer_set;
			pv = unit->beer_temperature / 1000.0;
			if (unit->mode == UNITMODE_FRIDGE) {
				sp = unit->fridge_set;
				pv = unit->air_temperature / 1000.0;
			} else if (unit->mode == UNITMODE_PROFILE) {
				sp = unit->prof_target;
			}

//			unit->PID_err_old = err;
			err = sp - pv;
			if (err < unit->idle_rangeH && err > unit->idle_rangeL) {
			    err = 0;
			    unit->PID_I_err -= unit->PID_err_old;
			} else {
			    unit->PID_I_err += unit->PID_err_old;
			}
			/* Limit intergral error */
			if (unit->PID_I_err < -10.0)
			    unit->PID_I_err = -10.0;
			if (unit->PID_I_err > 10.0)
			    unit->PID_I_err = 10.0;
			P_err = err;
			D_err = err - unit->PID_err_old;
			unit->PID_err_old = err;

			/*
			 * A postive value means heating, a negative value cooling.
			 */
			Out = (10.0*P_err) + (0.1*unit->PID_I_err) + (5*D_err);
			//     Kp 0.1        Ki 0.3                   Kd 0.02
			if (err != 0.0) {
			    if (debug)
			    	fprintf(stdout, "sp=%.2f pv=%.2f err_old=%.2f err=%.2f P_err=%.2f I_err=%.2f D_err=%.2f Out=%.2f\n",
					sp, pv, unit->PID_err_old, err, P_err, unit->PID_I_err, D_err, Out);
			    syslog(LOG_NOTICE, "sp=%.2f pv=%.2f err_old=%.2f err=%.2f P_err=%.2f I_err=%.2f D_err=%.2f Out=%.2f",
					sp, pv, unit->PID_err_old, err, P_err, unit->PID_I_err, D_err, Out);
			}
			if (unit->heater_address) {
			    if (Out >= 2) {
				if (unit->heater_wait < unit->heater_delay) {
				    unit->heater_wait++;
				    syslog(LOG_NOTICE, "heater_wait + %d/%d", unit->heater_wait, unit->heater_delay);
				} else {
				    if (! unit->heater_state && ! unit->cooler_state) {
					syslog(LOG_NOTICE, "Heater Off => On");
					unit->heater_state = 100;
				    }
				}
			    } 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, "Heater On => Off");
					unit->heater_state = 0;
				    }
				}
			    }
			    device_out(unit->heater_address, unit->heater_state);
			}
			if (unit->cooler_address) {
			    if (Out <= -2) {
			    	if (unit->cooler_wait < unit->cooler_delay) {
				    unit->cooler_wait++;
				    syslog(LOG_NOTICE, "cooler_wait + %d/%d", unit->cooler_wait, unit->cooler_delay);
			    	} else {
				    if (! unit->cooler_state && ! unit->heater_state) {
				    	syslog(LOG_NOTICE, "Cooler Off => On");
					unit->cooler_state = 100;
				    }
				}
			    } 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, "Cooler On => Off");
					unit->cooler_state = 0;
				    }
				}
			    }
			    device_out(unit->cooler_address, unit->cooler_state);
			}
			if (unit->heater_address && unit->cooler_address && unit->fan_address) {
			    /*
			     * If the temperature difference between air and beer is more then
			     * xxx degrees, turn the fan on to make an airflow.
			     * Maybe, run the fan too if the heater is on because the heater in
			     * most cases will be some sort of radiating heat device.
			     * For cooling ??? dunno yet.
			     */
			    if (((unit->air_temperature - unit->beer_temperature) > 1000) ||
				((unit->air_temperature - unit->beer_temperature) < -1000)) {
				if (! unit->fan_state)
				    syslog(LOG_NOTICE, "Fan Off => On");
				unit->fan_state = 100;
			    } else {
				if (unit->fan_state)
				    syslog(LOG_NOTICE, "Fan On => Off");
				unit->fan_state = 0;
			    }
			    device_out(unit->fan_address, unit->fan_state);
			}
		    } else {
			err = 0.0;
			unit->PID_I_err = 0.0;
			unit->PID_err_old = 0.0;
		    }
		}
	    }

#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");


			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);
			}

			snprintf(buf, 1023, "%s,%.3f,%.3f,%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);
			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 = 0;
	unit->heater_wait = unit->cooler_wait = unit->fan_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);
	syslog(LOG_NOTICE, "Stopped unit %s 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