Brewco first phase development configuration structure.

Wed, 25 Nov 2015 22:49:35 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 25 Nov 2015 22:49:35 +0100
changeset 434
eb724767860d
parent 433
1421ece29197
child 435
4b1ed6897d80

Brewco first phase development configuration structure.

brewco/Makefile file | annotate | diff | comparison | revisions
brewco/brewco.c file | annotate | diff | comparison | revisions
brewco/brewco.h file | annotate | diff | comparison | revisions
brewco/devices.c file | annotate | diff | comparison | revisions
brewco/devices.h file | annotate | diff | comparison | revisions
brewco/lcd-pcf8574.c file | annotate | diff | comparison | revisions
brewco/lcd-pcf8574.h file | annotate | diff | comparison | revisions
brewco/lock.c file | annotate | diff | comparison | revisions
brewco/lock.h file | annotate | diff | comparison | revisions
brewco/pid.c file | annotate | diff | comparison | revisions
brewco/pid.h file | annotate | diff | comparison | revisions
brewco/rdconfig.c file | annotate | diff | comparison | revisions
brewco/slcd.c file | annotate | diff | comparison | revisions
brewco/slcd.h file | annotate | diff | comparison | revisions
--- a/brewco/Makefile	Wed Nov 25 16:39:25 2015 +0100
+++ b/brewco/Makefile	Wed Nov 25 22:49:35 2015 +0100
@@ -54,8 +54,13 @@
 
 # DO NOT DELETE THIS LINE - MAKE DEPEND RELIES ON IT
 # Dependencies generated by make depend
+slcd.o: brewco.h slcd.h futil.h xutil.h
+devices.o: brewco.h devices.h xutil.h
 futil.o: brewco.h futil.h
-brewco.o: rdconfig.h brewco.h futil.h xutil.h
+brewco.o: rdconfig.h brewco.h futil.h xutil.h lcd-pcf8574.h slcd.h lock.h devices.h
+lock.o: lock.h brewco.h
+lcd-pcf8574.o: brewco.h lcd-pcf8574.h slcd.h
+pid.o: brewco.h pid.h
 xutil.o: brewco.h xutil.h
 rdconfig.o: rdconfig.h brewco.h futil.h xutil.h
 # End of generated dependencies
--- a/brewco/brewco.c	Wed Nov 25 16:39:25 2015 +0100
+++ b/brewco/brewco.c	Wed Nov 25 22:49:35 2015 +0100
@@ -24,13 +24,20 @@
 #include "brewco.h"
 #include "futil.h"
 #include "xutil.h"
+#include "lcd-pcf8574.h"
+#include "slcd.h"
+#include "lock.h"
+#include "devices.h"
 
 
 int			my_shutdown = FALSE;
 extern int              debug;
 extern sys_config       Config;
-#ifdef HAVE_WIRINGPI_H
 extern int              lcdHandle;
+extern int		slcdHandle;
+
+#ifndef HAVE_WIRINGPI_H
+pthread_t               threads[5];
 #endif
 
 
@@ -70,6 +77,65 @@
 
 
 
+int server(void);
+int server(void)
+{
+    int 	rc = 0;
+#ifndef HAVE_WIRINGPI_H
+    long	t = 0;
+#endif
+
+//    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
+    }
+
+    /*
+     * 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;
+//    }
+
+#ifdef HAVE_WIRINGPI_H
+        piLock(LOCK_LCD);
+	lcdPosition(lcdHandle, 0, 0);
+        lcdPrintf(lcdHandle, "    Brewco %s", VERSION);
+#endif
+	slcdPosition(slcdHandle, 0, 0);
+	slcdPrintf(slcdHandle, "    Brewco %s", VERSION);
+#ifdef HAVE_WIRINGPI_H
+	piUnlock(LOCK_LCD);
+#endif
+
+    return rc;
+}
+
+
+
 int main(int argc, char *argv[])
 {
     int		rc = 0, c, i;
@@ -118,17 +184,19 @@
 #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 ((rc = initLCD (Config.lcd_cols, Config.lcd_rows))) {
+	fprintf(stderr, "Cannot initialize LCD display, rc=%d\n", rc);
+	return 1;
+    }
+
+    rc = server();
 
     syslog(LOG_NOTICE, "Finished, rc=%d", rc);
+    if (debug)
+	fprintf(stdout, "Finished, rc=%d\n", rc);
     return rc;
 }
 
 
-
--- a/brewco/brewco.h	Wed Nov 25 16:39:25 2015 +0100
+++ b/brewco/brewco.h	Wed Nov 25 22:49:35 2015 +0100
@@ -5,6 +5,7 @@
 #define FALSE 0
 
 #include "../config.h"
+#include "pid.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -13,6 +14,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <time.h>
@@ -23,6 +25,13 @@
 #include <signal.h>
 #include <getopt.h>
 #include <limits.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <dirent.h>
+#include <uuid/uuid.h>
+#include <math.h>
 
 #ifndef HAVE_WIRINGPI_H
 #include <pthread.h>
@@ -74,24 +83,137 @@
 #define MBSE_SS(x) (x)?(x):"(null)"
 
 
+
+/*
+ * Brewing units. The sensors of the units are connected via the 1-wire bus.
+ * The heaters, pumps (or mixer) are connected on the Pi GPIO. The hardware
+ * is configured in the devices lists.
+ */
+typedef struct _units_list {
+    struct _units_list  *next;
+    int                 version;                /* Record version               */
+    char		*uuid;			/* uuid code			*/
+    char                *name;                  /* Unit name                    */
+    char                *hlt_sensor_address;    /* HLT sensor address           */
+    int                 hlt_sensor_state;       /* HLT sensor state             */
+    int                 hlt_sensor_value;       /* HLT sensor value             */
+    char		*hlt_heater_address;	/* HLT heater address		*/
+    int			hlt_heater_state;	/* HLT heater state 0..100	*/
+    int			hlt_heater_delay;	/* HLT heater delay / 15 sec	*/
+    int			hlt_heater_mltfirst;	/* HLT heater MLT high priority	*/
+    char                *mlt_sensor_address;    /* MLT sensor address           */
+    int                 mlt_sensor_state;       /* MLT sensor state             */
+    int                 mlt_sensor_value;       /* MLT sensor value             */
+    char		*mlt_heater_address;	/* MLT heater address		*/
+    int			mlt_heater_state;	/* MLT heater state 0..100	*/
+    int			mlt_heater_delay;	/* MLT heater delay / 15 sec	*/
+    char		*mlt_pump_address;	/* MLT pump address		*/
+    int			mlt_pump_state;		/* MLT pump state 0..100	*/
+    int			mlt_pump_delay;		/* MLT_pump_delay		*/
+    pid_var		*PID_hlt;		/* HLT PID			*/
+    pid_var		*PID_mlt;		/* MLT PID			*/
+} units_list;
+
+
+
+/*
+ * External devices like sensors, relays, SSR.
+ */
+typedef struct _dev_list {
+    struct _dev_list    *next;
+    int                 version;                /* Version 1                    */
+    char                *uuid;                  /* UUID of this device          */
+    int                 type;                   /* Device type                  */
+    int                 direction;              /* Device direction             */
+    int                 value;                  /* Device value                 */
+    int                 offset;                 /* Device offset value          */
+    int                 present;                /* Device present               */
+    char                *address;               /* Device address               */
+    int                 subdevice;              /* Device sub address           */
+    int                 gpiopin;                /* Device GPIO pin or -1        */
+    char                *description;           /* Device description           */
+    int                 inuse;                  /* In use counter               */
+    char                *comment;               /* What we think it is          */
+    time_t              timestamp;              /* Last updated                 */
+} devices_list;
+
+
+#define DEVTYPE_NA              0               /* Unknown device type          */
+#define DEVTYPE_W1              1               /* 1-Wire bus                   */
+#define DEVTYPE_GPIO            2               /* GPIO I/O device              */
+#define DEVTYPE_I2C             3               /* I2C bus device               */
+#define DEVTYPE_SPI             4               /* SPI bus device               */
+#ifdef USE_SIMULATOR
+#define DEVTYPE_SIM             5               /* Simulated device             */
+#endif
+
+#define DEVPRESENT_UNDEF        0               /* Precence not testable        */
+#define DEVPRESENT_NO           1               /* Device is missing            */
+#define DEVPRESENT_YES          2               /* Device is detected           */
+#define DEVPRESENT_ERROR        3               /* Device is in error           */
+
+#define DEVDIR_UNDEF            0               /* Undefined                    */
+#define DEVDIR_IN_BIN           1               /* Binary input                 */
+#define DEVDIR_OUT_BIN          2               /* Binary output                */
+#define DEVDIR_IN_ANALOG        3               /* Temperature input etc.       */
+#define DEVDIR_OUT_ANALOG       4               /* Analog steering              */
+#define DEVDIR_OUT_PWM          5               /* PWM outout                   */
+#define DEVDIR_INTERN           6               /* Internal function            */
+
+
+#ifdef USE_SIMULATOR
+
+/*
+ * Simulate a HLT and MLT.
+ */
+typedef struct _simulator {
+    struct _simulator   *next;
+    int                 version;                /* Version of this record       */
+    char                *uuid;                  /* Simulator uuid               */
+    char                *name;                  /* Simulator name               */
+    double		room_temperature;	/* Simulated envionment temp	*/
+    double		hlt_temperature;	/* Simulated HLT temperature	*/
+    double		hlt_heater_temp;	/* Maximum heater temp		*/
+    int			hlt_heater_time;	/* Time to reach temperature	*/
+    float		hlt_heater_size;	/* Size of the heater		*/
+    int			hlt_heater_state;	/* Heater status		*/
+    double		mlt_temperature;	/* Simulated MLT temperature	*/
+    double		mlt_heater_temp;	/* Maximum heater temp		*/
+    int			mlt_heater_time;	/* Time to reach temperature	*/
+    float		mlt_heater_size;	/* Size of the heater		*/
+    int			mlt_heater_state;	/* Heater status		*/
+    /*
+     * Status values, maintained by the simulator but stored
+     * here so they don't get lost over program restarts.
+     */
+    double		s_hlt_temp;		/* Temp HLT			*/
+    double		s_mlt_temp;		/* Temp MLT			*/
+} simulator_list;
+
+#endif
+
+
+
 typedef struct _sys_config {
     char		*name;			/* Configuration name		*/
+    int			my_port;		/* my client/server port	*/
     unsigned char	tempFormat;		/* Temperature format, C or F	*/
-    char		*hlt_sensor_address;	/* HLT sensor address		*/
-    int			hlt_sensor_state;	/* HLT sensor state		*/
-    int			hlt_sensor_value;	/* HLT sensor value		*/
-
-    char		*mlt_sensor_address;	/* MLT sensor address		*/
-    int			mlt_sensor_state;	/* MLT sensor state		*/
-    int			mlt_sensor_value;	/* MLT sensor value		*/
-
-#ifdef HAVE_WIRINGPI_H
+    char                *temp_address;          /* Environment temperature      */
+    int                 temp_state;             /* 0=ok, 1=missing, 2=error     */
+    int                 temp_value;             /* Air temperature in C * 1000  */
+    char                *hum_address;           /* Environment huminity         */
+    int                 hum_state;              /* 0=ok, 1=missing, 2=error     */
+    int                 hum_value;              /* Huminity in % * 1000         */
     int			lcd_cols;		/* LCD display columns		*/
     int			lcd_rows;		/* LCD display rows		*/
     int			lcd_address;		/* LCD display i2c address	*/
+    units_list		*units;			/* Brewing units		*/
+    devices_list	*devices;		/* Sensors and switches		*/
+#ifdef USE_SIMULATOR
+    simulator_list      *simulators;            /* Simulators                   */
 #endif
-
 } sys_config;
 
 
