thermferm/thermferm.c

Thu, 17 Sep 2015 20:54:34 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 17 Sep 2015 20:54:34 +0200
changeset 406
44566f986f76
parent 405
0ad4cb5f4afa
child 418
0bfe08c7ba6e
permissions
-rw-r--r--

Fixed compiling on a real RPi.

/*****************************************************************************
 * 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_cool->OutP = current_unit->PID_heat->OutP = 0.0;
    current_unit->PID_cool->Mode = current_unit->PID_heat->Mode = PID_MODE_NONE;
    current_unit->heater_state = current_unit->cooler_state = current_unit->fan_state = current_unit->light_state = 0;
    current_unit->heater_wait = current_unit->cooler_wait = current_unit->fan_wait = current_unit->light_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);
    device_out(current_unit->light_address, current_unit->light_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_lo = 19.8;
	current_unit->prof_target_hi = 20.2;
	current_unit->prof_fridge_mode = 0;
    }
}


#endif



/*
 * 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_lo[40], target_hi[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, previous_fridge_mode;
    float		previous_target_lo, previous_target_hi;


    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 = device_in(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 = device_in(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 = device_in(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 = device_in(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 = device_in(unit->door_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (temp) {
			    if (unit->door_state == 0) {
			    	syslog(LOG_NOTICE, "Unit `%s' door closed", unit->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 = device_in(unit->psu_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (temp) {
			    if (unit->psu_state == 0) {
				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->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) {

			    /*
			     * Safe defaults
			     */
			    unit->prof_target_lo = profile->inittemp_lo;
			    unit->prof_target_hi = profile->inittemp_hi;
			    unit->prof_fridge_mode = 0;

			    switch (unit->prof_state) {
				case PROFILE_OFF:
						unit->prof_percent = 0;
						break;
				case PROFILE_PAUSE:
						/*
						 * Keep current temperature, measure pause time. For
						 * temperature fall thru.
						 */
						unit->prof_paused++;
				case PROFILE_RUN:
						/*
						 * Calculate current profile step and desired temperature.
						 * When all steps are done, set state to PROFILE_DONE.
						 */
						previous_target_lo = profile->inittemp_lo;
						previous_target_hi = profile->inittemp_hi;
						previous_fridge_mode = profile->fridge_mode;
						time_until_now = current_step = 0;
						run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
						run_minutes = run_seconds / 60;
						run_hours = run_minutes / 60;
						if (debug)
						    fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);

						/*
						 * Primary fermentation tests
						 */
						if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
						    unit->prof_peak_abs = unit->beer_temperature / 1000.0;
						if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
						    unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
						if (unit->prof_primary_done == 0) {
						    if (unit->cooler_address) {
							/*
							 * There is a cooler. If the difference between the beer and air temperature
							 * drops we assume the primary fermentation is done.
							 */
							if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
							    unit->prof_primary_done = time(NULL);
							    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", 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;
//							unit->prof_fridge_mode = step->fridge_mode;
							if (debug)
							    fprintf(stdout, "step=%d step_pos=%d step=%d/%d target=%.1f..%.1f ", 
									    current_step, run_hours - time_until_now,
									    step->steptime, step->resttime, step->target_lo, step->target_hi);
							if ((run_hours - time_until_now) < step->steptime) {
							    unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
							    unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
							    if (step->fridge_mode > previous_fridge_mode) {
							    	unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
							    } else if (step->fridge_mode < previous_fridge_mode) {
								unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
							    } else {
								unit->prof_fridge_mode = step->fridge_mode;
							    }
							    if (debug)
								fprintf(stdout, "tempshift=%.1f..%.1f  minutes=%d duration=%d temp_move=%.3f..%.3f ", 
										step->target_lo - previous_target_lo,
										step->target_hi - previous_target_hi,
										run_minutes - (time_until_now * 60), 
										step->steptime * 60, unit->prof_target_lo, unit->prof_target_hi);
							} else {
							    unit->prof_target_lo = step->target_lo;
							    unit->prof_target_hi = step->target_hi;
							    unit->prof_fridge_mode = step->fridge_mode;
							    if (debug)
							    	fprintf(stdout, "resting target=%.1f..%.1f ", step->target_lo, step->target_hi);
							}
							break;
						    }
						    time_until_now += step->steptime + step->resttime;
						    previous_target_lo = step->target_lo;
						    previous_target_hi = step->target_hi;
						    previous_fridge_mode = step->fridge_mode;
						}
						if (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 %s %.3f..%.3f degrees", 
								profile->name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
								unit->prof_percent, unit->prof_fridge_mode ? (char *)"air":(char *)"beer",
								unit->prof_target_lo, unit->prof_target_hi);
						    }
						} 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_lo = profile->inittemp_lo;
						previous_target_hi = profile->inittemp_hi;
						previous_fridge_mode = profile->fridge_mode;
						for (step = profile->steps; step; step = step->next) {
						    if ((step->steptime + step->resttime) == 0)
							break;
						    previous_target_lo = step->target_lo;
						    previous_target_hi = step->target_hi;
						    previous_fridge_mode = step->fridge_mode;

						}
						unit->prof_target_lo = previous_target_lo;
						unit->prof_target_hi = previous_target_hi;
						unit->prof_fridge_mode = previous_fridge_mode;
						unit->prof_percent = 100;
						break;
			    } /* switch */
			}
		    }
		} else {
		    /*
		     * Set some sane values
		     */
		    unit->prof_target_lo = 19.8;
		    unit->prof_target_hi = 20.2;
		    unit->prof_fridge_mode = 0;
		}

		/*
		 * Manual switching
		 */
		if (unit->mode == UNITMODE_NONE) {
		    device_out(unit->heater_address, unit->heater_state);
		    device_out(unit->cooler_address, unit->cooler_state);
		    device_out(unit->fan_address, unit->fan_state);
		}

		/*
		 * Usage counters
		 */
		if (unit->heater_address && unit->heater_state)
		    unit->heater_usage++;
		if (unit->cooler_address && unit->cooler_state)
		    unit->cooler_usage++;
		if (unit->fan_address && unit->fan_state)
		    unit->fan_usage++;
		if (unit->light_address && unit->light_state)
		    unit->light_usage++;

