thermferm/thermferm.c

Tue, 12 Aug 2014 13:09:50 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 12 Aug 2014 13:09:50 +0200
changeset 223
14700edd2a67
parent 221
91a5e7281c35
child 227
1cb55ea51f76
permissions
-rw-r--r--

Removed LCD shadow copy buffer.

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

extern int		debug;
extern sys_config	Config;
#ifdef HAVE_WIRINGPI_H
extern int		lcdHandle;
int			setupmenu = MENU_NONE;

#endif
int			lcdupdate;
#ifndef HAVE_WIRINGPI_H
pthread_t		threads[4];
#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 go_menu(int menu)
{
    lcdClear(lcdHandle);
    lcdPosition(lcdHandle, 0, 0);
    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:");
				break;

	case MENU_MODE_OFF:	lcdPuts(lcdHandle, "Set unit OFF");
				break;

	case MENU_MODE_NONE:	lcdPuts(lcdHandle, "Set unit NONE");
				break;

	case MENU_NONE_HEAT:	lcdPuts(lcdHandle, "Switch heater");
				break;

	case MENU_NONE_COOL:	lcdPuts(lcdHandle, "Switch cooler");
				break;

	case MENU_NONE_FAN:	lcdPuts(lcdHandle, "Switch Fan");
				break;

	case MENU_MODE_BEER:	lcdPuts(lcdHandle, "Set unit BEER");
				break;

	case MENU_BEER_TEMP:	lcdPuts(lcdHandle, "Set beer temp");
				break;

	case MENU_MODE_FRIDGE:	lcdPuts(lcdHandle, "Set unit FRIDGE");
				break;

	case MENU_FRIDGE_TEMP:	lcdPuts(lcdHandle, "Set fridge temp");
				break;

	case MENU_MODE_PROFILE:	lcdPuts(lcdHandle, "Set unit PROFILE");
				break;

	case MENU_PROFILE_SELECT:	lcdPuts(lcdHandle, "Select profile");
					break;
	
	case MENU_PROFILE_START:	lcdPuts(lcdHandle, "Start profile");
					break;
	
	case MENU_PROFILE_PAUSE:	lcdPuts(lcdHandle, "Pause profile");
					break;

	case MENU_PROFILE_ABORT:	lcdPuts(lcdHandle, "Abort profile");
					break;

	case MENU_PROFILE_RESUME:	lcdPuts(lcdHandle, "Resume profile");
					break;

	case MENU_PROFILE_GOOFF:	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;

	case MENU_SYS_MASH:	lcdPuts(lcdHandle, "Start Mash program");
				break;

	case MENU_SYS_THERMS:	lcdPuts(lcdHandle, "Start Thermometers");
				break;
    }
}



void stopLCD(void)
{
    lcdClear(lcdHandle);
    setBacklight(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) {
	    tmp = device->value + device->offset;
	    *val = tmp;
	    return device->present;
	}
    }

    return DEVPRESENT_NO;
}



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, "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));
			syslog(LOG_NOTICE, "Finished, rc=1");
#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];
    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;
    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

    /*
     * 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;
	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
    lcd_buf_write(1, (char *)"    ThermFerm   ");
    lcd_buf_write(2, (char *)" Version %s     ", VERSION);
#endif

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

    do {
	if (my_shutdown)
	    run = 0;

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

#ifdef HAVE_WIRINGPI_H
	    row = 3;
	    tm = localtime(&now);
	    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);
#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
		    lcd_buf_write(row++, "Room temp %.1f %cC    ", Config.temp_value / 1000.0, 0xdf);
#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
		    lcd_buf_write(row++, " Humidity %.1f %%    ", Config.hum_value / 1000.0, 0xdf);
#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
		lcd_buf_write(row++, "Unit %s              ", unit->name);
		lcd_buf_write(row++, "Mode %s              ", UNITMODE[unit->mode]);
#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
			    lcd_buf_write(row++, " Air %.3f %cC         ", unit->air_temperature / 1000.0, 0xdf);
#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
			    lcd_buf_write(row++, "Beer %.3f %cC         ", unit->beer_temperature / 1000.0, 0xdf);
#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.
		     */
		    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;
						break;
				case PROFILE_PAUSE:
						/*
						 * Keep current temperature, measure pause time. For
						 * temperature fall thru.
						 */
						unit->prof_paused++;
				case PROFILE_RUN:
						/*
						 * Calculate current profile step en desired temperature.
						 * When all done, set state to PROFILE_DONE.
						 */
						previous_target = profile->inittemp;
						time_until_now = 0;
						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);

						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\n", valid_step ? "TRUE":"FALSE");

						/*
						 * No more steps to do
						 */
						if (valid_step == FALSE) {
						    unit->prof_state = PROFILE_DONE;
						    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;
						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);
		}
#ifdef HAVE_WIRINGPI_H
		if (unit->heater_address && unit->cooler_address) {
		    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            ");
		    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     ");
		    }
		} else {
		    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");
		    }
		}
#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;

			/*
			 * 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 (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)
				unit->heater_state = 100;
			    else
				unit->heater_state = 0;
			    device_out(unit->heater_address, unit->heater_state);
			}
			if (unit->cooler_address) {
			    if (Out <= -2)
				unit->cooler_state = 100;
			    else
				unit->cooler_state = 0;
			    device_out(unit->cooler_address, unit->cooler_state);
			}
		    } else {
			err = 0.0;
			unit->PID_I_err = 0.0;
			unit->PID_err_old = 0.0;
		    }
		}
	    }

#ifdef HAVE_WIRINGPI_H
	    if (setupmenu == MENU_NONE)
	    	lcd_buf_show();
#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");

			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);
			}
			if (unit->cooler_address) {
			    snprintf(cooler, 39, "%d", unit->cooler_state);
			}
			if (unit->fan_address) {
			    snprintf(fan, 39, "%d", unit->fan_state);
			}
			if (unit->door_address) {
			    snprintf(door, 39, "%d", unit->door_state);
			}

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

		minutes++;
		if (minutes == 60)
		    minutes = 0;

		if ((minutes == 15) || (minutes == 45)) {
		    syslog(LOG_NOTICE, "minutes %d seconds %d", minutes, seconds);
		    wrconfig();
		}
	    }
	}

#ifdef HAVE_WIRINGPI_H
	/*
	 * Handle panel key events.
	 */
	key = keycheck();

	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);
			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)
			    go_menu(MENU_SYS_REBOOT);
			if (key == KEY_UP)
			    go_menu(MENU_SYS_THERMS);
			break;

	    case MENU_SYS_REBOOT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if (key == KEY_DOWN)
			    go_menu(MENU_SYS_MASH);
			if (key == KEY_UP)
			    go_menu(MENU_SYS_HALT);
			break;

	    case MENU_SYS_MASH:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if (key == KEY_DOWN)
			    go_menu(MENU_SYS_THERMS);
			if (key == KEY_UP)
			    go_menu(MENU_SYS_REBOOT);
			break;

	    case MENU_SYS_THERMS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if (key == KEY_DOWN)
			    go_menu(MENU_SYS_HALT);
			if (key == KEY_UP)
			    go_menu(MENU_SYS_MASH);
			break;

	}

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