+
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/devices.c	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,795 @@
+/*****************************************************************************
+ * 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 "brewco.h"
+#include "devices.h"
+#include "xutil.h"
+
+
+extern int		debug;
+extern sys_config	Config;
+extern int		my_shutdown;
+
+#ifdef USE_SIMULATOR
+
+/*extern*/ int	SIM_hlt_temp = 0;
+/*extern*/ int	SIM_mlt_temp = 0;
+
+#endif
+
+
+
+/*
+ * Read one byte from a 1-wire device like a DS2413
+ */
+int read_w1(char *address, char *file)
+{
+    char	*addr = NULL;
+    int		fn = -1, rc = -1, retries = 5;
+    uint8_t	val;
+
+    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
+    addr = xstrcat(addr, address);
+    addr = xstrcat(addr, (char *)"/");
+    addr = xstrcat(addr, file);
+
+    if ((fn = open(addr, O_RDONLY)) >= 0) {
+
+    	if ((lseek(fn, 0L, SEEK_SET)) == 0) {
+
+	    while (retries--) {
+    	    	if ((read(fn, &val, 1)) == 1) {
+		    rc = (int)val;
+		    goto leave;
+    	    	}
+	    }
+	    syslog(LOG_NOTICE, "read_w1() read %s fatal: %s", addr, strerror(errno));
+
+	} else {
+	    syslog(LOG_NOTICE, "read_w1() lseek %s: %s", addr, strerror(errno));
+	}
+    	
+    } else {
+	syslog(LOG_NOTICE, "read_w1() open %s: %s", addr, strerror(errno));
+    }
+
+leave:
+    if (fn != -1) {
+	if ((close(fn)) == -1) {
+	    syslog(LOG_NOTICE, "read_w1() close %s: %s", addr, strerror(errno));
+	}
+    }
+
+    free(addr);
+    return rc;
+}
+
+
+
+/*
+ * Write a byte to a 1-wire device like a DS2413
+ */
+int write_w1(char *address, char *file, uint8_t val)
+{
+    char	*addr = NULL;
+    int		fn = -1, rc = -1, retries = 5;
+
+    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
+    addr = xstrcat(addr, address);
+    addr = xstrcat(addr, (char *)"/");
+    addr = xstrcat(addr, file);
+
+    if ((fn = open(addr, O_WRONLY)) >= 0) {
+
+	if ((lseek(fn, 0L, SEEK_SET)) == 0) {
+
+	    while (retries--) {
+	    	if ((write(fn, &val, 1)) == 1) {
+		    rc = 0;
+		    goto leave;
+	    	}
+	    }
+	    syslog(LOG_NOTICE, "write_w1() write %s fatal: %s", addr, strerror(errno));
+
+	} else {
+	    syslog(LOG_NOTICE, "write_w1() lseek %s: %s", addr, strerror(errno));
+	}
+
+    } else {
+	syslog(LOG_NOTICE, "write_w1() open %s: %s", addr, strerror(errno));
+    }
+
+leave:
+    if (fn != -1) {
+	if ((close(fn)) == -1) {
+	    syslog(LOG_NOTICE, "write_w1() close %s: %s", addr, strerror(errno));
+	}
+    }
+
+    free(addr);
+    return rc;
+}
+
+
+
+int device_out(char *uuid, int value)
+{
+    devices_list	*device;
+    time_t		now, my_timestamp;
+    int			rc, my_value, test_value;
+#ifdef HAVE_WIRINGPI_H
+    int			i;
+    char		buf[40];
+#endif
+
+    if (uuid == NULL)
+	return 0;
+
+    now = time(NULL);
+
+#ifdef HAVE_WIRINGPI_H
+    piLock(LOCK_DEVICES);
+#endif
+
+    for (device = Config.devices; device; device = device->next) {
+	if (! strcmp(uuid, device->uuid)) {
+	    /*
+	     * Execute command if different then the old value. But also
+	     * every 2 minutes because commands can have temporary
+	     * disconnects, or have radio problems.
+	     */
+	    my_timestamp = device->timestamp;
+	    my_value = device->value;
+
+	    if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN)) {
+		test_value = (value == 0) ? 0 : 1;
+	    } else {
+		test_value = value;
+	    }
+
+	    if ((test_value != my_value) || (((int)now - (int)my_timestamp) >= 120)) {
+
+#ifdef HAVE_WIRINGPI_H
+		rc = 0;
+		if ((device->type == DEVTYPE_RC433) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) {
+		    snprintf(buf, 39, "%s,%d", device->address, value ? 1:0);
+		    for (i = 0; i < strlen(buf); i++)
+			if (buf[i] == '-')
+			    buf[i] = ',';
+		    piUnlock(LOCK_DEVICES);
+	    	    enableTransmit(device->gpiopin);	    
+		    rc = toggleSwitch(buf);
+		    disableTransmit();
+		    piLock(LOCK_DEVICES);
+		    syslog(LOG_NOTICE, "RC433 command %s rc=%d", buf, rc);
+		    if (debug)
+			fprintf(stdout, "RC433 command %s rc=%d\n", buf, rc);
+                    device->value = value;
+		    device->timestamp = time(NULL);
+		    piUnlock(LOCK_DEVICES);
+		    return rc;
+	        }
+
+		if ((device->type == DEVTYPE_GPIO) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) {
+
+		}
+#endif
+		if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
+		    if (strncmp(device->address, (char *)"3a", 2) == 0) {
+			/*
+			 * DS2413. First read state so that we can preserve the state of
+			 * the "other" PIO channel. To make things a bit more complicated
+			 * the bits in the state register differ from the output register.
+			 */
+			uint8_t	state, output;
+
+			if ((rc = read_w1(device->address, (char *)"state")) >= 0) {
+			    state = (unsigned int)rc;
+			    output = (state & 0x01) + ((state & 0x04) >> 1);
+
+			    if (device->subdevice == 0) {
+				output = (output & 0xfe);
+				output |= (value == 0) ? 0x01 : 0x00;
+			    } else if (device->subdevice == 1) {
+				output = (output & 0xfd);
+				output |= (value == 0) ? 0x02 : 0x00;
+			    } else {
+				output = 0xff;
+			    }
+
+			    if ((write_w1(device->address, (char *)"output", output)) == 0) {
+			    	syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
+			    	if (debug)
+				    fprintf(stdout, "DS2413 PIO%c value=%d (%s)\n", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
+				device->value = (value == 0) ? 0 : 1;
+				device->timestamp = time(NULL);
+			    }
+			}
+		    }
+		}
+
+#ifdef USE_SIMULATOR
+		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
+		    if ((strcmp((char *)"SimHLTheater", device->address) == 0) || (strcmp((char *)"SimMLTheater", device->address) == 0)) {
+			if (value != device->value) {
+			    syslog(LOG_NOTICE, "SIM %s value=%d", device->address, value);
+			    if (debug)
+			    	fprintf(stdout, "SIM %s value=%d\n", device->address, value);
+			}
+			device->value = value;
+			if (strcmp((char *)"SimHLTheater", device->address) == 0)
+			    SIM_hlt_temp = value;
+			if (strcmp((char *)"SimMLTheater", device->address) == 0)
+			    SIM_mlt_temp = value;
+		    }
+		}
+#endif
+	    } else {
+#ifdef HAVE_WIRINGPI_H
+		piUnlock(LOCK_DEVICES);
+#endif
+		return 0;
+	    }
+	}
+    }
+#ifdef HAVE_WIRINGPI_H
+    piUnlock(LOCK_DEVICES);
+#endif
+
+    return 0;
+}	   
+
+
+/*
+ * Returns DEVPRESENT_NO  if failed.
+ * Returns DEVPRESENT_YES if success, value contains new value.
+ */
+int device_in(char *uuid, int *value)
+{
+    devices_list	*device;
+    int			tmp, present;
+
+    if (uuid == NULL)
+	return 0;
+
+#ifdef HAVE_WIRINGPI_H
+    piLock(LOCK_DEVICES);
+#endif
+
+    for (device = Config.devices; device; device = device->next) {
+	if (! strcmp(uuid, device->uuid)) {
+	    present = device->present;
+	    if (present == DEVPRESENT_YES) {
+		tmp = device->value + device->offset;
+	    } else {
+		tmp = 0;
+	    }
+#ifdef HAVE_WIRINGPI_H
+	    piUnlock(LOCK_DEVICES);
+#endif
+	    *value = tmp;
+	    return present;
+	}
+    }
+
+#ifdef HAVE_WIRINGPI_H
+    piUnlock(LOCK_DEVICES);
+#endif
+
+    return DEVPRESENT_NO;
+}
+
+
+
+
+/*
+ * Auto detect hotplugged or known to be present devices
+ */
+int devices_detect(void)
+{
+    struct dirent	*de;
+    DIR			*fd;
+    devices_list	*device, *ndev;
+    int			found, subdevices, ival, i, rc = 0;
+    char		buf[40];
+    uuid_t		uu;
+#ifdef HAVE_WIRINGPI_H
+    int			pin;
+#endif
+
+    /*
+     * Scan for 1-wire devices
+     */
+    if ((fd = opendir((char *)"/sys/bus/w1/devices"))) {
+	while ((de = readdir(fd))) {
+	    if (de->d_name[0] != '.') {
+		found = FALSE;
+		for (device = Config.devices; device; device = device->next) {
+		    if (strcmp(device->address,de->d_name) == 0) {
+			found = TRUE;
+			break;
+		    }
+		}
+
+		if (found == FALSE) {
+		    strncpy(buf, de->d_name, 2);
+		    buf[2] = '\0';
+		    sscanf(buf, "%02x", &ival);
+		    syslog(LOG_NOTICE, "Scan 1-wire %02x %d", ival, ival);
+		    subdevices = 1;
+		    if (strcmp(buf, (char *)"29") == 0)
+			subdevices = 8;
+		    if (strcmp(buf, (char *)"3a") == 0)
+			subdevices = 2;
+		    for (i = 0; i < subdevices; i++) {
+			ndev = (devices_list *)malloc(sizeof(devices_list));
+			ndev->next = NULL;
+			ndev->version = 1;
+			ndev->uuid = malloc(37);
+			uuid_generate(uu);
+			uuid_unparse(uu, ndev->uuid);
+			ndev->type = DEVTYPE_W1;
+			ndev->direction = DEVDIR_UNDEF;
+			if (strcmp(buf, (char *)"10") == 0) {
+			    ndev->direction = DEVDIR_IN_ANALOG;
+			    ndev->description = xstrcpy((char *)"DS18S20 Digital thermometer");
+			} else if (strcmp(buf, (char *)"22") == 0) {
+			    ndev->direction = DEVDIR_IN_ANALOG;
+			    ndev->description = xstrcpy((char *)"DS1820 Digital thermometer");
+			} else if (strcmp(buf, (char *)"28") == 0) {
+			    ndev->direction = DEVDIR_IN_ANALOG;
+			    ndev->description = xstrcpy((char *)"DS18B20 Digital thermometer");
+			} else if (strcmp(buf, (char *)"3a") == 0) {
+			    ndev->description = xstrcpy((char *)"DS2413 Dual channel addressable switch");
+			    ndev->direction = DEVDIR_IN_BIN;
+			} else if (strcmp(buf, (char *)"3b") == 0) {
+			    ndev->direction = DEVDIR_IN_ANALOG;
+			    ndev->description = xstrcpy((char *)"DS1825 Digital thermometer");
+			} else if (strcmp(buf, (char *)"42") == 0) {
+			    ndev->direction = DEVDIR_IN_ANALOG;
+			    ndev->description = xstrcpy((char *)"DS28EA00 Digital thermometer");
+			} else if (strcmp(buf, (char *)"w1") == 0) {
+			    ndev->description = xstrcpy((char *)"Master System device");
+			} else {
+			    ndev->description = xstrcpy((char *)"Unknown device family ");
+			    ndev->description = xstrcat(ndev->description, buf);
+			}
+			ndev->value = ndev->offset = ndev->inuse = 0;
+			ndev->present = DEVPRESENT_YES;
+			ndev->address = xstrcpy(de->d_name);
+			ndev->subdevice = i;
+			ndev->gpiopin = -1;
+			ndev->comment = xstrcpy((char *)"Auto detected device");
+			ndev->timestamp = time(NULL);
+
+			if (Config.devices == NULL) {
+			    Config.devices = ndev;
+			} else {
+			    for (device = Config.devices; device; device = device->next) {
+				if (device->next == NULL) {
+				    device->next = ndev;
+				    break;
+				}
+			    }
+			}
+			rc++;
+		    }
+		}
+	    }
+	}
+	closedir(fd);
+    }
+
+#ifdef HAVE_WIRINGPI_H
+    if (piBoardRev() == 2) {
+	/*
+	 * Support rev B and newer boards only
+	 */
+	found = FALSE;
+	for (device = Config.devices; device; device = device->next) {
+	    if (device->type == DEVTYPE_GPIO) {
+		found = TRUE;
+		break;
+	    }
+	}
+
+    	if (found == FALSE) {
+	    /*
+	     * There were no GPIO devices found.
+	     */
+	    subdevices = 12;
+	    pin = 0;
+	    for (i = 0; i < subdevices; i++) {
+	    	if (i == 8)
+		    pin = 17;
+
+	    	ndev = (devices_list *)malloc(sizeof(devices_list));
+	    	ndev->next = NULL;
+	    	ndev->version = 1;
+	    	ndev->uuid = malloc(37);
+	    	uuid_generate(uu);
+	    	uuid_unparse(uu, ndev->uuid);
+	    	ndev->type = DEVTYPE_GPIO;
+	    	ndev->value = digitalRead(pin);
+		ndev->offset = 0;
+	    	ndev->present = DEVPRESENT_YES;
+	    	ndev->address = xstrcpy((char *)"GPIO");
+	    	snprintf(buf, 39, "Raspberry GPIO %d", i);
+	    	ndev->description = xstrcpy(buf);
+	    	ndev->subdevice = i;
+	    	ndev->gpiopin = pin;
+	    	ndev->timestamp = time(NULL);
+		if (i == PANEL_RETURN) {
+		    ndev->direction = DEVDIR_IN_BIN;
+		    ndev->inuse = 1;
+		    ndev->comment = xstrcpy((char *)"Frontpanel Return");
+		} else if (i == PANEL_ENTER) {
+		    ndev->direction = DEVDIR_IN_BIN;
+		    ndev->inuse = 1;
+		    ndev->comment = xstrcpy((char *)"Frontpanel Enter key");
+		} else if (i == PANEL_DOWN) {
+		    ndev->direction = DEVDIR_IN_BIN;
+		    ndev->inuse = 1;
+		    ndev->comment = xstrcpy((char *)"Frontpanel Down key");
+		} else if (i == PANEL_UP) {
+		    ndev->direction = DEVDIR_IN_BIN;
+		    ndev->inuse = 1;
+		    ndev->comment = xstrcpy((char *)"Frontpanel Up key");
+		} else if (i == 7) {
+		    ndev->direction = DEVDIR_INTERN;
+		    ndev->inuse = 1;
+		    ndev->comment = xstrcpy((char *)"1-Wire bus");
+	    	} else {
+		    ndev->direction = DEVDIR_IN_BIN;
+		    ndev->inuse = 0;
+		    ndev->comment = xstrcpy((char *)"Raspberry GPIO");
+	    	}
+	    	pin++;
+
+	    	if (Config.devices == NULL) {
+		    Config.devices = ndev;
+	    	} else {
+		    for (device = Config.devices; device; device = device->next) {
+		        if (device->next == NULL) {
+			    device->next = ndev;
+			    break;
+		    	}
+		    }
+	    	}
+	    	rc++;
+	    }
+	}
+    }
+#endif
+
+#ifdef USE_SIMULATOR
+    found = FALSE;
+    for (device = Config.devices; device; device = device->next) {
+	if (device->type == DEVTYPE_SIM) {
+	    found = TRUE;
+	    break;
+	}
+    }
+
+    if (found == FALSE) {
+	subdevices = 5;
+	for (i = 0; i < subdevices; i++) {
+	    ndev = (devices_list *)malloc(sizeof(devices_list));
+	    ndev->next = NULL;
+	    ndev->version = 1;
+	    ndev->uuid = malloc(37);
+	    uuid_generate(uu);
+	    uuid_unparse(uu, ndev->uuid);
+	    ndev->type = DEVTYPE_SIM;
+	    ndev->value = ndev->offset = 0;
+	    ndev->present = DEVPRESENT_YES;
+	    ndev->subdevice = i;
+	    ndev->gpiopin = -1;
+	    ndev->comment = xstrcpy((char *)"Auto detected device");
+	    ndev->timestamp = time(NULL);
+	    ndev->inuse = 0;
+	    switch (i) {
+		case 0:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimRoomtemp");
+			ndev->description = xstrcpy((char *)"Simulated room temperature");
+			break;
+		case 1:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimHLTtemp");
+			ndev->description = xstrcpy((char *)"Simulated HLT temperature");
+			break;
+		case 2:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimMLTtemp");
+			ndev->description = xstrcpy((char *)"Simulated MLT temperature");
+			break;
+		case 3:	ndev->direction = DEVDIR_OUT_ANALOG;
+			ndev->address = xstrcpy((char *)"SimHLTheater");
+			ndev->description = xstrcpy((char *)"Simulated HLT heater");
+			break;
+		case 4:	ndev->direction = DEVDIR_OUT_ANALOG;
+			ndev->address = xstrcpy((char *)"SimMLTheater");
+			ndev->description = xstrcpy((char *)"Simulated MLT heater");
+			break;
+	    }
+
+	    if (Config.devices == NULL) {
+		Config.devices = ndev;
+	    } else {
+		for (device = Config.devices; device; device = device->next) {
+		    if (device->next == NULL) {
+			device->next = ndev;
+			break;
+		    }
+		}
+	    }
+	    rc++;
+	}
+    }
+#endif
+
+    return rc;
+}
+
+
+
+#ifdef HAVE_WIRINGPI_H
+PI_THREAD (my_devices_loop)
+#else
+void *my_devices_loop(void *threadid)
+#endif
+{
+    devices_list	*device;
+#ifdef USE_SIMULATOR
+    simulator_list	*simulator;
+#endif
+    char		*addr = NULL, line[60], *p = NULL;
+    FILE		*fp;
+    int			temp, rc;
+#ifdef HAVE_WIRINGPI_H
+    time_t		now;
+#endif
+
+    syslog(LOG_NOTICE, "Thread my_devices_loop started");
+
+#ifdef HAVE_WIRINGPI_H
+    if ((rc = piHiPri(10)))
+    	syslog(LOG_NOTICE, "my_devices_loop: piHiPri(10) rc=%d", rc);
+#endif
+
+    /*
+     * Loop forever until the external shutdown variable is set.
+     */
+    for (;;) {
+
+    	/*
+    	 * Process all devices.
+    	 */
+    	for (device = Config.devices; device; device = device->next) {
+
+	    if (my_shutdown)
+		break;
+
+	    switch (device->type) {
+		case DEVTYPE_W1:
+			/*
+			 * Only tested with DS18B20 but from the kernel source this
+			 * should work with all 1-wire thermometer sensors.
+			 */
+			if ((strncmp(device->address, (char *)"10", 2) == 0) ||
+			    (strncmp(device->address, (char *)"22", 2) == 0) ||
+			    (strncmp(device->address, (char *)"28", 2) == 0) ||
+			    (strncmp(device->address, (char *)"3b", 2) == 0) ||
+			    (strncmp(device->address, (char *)"42", 2) == 0)) {
+			    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
+			    addr = xstrcat(addr, device->address);
+			    addr = xstrcat(addr, (char *)"/w1_slave");
+			    if ((fp = fopen(addr, "r"))) {
+				if (device->present != DEVPRESENT_YES) {
+				     syslog(LOG_NOTICE, "sensor %s is back", device->address);
+#ifdef HAVE_WIRINGPI_H
+				     piLock(LOCK_DEVICES);
+#endif
+				     device->present = DEVPRESENT_YES;
+#ifdef HAVE_WIRINGPI_H
+				     piUnlock(LOCK_DEVICES);
+#endif
+				}
+				/*
+				 * The output looks like:
+				 * 72 01 4b 46 7f ff 0e 10 57 : crc=57 YES
+				 * 72 01 4b 46 7f ff 0e 10 57 t=23125
+				 */
+				fgets(line, 50, fp);
+				line[strlen(line)-1] = '\0';
+				if ((line[36] == 'Y') && (line[37] == 'E')) {
+				    /* CRC is Ok, continue */
+				    fgets(line, 50, fp);
+				    line[strlen(line)-1] = '\0';
+				    strtok(line, (char *)"=");
+				    p = strtok(NULL, (char *)"=");
+				    rc = sscanf(p, "%d", &temp);
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);
+#endif
+				    if ((rc == 1) && (device->value != temp)) {
+					device->value = temp;
+				    	device->timestamp = time(NULL);
+				    }
+#ifdef HAVE_WIRINGPI_H
+				    piUnlock(LOCK_DEVICES);
+#endif
+				} else {
+				    syslog(LOG_NOTICE, "sensor %s CRC error", device->address);
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);                
+#endif
+				    device->present = DEVPRESENT_ERROR;
+#ifdef HAVE_WIRINGPI_H
+				    piUnlock(LOCK_DEVICES);              
+#endif
+				}
+				fclose(fp);
+			    } else {
+				if (device->present != DEVPRESENT_NO) {
+				    syslog(LOG_NOTICE, "sensor %s is missing", device->address);
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);                
+#endif
+				    device->present = DEVPRESENT_NO;
+#ifdef HAVE_WIRINGPI_H          
+				    piUnlock(LOCK_DEVICES);              
+#endif
+				}
+			    }
+			    free(addr);
+			    addr = NULL;
+			} /* if temperature sensor */
+			/*
+			 * DS2413 Dual channel addressable switch
+			 */
+			if (strncmp(device->address, (char *)"3a", 2) == 0) {
+			    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
+			    addr = xstrcat(addr, device->address);
+			    addr = xstrcat(addr, (char *)"/state");
+
+			    if ((access(addr, R_OK)) == 0) {
+				if (device->present != DEVPRESENT_YES) {
+				    syslog(LOG_NOTICE, "DS2413 %s is back", device->address);
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);
+#endif
+				    device->present = DEVPRESENT_YES;
+#ifdef HAVE_WIRINGPI_H
+				    piUnlock(LOCK_DEVICES);
+#endif
+				}
+				/*
+				 * First make sure that if this device is configured as input
+				 * to drive the output high.
+				 */
+				if (device->direction == DEVDIR_IN_BIN) {
+				    uint8_t state, output;
+
+				    if ((rc = read_w1(device->address, (char *)"state")) >= 0) {
+					state = (unsigned int)rc;
+					output = ((state & 0x02) >> 1) + ((state & 0x08) >> 2);		/* Both latch states	*/
+					if (device->subdevice == 0) {
+					    output = (output & 0xfe);
+					    output |= 0x01;
+					} else if (device->subdevice == 1) {
+					    output = (output & 0xfd);
+					    output |= 0x02;
+					} else {
+					    output = 0xff;
+					}
+					write_w1(device->address, (char *)"output", output);
+				    }
+				}
+				if ((rc = read_w1(device->address, (char *)"state")) >= 0) {
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);
+#endif
+				    /*
+				     * Read PIOA or PIOB pin state bits
+				     */
+				    if (device->subdevice == 0)
+					device->value = (rc & 0x01) ? 0 : 1;
+				    else if (device->subdevice == 1)
+					device->value = (rc & 0x04) ? 0 : 1;
+				    device->timestamp = time(NULL);
+#ifdef HAVE_WIRINGPI_H
+				    piUnlock(LOCK_DEVICES);
+#endif
+				}
+			    } else {
+				if (device->present != DEVPRESENT_NO) {
+				    syslog(LOG_NOTICE, "DS2413 %s is missing", device->address);
+#ifdef HAVE_WIRINGPI_H
+				    piLock(LOCK_DEVICES);
+#endif
+				    device->present = DEVPRESENT_NO;
+#ifdef HAVE_WIRINGPI_H          
+				    piUnlock(LOCK_DEVICES);
+#endif
+				}
+			    }
+			    free(addr);
+			    addr = NULL;
+			}
+
+			break;
+
+#ifdef HAVE_WIRINGPI_H
+		case DEVTYPE_GPIO:
+			if (device->direction == DEVDIR_IN_BIN) {
+			    piLock(LOCK_DEVICES);
+			    device->value = digitalRead(device->gpiopin);
+			    device->offset = 0;
+			    device->timestamp = time(NULL);
+			    piUnlock(LOCK_DEVICES);
+			}
+			break;
+
+#endif
+#ifdef USE_SIMULATOR
+		case DEVTYPE_SIM:
+#ifdef HAVE_WIRINGPI_H
+			piLock(LOCK_DEVICES);
+#endif
+			if (Config.simulators) {
+			    simulator = Config.simulators;
+			    if (device->subdevice == 0) {
+			    	device->value = (int)(simulator->room_temperature * 1000);
+			    	device->timestamp = time(NULL);
+			    } else if (device->subdevice == 1) {
+			    	device->value = (int)(simulator->hlt_temperature * 1000);
+			    	device->timestamp = time(NULL);
+			    } else if (device->subdevice == 2) {
+			    	device->value = (int)(simulator->mlt_temperature * 1000);
+			    	device->timestamp = time(NULL);
+			    }
+			}
+#ifdef HAVE_WIRINGPI_H
+			piUnlock(LOCK_DEVICES);
+#endif
+			break;
+#endif
+		default:
+			break;
+	    }
+
+	    /*
+	     * Delay a bit after procesing a device.
+	     */
+	    usleep(10000);
+	}
+	/*
+	 * Delay a bit after all devices
+	 */
+	usleep(100000);
+    }
+
+    syslog(LOG_NOTICE, "Thread my_devices_loop stopped");
+    return 0;
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/devices.h	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,15 @@
+#ifndef MY_DEVICES_H
+#define	MY_DEVICES_H
+
+
+int device_out(char *, int);
+int device_in(char *, int *);
+int devices_detect(void);
+
+#ifdef HAVE_WIRINGPI_H
+PI_THREAD (my_devices_loop);
+#else
+void *my_devices_loop(void *);
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/lcd-pcf8574.c	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,114 @@
+/*
+ * lcd-pcf8574.c:
+ *	Text-based LCD driver library code
+ *	This is designed to drive the HD44780U LCD display connected via
+ *	a "LCM1602 IIC  A0 A1 A2" board with a PCF8574 I2C controller.
+ *
+ * Copyright (c) 2012-2013 Gordon Henderson.
+ * Copyright (c) 2014 Michiel Broek.
+ ***********************************************************************
+ *
+ *    mbsePi is free software: you can redistribute it and/or modify
+ *    it under the terms of the GNU Lesser General Public License as published by
+ *    the Free Software Foundation, either version 3 of the License, or
+ *    (at your option) any later version.
+ *
+ *    mbsePi 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 Lesser General Public License for more details.
+ *
+ *    You should have received a copy of the GNU Lesser General Public License
+ *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
+ ***********************************************************************
+ */
+
+#include "brewco.h"
+#include "lcd-pcf8574.h"
+#include "slcd.h"
+
+
+int			lcdHandle;
+int			slcdHandle;
+
+
+#ifdef HAVE_WIRINGPI_H
+struct lcdDataStruct
+{
+    int bits, rows, cols ;
+    int rsPin, strbPin ;
+    int dataPins [8] ;
+    int cx, cy ;
+};
+
+extern struct lcdDataStruct	*lcds [MAX_LCDS];
+#endif
+extern sys_config       	Config;
+extern uint16_t			leds;
+
+
+/*
+ * setBacklight:
+ *********************************************************************************
+ */
+void setBacklight(int value)
+{
+#ifdef HAVE_WIRINGPI_H
+    pinMode (AF_BACKLIGHT, OUTPUT) ;
+    digitalWrite (AF_BACKLIGHT, (value & 1)) ;
+#endif
+    if (value) {
+	leds |= SLED_LCD;
+    } else {
+	leds &= ~SLED_LCD;
+    }
+    slcdLEDs(slcdHandle);
+}
+
+
+/*
+ * initLCD:
+ *********************************************************************************
+ */
+
+int initLCD(int cols, int rows)
+{
+    if (!((rows == 1) || (rows == 2) || (rows == 4))) {
+    	fprintf (stderr, "rows must be 1, 2 or 4\n") ;
+    	return EXIT_FAILURE ;
+    }
+
+    if (!((cols == 16) || (cols == 20))) {
+    	fprintf (stderr, "cols must be 16 or 20\n") ;
+    	return EXIT_FAILURE ;
+    }
+
+#ifdef HAVE_WIRINGPI_H
+    pcf8574Setup(AF_BASE, 0x27) ;
+    pinMode (AF_RW, OUTPUT) ;
+    digitalWrite (AF_RW, LOW) ;        // Not used with wiringPi - always in write mode
+
+    /*
+     * The other control pins are initialised with lcdInit ()
+     */
+    lcdHandle = lcdInit (rows, cols, 4, AF_RS, AF_E, AF_DB4, AF_DB5, AF_DB6, AF_DB7, 0, 0, 0, 0) ;
+    if (lcdHandle < 0) {
+    	fprintf (stderr, "lcdInit failed\n") ;
+    	return -1 ;
+    }
+
+    lcdClear (lcdHandle) ;
+#endif
+
+    slcdHandle = slcdInit(0, rows, cols);
+    if (slcdHandle < 0) {
+	fprintf (stderr, "slcdInit failed\n") ;
+	return -1;
+    }
+    slcdClear(slcdHandle);
+    setBacklight(1);
+
+    return 0 ;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/lcd-pcf8574.h	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,25 @@
+#ifndef	LCD_PCF8574_H
+#define	LCD_PCF8574_H
+
+
+#ifdef HAVE_WIRINGPI_H
+
+// Defines for the pcf8574 Pi LCD interface board
+#define AF_BASE         100
+
+#define AF_RS           (AF_BASE + 0)
+#define AF_RW           (AF_BASE + 1)
+#define AF_E            (AF_BASE + 2)
+#define AF_BACKLIGHT    (AF_BASE + 3)
+#define AF_DB4          (AF_BASE + 4)
+#define AF_DB5          (AF_BASE + 5)
+#define AF_DB6          (AF_BASE + 6)
+#define AF_DB7          (AF_BASE + 7)
+
+#endif
+
+void setBacklight (int);
+int  initLCD (int, int);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/lock.c	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,133 @@
+/*****************************************************************************
+ * Copyright (C) 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 "brewco.h"
+
+/*
+ *  Put a lock on this program.
+ */
+int lockprog(char *name)
+{
+    char    *tempfile, *lockfile;
+    FILE    *fp;
+    pid_t   oldpid;
+
+    tempfile = calloc(PATH_MAX, sizeof(char));
+    lockfile = calloc(PATH_MAX, sizeof(char));
+
+    snprintf(tempfile, PATH_MAX, "/var/run/%s.tmp", name);
+    snprintf(lockfile, PATH_MAX, "/var/run/%s.pid", name);
+
+    if ((fp = fopen(tempfile, "w")) == NULL) {
+	perror(name);
+	printf("Can't create lockfile \"%s\"\n", tempfile);
+	free(tempfile);
+	free(lockfile);
+	return 1;
+    }
+    fprintf(fp, "%10u\n", getpid());
+    fclose(fp);
+
+    while (TRUE) {
+	if (link(tempfile, lockfile) == 0) {
+	    unlink(tempfile);
+	    free(tempfile);
+	    free(lockfile);
+	    return 0;
+	}
+	if ((fp = fopen(lockfile, "r")) == NULL) {
+	    perror(name);
+	    printf("Can't open lockfile \"%s\"\n", tempfile);
+	    unlink(tempfile);
+	    free(tempfile);
+	    free(lockfile);
+	    return 1;
+	}
+	if (fscanf(fp, "%u", &oldpid) != 1) {
+	    perror(name);
+	    printf("Can't read old pid from \"%s\"\n", tempfile);
+	    fclose(fp);
+	    unlink(tempfile);
+	    free(tempfile);
+	    free(lockfile);
+	    return 1;
+	}
+	fclose(fp);
+	if (kill(oldpid,0) == -1) {
+	    if (errno == ESRCH) {
+		printf("Stale lock found for pid %u\n", oldpid);
+		unlink(lockfile);
+		/* no return, try lock again */  
+	    } else {
+		perror(name);
+		printf("Kill for %u failed\n",oldpid);
+		unlink(tempfile);
+		free(tempfile);
+		free(lockfile);
+		return 1;
+	    }
+	} else {
+	    printf("Another %s is already running, pid=%u\n", name, oldpid);
+	    unlink(tempfile);
+	    free(tempfile);
+	    free(lockfile);
+	    return 1;
+	}
+    }
+}
+
+
+
+void ulockprog(char *name)
+{
+    char	    *lockfile;
+    pid_t	    oldpid;
+    FILE	    *fp;
+
+    lockfile = calloc(PATH_MAX, sizeof(char));
+    snprintf(lockfile, PATH_MAX, "/var/run/%s.pid", name);
+
+    if ((fp = fopen(lockfile, "r")) == NULL) {
+	syslog(LOG_NOTICE, "Can't open lockfile \"%s\"", lockfile);
+	free(lockfile);
+	return;
+    }
+
+    if (fscanf(fp, "%u", &oldpid) != 1) {
+	syslog(LOG_NOTICE, "Can't read old pid from \"%s\"", lockfile);
+	fclose(fp);
+	unlink(lockfile);
+	free(lockfile);
+	return;
+    }
+
+    fclose(fp);
+
+    if (oldpid == getpid()) {
+	(void)unlink(lockfile);
+    }
+
+    free(lockfile);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/lock.h	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,8 @@
+#ifndef	LOCK_H
+#define	LOCK_H
+
+
+int  lockprog(char *);
+void ulockprog(char *);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/pid.c	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * Copyright (C) 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 "brewco.h"
+#include "pid.h"
+
+
+void InitPID(pid_var *pid)
+{
+    pid->Err = pid->ErrLast = pid->iState = 0.0;
+    pid->Input = pid->OutP = pid->SetP = 0.0;
+    pid->pGain = pid->iGain = pid->dGain = 0.0;
+    pid->Mode = PID_MODE_NONE;
+    pid->iMax = 100.0;
+}
+
+
+
+void UpdatePID(pid_var *pid)
+{
+    if (pid->Mode == PID_MODE_AUTO) {
+
+	double	pTerm, dTerm, iTerm;
+
+	pid->Err = pid->SetP - pid->Input;
+
+	/*
+	 * Calculate the integral state with appopriate limiting.
+	 * Use ErrLastLast as iState
+	 */
+	pid->iState += pid->Err;
+	if (pid->iState > PID_WINDUP_GUARD)
+	    pid->iState = PID_WINDUP_GUARD;
+	else if (pid->iState < -PID_WINDUP_GUARD)
+	    pid->iState = -PID_WINDUP_GUARD;
+
+	pTerm = pid->pGain * pid->Err;
+	iTerm = pid->iGain * pid->iState;
+	dTerm = pid->dGain * (pid->Err - pid->ErrLast);
+
+	pid->OutP = pTerm + dTerm + iTerm;
+	pid->ErrLast = pid->Err;
+
+    } else if (pid->Mode == PID_MODE_BOO) {
+	/*
+	 * Mode Bang On Off
+	 */
+	pid->ErrLast = pid->Err;
+	pid->Err = pid->SetP - pid->Input;
+
+	if (pid->OutP && (pid->Err <= 0.0))
+	    pid->OutP = 0.0;
+	else if ((pid->OutP == 0.0) && (pid->Err > 0.0))
+	    pid->OutP = pid->iMax;
+
+	pid->iState = 0.0;
+
+    } else {
+	/*
+	 * While in manual mode, stay ready for bumpless switch to
+	 * auto.
+	 */
+	pid->ErrLast = pid->Err = 0.0;
+	pid->OutP = pid->iState = 0.0;
+    }
+
+    if (pid->OutP > pid->iMax)
+	pid->OutP = pid->iMax;
+    if (pid->OutP < 0.0)
+	pid->OutP = 0.0;
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/pid.h	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,31 @@
+#ifndef	PID_H
+#define	PID_H
+
+#define	PID_MODE_NONE		0	/* Process control off				*/
+#define	PID_MODE_AUTO		1	/* Process control auto				*/
+#define	PID_MODE_BOO		2	/* Process control Bang On/Off			*/
+
+#define	PID_TIMES		60	/* 60 calculations per minute			*/
+#define	PID_WINDUP_GUARD	10.0	/* Error windup guard				*/
+
+
+typedef struct _pid_var {
+	double		iMax;		/* Maximum allowable integrator state		*/
+	double		iGain;		/* Integral gain				*/
+	double		pGain;		/* Proportional gain				*/
+	double		dGain;		/* Derivative gain				*/
+
+	double		Input;		/* Input value					*/
+	double		Err;		/* Error, diff between input and set point	*/
+	double		ErrLast;	/* Error from last pass				*/
+	double		iState;		/* Error from next last pass			*/
+	double		SetP;		/* Set point					*/
+	double		OutP;		/* Output of PID algorithm			*/
+	int		Mode;		/* Value is 'PID_AUTO' if loop is automatic	*/
+} pid_var;
+
+
+void InitPID( pid_var *);
+void UpdatePID( pid_var *);
+
+#endif
--- a/brewco/rdconfig.c	Wed Nov 25 16:39:25 2015 +0100
+++ b/brewco/rdconfig.c	Wed Nov 25 22:49:35 2015 +0100
@@ -25,37 +25,84 @@
 #include "futil.h"
 #include "xutil.h"
 
-int		debug = FALSE;
+int		debug = TRUE;
 sys_config	Config;			/* System configuration		*/
 
 #define MY_ENCODING "utf-8"
 
 const char	TEMPSTATE[3][8] = { "OK", "MISSING", "ERROR" };
-//const char	DEVTYPE[8][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI", "SIM" };
-//const char	DEVPRESENT[4][6] = { "UNDEF", "NO", "YES", "ERROR" };
-//const char	DEVDIR[7][11] = { "UNDEF", "IN_BIN", "OUT_BIN", "IN_ANALOG", "OUT_ANALOG", "OUT_PWM", "INTERN" };
-//const char	PIDMODE[3][5] = { "NONE", "AUTO", "BOO" };
+const char	DEVTYPE[8][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI", "SIM" };
+const char	DEVPRESENT[4][6] = { "UNDEF", "NO", "YES", "ERROR" };
+const char	DEVDIR[7][11] = { "UNDEF", "IN_BIN", "OUT_BIN", "IN_ANALOG", "OUT_ANALOG", "OUT_PWM", "INTERN" };
+const char	PIDMODE[3][5] = { "NONE", "AUTO", "BOO" };
 
 
 void killconfig(void)
 {
+    units_list          *unit;
+    devices_list        *device;
+#ifdef USE_SIMULATOR
+    simulator_list      *simulator;
+#endif
 
     if (Config.name)
 	free(Config.name);
     Config.name = NULL;
 
+    Config.my_port = 6554;
     Config.tempFormat = 'C';
-    if (Config.hlt_sensor_address)
-	free(Config.hlt_sensor_address);
-    if (Config.mlt_sensor_address)
-	free(Config.mlt_sensor_address);
-    Config.hlt_sensor_address = Config.mlt_sensor_address = NULL;
-    Config.hlt_sensor_value = Config.mlt_sensor_value = 20000;
-    Config.hlt_sensor_state = Config.mlt_sensor_state = 1;	// missing
+    if (Config.temp_address)
+        free(Config.temp_address);
+    if (Config.hum_address)
+        free(Config.hum_address);
+    Config.temp_address = Config.hum_address = NULL;
+    Config.temp_value = 20000;
+    Config.temp_state = Config.hum_state = 1;   // missing
+    Config.hum_value = 50000;
+    Config.lcd_cols = 20;
+    Config.lcd_rows = 4;
 
-#ifdef HAVE_WIRINGPI_H
-    Config.lcd_cols = 16;
-    Config.lcd_rows = 2;
+    for (unit = Config.units; unit; unit = unit->next) {
+        if (unit->uuid)
+            free(unit->uuid);
+        if (unit->name)
+            free(unit->name);
+        if (unit->hlt_sensor_address)
+            free(unit->hlt_sensor_address);
+        if (unit->mlt_sensor_address)
+            free(unit->mlt_sensor_address);
+        if (unit->hlt_heater_address)
+            free(unit->hlt_heater_address);
+        if (unit->mlt_heater_address)
+            free(unit->mlt_heater_address);
+        if (unit->mlt_pump_address)
+            free(unit->mlt_pump_address);
+        free(unit);
+    }
+    Config.units = NULL;
+
+    for (device = Config.devices; device; device = device->next) {
+        if (device->uuid)
+            free(device->uuid);
+        if (device->address)
+            free(device->address);
+        if (device->description)
+            free(device->description);
+        if (device->comment)
+            free(device->comment);
+        free(device);
+    }
+    Config.devices = NULL;
+
+#ifdef USE_SIMULATOR
+    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+        if (simulator->uuid)
+            free(simulator->uuid);
+        if (simulator->name)
+            free(simulator->name);
+        free(simulator);
+    }
+    Config.simulators = NULL;
 #endif
 }
 
@@ -69,6 +116,11 @@
     char		*mypath = NULL;
     xmlTextWriterPtr	writer;
     xmlBufferPtr	buf;
+    units_list		*unit;
+    devices_list	*device;
+#ifdef USE_SIMULATOR
+    simulator_list      *simulator;
+#endif
 
     /* 
      * Create a new XML buffer, to which the XML document will be written
@@ -124,36 +176,39 @@
 	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
 	return 1;
     }
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LISTEN_PORT", "%d", Config.my_port)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;
+    }
     if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMPFORMAT", "%c", Config.tempFormat)) < 0) {
 	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
 	return 1;
     }
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_ADDRESS", "%s", Config.hlt_sensor_address)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_ADDRESS", "%s", Config.temp_address)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;
     }
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_STATE", "%d", Config.hlt_sensor_state)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;                           
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_STATE", "%d", Config.temp_state)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;                           
     }
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_VALUE", "%d", Config.hlt_sensor_value)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_VALUE", "%d", Config.temp_value)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;
     }
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_ADDRESS", "%s", Config.mlt_sensor_address)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_ADDRESS", "%s", Config.hum_address)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;
     }   
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_STATE", "%d", Config.mlt_sensor_state)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;    
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_STATE", "%d", Config.hum_state)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;    
     }   
-    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_VALUE", "%d", Config.mlt_sensor_value)) < 0) {
-	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-	return 1;
+    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_VALUE", "%d", Config.hum_value)) < 0) {
+        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+        return 1;
     }
 
-#ifdef HAVE_WIRINGPI_H
     /* 
      * Start an element named "LCDS" as child of BREWCO.
      */
@@ -199,6 +254,343 @@
 	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
 	return 1;
     }
+
+    /*
+     * Brewsystems
+     */
+    if (Config.units) {
+        if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "BREWSYSTEMS")) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+            return 1;
+        }
+        for (unit = Config.units; unit; unit = unit->next) {
+            /*
+             * Only configuration items are written, measured values and states
+             * are written to a state file.
+             */
+            if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "BREWSYSTEM")) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", unit->uuid)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", unit->name)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+
+            if (unit->hlt_sensor_address) {
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_ADDRESS", "%s", unit->hlt_sensor_address)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_STATE", "%d", unit->hlt_sensor_state)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_SENSOR_VALUE", "%d", unit->hlt_sensor_value)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->hlt_heater_address) {
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_HEATER_ADDRESS", "%s", unit->hlt_heater_address)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_HEATER_STATE", "%d", unit->hlt_heater_state)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HLT_HEATER_DELAY", "%d", unit->hlt_heater_delay)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->mlt_sensor_address) {
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_ADDRESS", "%s", unit->mlt_sensor_address)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_STATE", "%d", unit->mlt_sensor_state)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_SENSOR_VALUE", "%d", unit->mlt_sensor_value)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->mlt_heater_address) {
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_HEATER_ADDRESS", "%s", unit->mlt_heater_address)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_HEATER_STATE", "%d", unit->mlt_heater_state)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_HEATER_DELAY", "%d", unit->mlt_heater_delay)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->mlt_pump_address) {
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_PUMP_ADDRESS", "%s", unit->mlt_pump_address)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_PUMP_STATE", "%d", unit->mlt_pump_state)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MLT_PUMP_DELAY", "%d", unit->mlt_pump_delay)) < 0)) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->PID_hlt) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_IMAX", "%.2f", unit->PID_hlt->iMax)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_IGAIN", "%.2f", unit->PID_hlt->iGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_PGAIN", "%.2f", unit->PID_hlt->pGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_DGAIN", "%.2f", unit->PID_hlt->dGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_INPUT", "%.2f", unit->PID_hlt->Input)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_ERR", "%.2f", unit->PID_hlt->Err)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_ERRLAST", "%.2f", unit->PID_hlt->ErrLast)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_ISTATE", "%.2f", unit->PID_hlt->iState)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_SETP", "%.2f", unit->PID_hlt->SetP)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_OUTP", "%.2f", unit->PID_hlt->OutP)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_MODE", "%s", PIDMODE[unit->PID_hlt->Mode])) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_TYPE", "HEAT")) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if (unit->PID_mlt) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_IMAX", "%.2f", unit->PID_mlt->iMax)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_IGAIN", "%.2f", unit->PID_mlt->iGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_PGAIN", "%.2f", unit->PID_mlt->pGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_DGAIN", "%.2f", unit->PID_mlt->dGain)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_INPUT", "%.2f", unit->PID_mlt->Input)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_ERR", "%.2f", unit->PID_mlt->Err)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_ERRLAST", "%.2f", unit->PID_mlt->ErrLast)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_ISTATE", "%.2f", unit->PID_mlt->iState)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_SETP", "%.2f", unit->PID_mlt->SetP)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_OUTP", "%.2f", unit->PID_mlt->OutP)) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_MODE", "%s", PIDMODE[unit->PID_mlt->Mode])) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_TYPE", "HEAT")) < 0) {
+                    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                    return 1;
+                }
+            }
+
+            if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+                return 1;
+            }
+        }
+        if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+            return 1;
+        }
+    }
+
+    if (Config.devices) {
+        if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "DEVICES")) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+            return 1;
+        }
+#ifdef HAVE_WIRINGPI_H
+        piLock(LOCK_DEVICES);
+#endif
+        for (device = Config.devices; device; device = device->next) {
+            if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "DEVICE")) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", device->version)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", device->uuid)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TYPE", "%s", DEVTYPE[device->type])) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DIRECTION", "%s", DEVDIR[device->direction])) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VALUE", "%d", device->value)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "OFFSET", "%d", device->offset)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PRESENT", "%s", DEVPRESENT[device->present])) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ADDRESS", "%s", device->address)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SUBDEVICE", "%d", device->subdevice)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "GPIOPIN", "%d", device->gpiopin)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DESCRIPTION", "%s", device->description)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "INUSE", "%d", device->inuse)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COMMENT", "%s", device->comment)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TIMESTAMP", "%d", (int)device->timestamp)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;                           
+            }
+            if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+                return 1;
+            }
+        }
+#ifdef HAVE_WIRINGPI_H
+        piUnlock(LOCK_DEVICES);
+#endif
+
+        if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+            return 1;
+        }
+    }
+
+#ifdef USE_SIMULATOR
+    if (Config.simulators) {
+        if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATORS")) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+            return 1;
+        }
+        for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+            if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR")) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", simulator->version)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+            if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+                return 1;
+            }
+
+            if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+                syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+                return 1;
+            }
+        }
+        if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+            syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
+            return 1;
+        }
+    }
 #endif
 
     /*
@@ -255,7 +647,6 @@
 /*
  * Parse one LCD display
  */