#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++, "Tgt %.1f..%.1f %cC   ", unit->prof_target_lo, unit->prof_target_hi, 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->fridge_set;
			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->beer_set;
			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) {
			double  usetemp;
			unit->PID_cool->SetP = unit->prof_target_hi;
			unit->PID_heat->SetP = unit->prof_target_lo;
			/*
			 * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
			 */
			usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
			          ((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
			if (debug)
			    fprintf(stdout, " fridge_mode=%d measured=%.3f %.3f => %.3f\n", unit->prof_fridge_mode,
					    unit->air_temperature / 1000.0, unit->beer_temperature / 1000.0, usetemp);
			unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
		    }

		    /*
		     * PID controller compute
		     */
		    UpdatePID(unit->PID_heat);
		    UpdatePID(unit->PID_cool);

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

		    /*
		     * Deadlock
		     */
		    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 >= 2) {
			    if (unit->heater_wait < unit->heater_delay) {
				unit->heater_wait++;
			    } else {
				int	power = round(unit->PID_heat->OutP);
				if (unit->heater_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->name, unit->heater_state, power);
				    unit->heater_state = power;
				}
			    }
			} else {
			    if (unit->heater_wait > 0) {
				unit->heater_wait--;
			    } 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 >= 2) {
			    if (unit->cooler_wait < unit->cooler_delay) {
				unit->cooler_wait++;
			    } else {
				int	power = round(unit->PID_cool->OutP); 
				if (unit->cooler_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->name, unit->cooler_state, power);
				    unit->cooler_state = power;
				}
			    }
			} else {
			    if (unit->cooler_wait > 0) {
				unit->cooler_wait--;
			    } 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_lo, 39, "NA");
			snprintf(target_hi, 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_lo, 39, "%.1f", unit->beer_set);
			    snprintf(target_hi, 39, "%.1f", unit->beer_set);
			} else if (unit->mode == UNITMODE_FRIDGE) {
			    snprintf(target_lo, 39, "%.1f", unit->fridge_set);
			    snprintf(target_hi, 39, "%.1f", unit->fridge_set);
			} else if (unit->mode == UNITMODE_PROFILE) {
			    snprintf(target_lo, 39, "%.1f", unit->prof_target_lo);
			    snprintf(target_hi, 39, "%.1f", unit->prof_target_hi);
			}

			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,%s", 
					UNITMODE[unit->mode], unit->air_temperature / 1000.0, 
					unit->beer_temperature / 1000.0, 
					target_lo, heater, cooler, fan, door, use_heater, use_cooler, use_fan, room_temp, target_hi);
			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");
    return 0;
}

mercurial