-#ifdef HAVE_WIRINGPI_H
 int parseLCD(xmlDocPtr doc, xmlNodePtr cur)
 {
     xmlChar	*key;
@@ -300,13 +691,569 @@
     }
     return 0;
 }
+
+
+
+/*
+ * Parse a brewsystem
+ */
+int parseBrewsystem(xmlDocPtr doc, xmlNodePtr cur)
+{
+    xmlChar     *key;
+    int         i, ival;
+    float       val;
+    units_list  *unit, *tmp;
+
+    unit = (units_list *)malloc(sizeof(units_list));
+    unit->next = NULL;
+    unit->version = 1;
+    unit->uuid = unit->name = unit->hlt_sensor_address = unit->hlt_heater_address = unit->mlt_sensor_address = \
+                 unit->mlt_heater_address = unit->mlt_pump_address = NULL;
+    unit->hlt_sensor_state = unit->mlt_sensor_state = 1; // missing
+    unit->hlt_heater_state = unit->mlt_heater_state = unit->mlt_pump_state = 0;
+    unit->hlt_heater_delay = unit->mlt_heater_delay = unit->mlt_pump_delay = 20;     /* 5 minutes delay */
+    unit->PID_hlt = (pid_var *)malloc(sizeof(pid_var));
+    unit->PID_mlt = (pid_var *)malloc(sizeof(pid_var));
+    InitPID(unit->PID_hlt);
+    InitPID(unit->PID_mlt);
+
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (xmlStrcmp(key, (const xmlChar *)"1")) {
+                xmlFree(key);
+                return 1;
+            }
+            unit->version = 1;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
+            unit->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
+            unit->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_SENSOR_ADDRESS"))) {
+            unit->hlt_sensor_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_SENSOR_STATE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->hlt_sensor_state = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_SENSOR_VALUE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->hlt_sensor_value = ival;
+            xmlFree(key);                           
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_ADDRESS"))) {
+            unit->hlt_heater_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_DELAY"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->hlt_heater_delay = ival;
+            xmlFree(key);
+        }
+
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_SENSOR_ADDRESS"))) {
+            unit->mlt_sensor_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_SENSOR_STATE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->mlt_sensor_state = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_SENSOR_VALUE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->mlt_sensor_value = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_ADDRESS"))) {
+            unit->mlt_heater_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_DELAY"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->mlt_heater_delay = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_PUMP_ADDRESS"))) {
+            unit->mlt_pump_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_PUMP_DELAY"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                unit->mlt_pump_delay = ival;
+            xmlFree(key);
+        }
+
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_IMAX"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->iMax = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_IGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->iGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_PGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->pGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_DGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->dGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_INPUT"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->Input = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ERR"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->Err = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ERRLAST"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->ErrLast = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ISTATE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->iState = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_SETP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->SetP = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_OUTP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_hlt->OutP = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_MODE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 3; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
+                    unit->PID_hlt->Mode = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_IMAX"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->iMax = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_IGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->iGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_PGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->pGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_DGAIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->dGain = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_INPUT"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->Input = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ERR"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->Err = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ERRLAST"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->ErrLast = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ISTATE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->iState = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_SETP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->SetP = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_OUTP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &val) == 1)
+                unit->PID_mlt->OutP = val;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_MODE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 3; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
+                    unit->PID_mlt->Mode = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+
+        cur = cur->next;
+    }
+
+    if (Config.units == NULL) {
+        Config.units = unit;
+    } else {
+        for (tmp = Config.units; tmp; tmp = tmp->next) {
+            if (tmp->next == NULL) {
+                tmp->next = unit;
+                break;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+
+int parseBrewsystems(xmlDocPtr doc, xmlNodePtr cur)
+{
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"BREWSYSTEM"))) {
+            parseBrewsystem(doc, cur);
+        }
+        cur = cur->next;
+    }
+    return 0;
+}
+
+
+int parseDevice(xmlDocPtr doc, xmlNodePtr cur)
+{
+    xmlChar             *key;
+    devices_list        *device, *tmp;
+    int                 i, ival;
+
+    device = (devices_list *)malloc(sizeof(devices_list));
+    device->next = NULL;
+    device->version = 1;
+    device->uuid = device->address = device->description = device->comment = NULL;
+    device->type = device->direction = device->present = device->subdevice = device->inuse = device->offset = 0;
+    device->gpiopin = -1;
+    device->timestamp = (time_t)0;
+
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (xmlStrcmp(key, (const xmlChar *)"1")) {
+                xmlFree(key);
+                return 1;
+            }
+            device->version = 1;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
+            device->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 8; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)DEVTYPE[i])) {
+                    device->type = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DIRECTION"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 7; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)DEVDIR[i])) {
+                    device->direction = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"VALUE"))) {
+                key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+                if (sscanf((const char *)key, "%d", &ival) == 1)
+                        device->value = ival;
+                xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"OFFSET"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                device->offset = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRESENT"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 4; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) {
+                    device->present = i;
+                    break;
+                }
+            }
+            xmlFree(key);                           
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"ADDRESS"))) {
+            device->address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"SUBDEVICE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                device->subdevice = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"GPIOPIN"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                device->gpiopin = ival;
+            xmlFree(key);                                       
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DESCRIPTION"))) {
+            device->description = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"INUSE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                device->inuse = ival;
+            xmlFree(key);                                       
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"COMMENT"))) {
+            device->comment = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIMESTAMP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                device->timestamp = (time_t)ival;
+            xmlFree(key);                                   
+        }
+
+        cur = cur->next;
+    }
+
+    if (Config.devices == NULL) {
+        Config.devices = device;
+    } else {
+        for (tmp = Config.devices; tmp; tmp = tmp->next) {
+            if (tmp->next == NULL) {
+                tmp->next = device;
+                break;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+
+int parseDevices(xmlDocPtr doc, xmlNodePtr cur)
+{
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICE"))) {
+            parseDevice(doc, cur);
+        }
+        cur = cur->next;
+    }
+    return 0;
+}
+
+
+
+#ifdef USE_SIMULATOR
+int parseSimulator(xmlDocPtr doc, xmlNodePtr cur)
+{
+    xmlChar             *key;
+    simulator_list      *simulator, *tmp;
+    int                 ival;
+    float               fval;
+
+    simulator = (simulator_list *)malloc(sizeof(simulator_list));
+    simulator->next = NULL;
+    simulator->version = 1;
+    simulator->uuid = simulator->name = NULL;
+    simulator->room_temperature = simulator->hlt_heater_temp = simulator->mlt_heater_temp = simulator->s_hlt_temp = simulator->s_mlt_temp = 20.0;
+    simulator->hlt_heater_temp = simulator->hlt_heater_size = simulator->mlt_heater_temp = simulator->mlt_heater_size = 0.0;
+    simulator->hlt_heater_time = simulator->mlt_heater_time = simulator->hlt_heater_state = simulator->mlt_heater_state = 0;
+    simulator->s_hlt_temp = simulator->s_mlt_temp = 0.0;
+
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (xmlStrcmp(key, (const xmlChar *)"1")) {
+                xmlFree(key);
+                return 1;
+            }
+            simulator->version = 1;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
+            simulator->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
+            simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPERATURE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->room_temperature = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_TEMPERATURE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->hlt_temperature = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->hlt_heater_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_TIME"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->hlt_heater_time = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_SIZE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->hlt_heater_size = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_HEATER_STATE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->hlt_heater_state = ival;
+	    xmlFree(key);
+	}
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_TEMPERATURE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &fval) == 1)
+                simulator->mlt_temperature = fval;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_TEMP"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &fval) == 1)
+                simulator->mlt_heater_temp = fval;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_TIME"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                simulator->mlt_heater_time = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_SIZE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%f", &fval) == 1)
+                simulator->mlt_heater_size = fval;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_HEATER_STATE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                simulator->mlt_heater_state = ival;
+            xmlFree(key);
+        }
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HLT_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->s_hlt_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_MLT_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->s_mlt_temp = fval;
+	    xmlFree(key);
+	}
+        cur = cur->next;
+    }
+
+    if (Config.simulators == NULL) {
+        Config.simulators = simulator;
+    } else {
+        for (tmp = Config.simulators; tmp; tmp = tmp->next) {
+            if (tmp->next == NULL) {
+                tmp->next = simulator;
+                break;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+
+int parseSimulators(xmlDocPtr doc, xmlNodePtr cur)
+{
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATOR"))) {
+            parseSimulator(doc, cur);
+        }
+        cur = cur->next;
+    }
+    return 0;
+}
 #endif
 
 
 
 int rdconfig(void) 
 {
-    int		rc = 0;
+    int		ival, rc = 0;
     char	*mypath;
     xmlDocPtr	doc;
     xmlNodePtr	cur;
@@ -369,21 +1316,36 @@
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
 	    Config.name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LISTEN_PORT"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		Config.my_port = ival;
+	    xmlFree(key);
+	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMPFORMAT"))) {
 	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	    Config.tempFormat = key[0];
 	    xmlFree(key);
 	}
-	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HLT_SENSOR_ADDRESS"))) {
-	    Config.hlt_sensor_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMP_ADDRESS"))) {
+	    Config.temp_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	}
-	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MLT_SENSOR_ADDRESS"))) {
-	    Config.mlt_sensor_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HUM_ADDRESS"))) {
+	    Config.hum_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	}
-#ifdef HAVE_WIRINGPI_H
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LCDS"))) {
 	    parseLCDs(doc, cur);
 	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BREWSYSTEMS"))) {
+	    parseBrewsystems(doc, cur);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICES"))) {
+	    parseDevices(doc, cur);
+	}
+#ifdef USE_SIMULATOR
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATORS"))) {
+	    parseSimulators(doc, cur);
+	}
 #endif
 	cur = cur->next;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/slcd.c	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,210 @@
+/*****************************************************************************
+ * Copyright (C) 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 "brewco.h"
+#include "slcd.h"
+#include "futil.h"
+#include "xutil.h"
+
+
+
+struct sockaddr_in	sendaddr;	/* Server send socket		*/
+int			sock = -1;
+uint16_t		keys = 0x0000;
+uint16_t		leds = 0x0400;	/* LED's, buzzer, LCD backlight	*/
+uint16_t		oleds = 0x0400;
+
+extern int		debug;
+extern sys_config       Config;
+
+
+
+void putLCDsocket(int fd, uint16_t data)
+{
+    socklen_t	slen;
+    uint16_t	rdat;
+
+    if (sock == -1)
+	return;
+
+    if (sendto(sock, &data, sizeof(uint16_t), 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr)) != sizeof(uint16_t)) {
+	syslog(LOG_NOTICE, "Socket %d send failed, closing socket: %s", sock, strerror(errno));
+	if (shutdown(sock, SHUT_RDWR)) {
+	    syslog(LOG_NOTICE, "Can't shutdown socket: %s", strerror(errno));
+	}
+	sock = -1;
+    } else {
+	if (recvfrom(sock, &rdat, sizeof(uint16_t), 0, (struct sockaddr *) &sendaddr, &slen) != sizeof(uint16_t)) {
+	    syslog(LOG_NOTICE, "Socket %d recvfrom failed, closing socket: %s", sock, strerror(errno));
+	    if (shutdown(sock, SHUT_RDWR)) {
+		syslog(LOG_NOTICE, "Can't shutdown socket: %s", strerror(errno));
+	    }
+	    sock = -1;
+	} else {
+	    if ((rdat & SLCD_MKEYS) == SLCD_KEYS) {
+		if (((rdat & 0x00ff) != keys) && debug)
+		    fprintf(stdout, "received keys %04x was %04x\n", rdat & 0x00ff, keys);
+		keys = rdat & 0x00ff;
+	    } else {
+	        if (debug)
+		    fprintf(stdout, "received %04x\n", rdat);
+	    }
+
+	}
+    }
+}
+
+
+
+void slcdDummy(int fd)
+{
+    putLCDsocket(fd, SLCD_NULL);
+}
+
+
+
+void slcdLEDs(int fd)
+{
+    if (leds != oleds)
+	putLCDsocket(fd, leds);
+    oleds = leds;
+}
+
+
+//void slcdHome(int fd)
+//{
+//}
+
+
+
+void slcdClear(int fd)
+{
+    putLCDsocket(fd, SLCD_CLEAR);
+    putLCDsocket(fd, SLCD_HOME);
+}
+
+
+
+//void slcdDisplay(int fd, int state)
+//{
+//}
+
+
+
+//void slcdCursor(int fd, int state)
+//{
+//}
+
+
+
+//void slcdCursorBlink(int fd, int state)
+//{
+//}
+
+
+
+//void slcdSendCommand(int fd, unsigned char command)
+//{
+//}
+
+
+
+void slcdPosition(int fd, int x, int y)
+{
+    uint16_t	data = SLCD_DGRAM;
+
+    data += (x & 0x1f) + ((y & 0x03) << 5);
+    putLCDsocket(fd, data);
+}
+
+
+
+//void slcdCharDef(int fd, int index, unsigned char data[8])
+//{
+//}
+
+
+
+void slcdPutchar(int fd, unsigned char c)
+{
+    uint16_t	data = SLCD_DATA;
+
+    data += c & 0x0ff;
+    putLCDsocket(fd, data);
+}
+
+
+
+void slcdPuts(int fd, const char *string)
+{
+    while (*string)
+	slcdPutchar(fd, *string++);
+}
+
+
+
+void slcdPrintf(int fd, const char *message, ...)
+{
+    char	buf[81 * sizeof(char)];
+    va_list	va_ptr;
+
+    va_start(va_ptr, message);
+    vsnprintf(buf, (Config.lcd_cols + 1) * sizeof(char), message, va_ptr);
+    va_end(va_ptr);
+    slcdPuts(fd, buf);
+}
+
+
+
+/*
+ * Try to setup a udp connection to 127.0.0.1 so we duplicate the panel
+ * display and keys of the real panel. This should fail on a production
+ * system because there should no brewpanel program be running. If it
+ * succeeds, all io will be duplicated over the network.
+ */
+int slcdInit(int fd, int cols, int rows)
+{
+    if ((sock = socket(AF_INET, SOCK_DGRAM /*| SOCK_NONBLOCK */, 0)) < 0) {
+	syslog(LOG_NOTICE, "slcdInit() can't create socket: %s", strerror(errno));
+	return -1;
+    }
+
+    /*
+     * Setup address structure for the server socket.
+     */
+    memset(&sendaddr, 0, sizeof(struct sockaddr_in));
+    sendaddr.sin_family = AF_INET;
+    sendaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
+    sendaddr.sin_port = htons(SEND_PORT);
+
+    if (connect(sock, (struct sockaddr *)&sendaddr, sizeof(sendaddr)) < 0) {
+	close(sock);
+	sock = -1;
+	syslog(LOG_NOTICE, "slcdInit() can't bind sendsock %s", strerror(errno));
+	return -1;
+    }
+
+    syslog(LOG_NOTICE, "slcdInit() socket %d to brewpanel is active", sock);
+    return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/slcd.h	Wed Nov 25 22:49:35 2015 +0100
@@ -0,0 +1,15 @@
+#ifndef	_SLCD_H
+#define	_SLCD_H
+
+
+void slcdDummy(int fd);
+void slcdLEDs(int fd);
+void slcdClear(int fd);
+void slcdPosition(int fd, int x, int y);
+void slcdPutchar(int fd, unsigned char c);
+void slcdPuts(int fd, const char *string);
+void slcdPrintf(int fd, const char *message, ...);
+int  slcdInit(int fd, int cols, int rows);
+
+
+#endif

mercurial