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

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

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

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

configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
thermferm/devices.c file | annotate | diff | comparison | revisions
thermferm/pid.c file | annotate | diff | comparison | revisions
thermferm/pid.h file | annotate | diff | comparison | revisions
thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/simulator.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
thermferm/thermferm.h file | annotate | diff | comparison | revisions
www-thermferm/devices.php file | annotate | diff | comparison | revisions
www-thermferm/units.php file | annotate | diff | comparison | revisions
www-thermferm/utilities.php file | annotate | diff | comparison | revisions
--- a/configure	Thu May 14 22:03:35 2015 +0200
+++ b/configure	Sat May 16 17:39:30 2015 +0200
@@ -2034,7 +2034,7 @@
 
 
 PACKAGE="mbsePi-apps"
-VERSION="0.2.13"
+VERSION="0.3.0"
 COPYRIGHT="Copyright (C) 2014-2015 Michiel Broek, All Rights Reserved"
 CYEARS="2014-2015"
 
--- a/configure.ac	Thu May 14 22:03:35 2015 +0200
+++ b/configure.ac	Sat May 16 17:39:30 2015 +0200
@@ -8,7 +8,7 @@
 dnl General settings
 dnl After changeing the version number, run autoconf!
 PACKAGE="mbsePi-apps"
-VERSION="0.2.13"
+VERSION="0.3.0"
 COPYRIGHT="Copyright (C) 2014-2015 Michiel Broek, All Rights Reserved"
 CYEARS="2014-2015"
 AC_SUBST(PACKAGE)
--- a/thermferm/devices.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/devices.c	Sat May 16 17:39:30 2015 +0200
@@ -31,6 +31,12 @@
 extern sys_config	Config;
 extern int		my_shutdown;
 
+#ifdef USE_SIMULATOR
+
+extern int	SIMcooling;
+extern int	SIMheating;
+
+#endif
 
 #ifdef HAVE_WIRINGPI_H
 
@@ -351,7 +357,19 @@
 
 #ifdef USE_SIMULATOR
 		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
-
+		    if ((strcmp((char *)"SimCooler", device->address) == 0) || (strcmp((char *)"SimHeater", 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;
+			device->timestamp = time(NULL);
+			if (strcmp((char *)"SimCooler", device->address) == 0)
+			    SIMcooling = value;
+			if (strcmp((char *)"SimHeater", device->address) == 0)
+			    SIMheating = value;
+		    }
 		}
 #endif
 	    } else {
--- a/thermferm/pid.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/pid.c	Sat May 16 17:39:30 2015 +0200
@@ -24,27 +24,63 @@
 #include "pid.h"
 
 
-double UpdatePID(pid_var *pid, double error, double position)
+void InitPID(pid_var *pid, int type)
 {
-    double	pTerm, dTerm, iTerm;
-
-    pTerm = pid->pGain * error;
-
-    /*
-     * Calculate the integral state with appopriate limiting
-     */
-    pid->iState += error;
-    if (pid->iState > pid->iMax)
-	pid->iState = pid->iMax;
-    else if (pid->iState < pid->iMin)
-	pid->iState = pid->iMin;
-
-    iTerm = pid->iGain * pid->iState;
-
-    dTerm = pid->dGain * (pid->dState - position);
-    pid->dState = position;
-
-    return pTerm + dTerm + iTerm;
+    pid->Err = pid->ErrLast = pid->ErrLastLast = 0.0;
+    pid->Input = pid->InputD = pid->OutP = pid->InputLast = pid->SetP = 0.0;
+    pid->pGain = pid->iGain = pid->dGain = 0.0;
+    pid->idleRange = 0.4;
+    pid->Mode = PID_MODE_NONE;
+    pid->Type = type;
+    pid->iMax = 100.0;
 }
 
 
+
+void UpdatePID(pid_var *pid)
+{
+    if (pid->Mode == PID_MODE_AUTO) {
+
+	pid->InputD = pid->Input + (pid->Input - pid->InputLast) * pid->dGain * PID_TIMES;
+	pid->InputLast = pid->Input;
+	if (pid->Type == PID_TYPE_HEAT)
+	    pid->Err = pid->InputD - pid->SetP;
+	else
+	    pid->Err = pid->SetP - pid->InputD;
+
+	pid->OutP = pid->OutP + pid->pGain * (pid->Err - pid->ErrLast + pid->iGain * pid->Err + pid->dGain * (pid->Err - pid->ErrLast * 2 + pid->ErrLastLast));
+	pid->ErrLastLast = pid->ErrLast;
+	pid->ErrLast = pid->Err;
+
+    } if (pid->Mode == PID_MODE_BOO) {
+	/*
+	 * Mode Bang On Off
+	 */
+	pid->InputLast = pid->Input;
+	if (pid->Type == PID_TYPE_HEAT)
+	    pid->ErrLastLast = pid->ErrLast = pid->Err = pid->SetP - pid->Input;
+	else
+	    pid->ErrLastLast = pid->ErrLast = pid->Err = pid->Input - pid->SetP;
+
+	if (pid->Err > 0.0)
+	    pid->OutP = pid->iMax;
+	else
+	    pid->OutP = 0.0;
+
+    } else {
+	/*
+	 * While in manual mode, stay ready for bumpless switch to
+	 * auto.
+	 */
+	pid->InputLast = pid->Input;
+	pid->ErrLastLast = pid->ErrLast = pid->Err;
+    }
+
+    if (pid->OutP > pid->iMax)
+	pid->OutP = pid->iMax;
+    if (pid->OutP < 0.0)
+	pid->OutP = 0.0;
+
+}
+
+
--- a/thermferm/pid.h	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/pid.h	Sat May 16 17:39:30 2015 +0200
@@ -1,17 +1,37 @@
 #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_TYPE_HEAT		0	/* PID is used for heating			*/
+#define	PID_TYPE_COOL		1	/* PID is used for cooling			*/
+
+#define	PID_TIMES		60	/* 60 calculations per minute			*/
+
+
 typedef struct _pid_var {
-	double		iState;		/* Integrator state			*/
-	double		dState;		/* Last measured value input		*/
-	double		iMax;		/* Maximum allowable integrator state	*/
-	double		iMin;		/* Minimum allowable integrator state	*/
-	double		iGain;		/* Integral gain			*/
-	double		pGain;		/* Proportional gain			*/
-	double		dGain;		/* Derivative gain			*/
+	double		iMax;		/* Maximum allowable integrator state		*/
+	double		iGain;		/* Integral gain				*/
+	double		pGain;		/* Proportional gain				*/
+	double		dGain;		/* Derivative gain				*/
+	double		idleRange;	/* Idle range					*/
+
+	double		Input;		/* Input value					*/
+	double		InputD;		/* Process input plus derivative		*/
+	double		InputLast;	/* Process input from last pass			*/
+	double		Err;		/* Error, diff between input and set point	*/
+	double		ErrLast;	/* Error from last pass				*/
+	double		ErrLastLast;	/* 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	*/
+	int		Type;		/* Value is 'HEAT' or 'COOL'			*/
 } pid_var;
 
 
-double UpdatePID( pid_var *, double, double);
+void InitPID( pid_var *, int);
+void UpdatePID( pid_var *);
 
 #endif
--- a/thermferm/rdconfig.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/rdconfig.c	Sat May 16 17:39:30 2015 +0200
@@ -37,7 +37,7 @@
 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)
@@ -461,26 +461,6 @@
 		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
 		return 1;
 	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "IDLE_RANGE_L", "%.2f", tmp3->idle_rangeL)) < 0) {
-		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		return 1;
-	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "IDLE_RANGE_H", "%.2f", tmp3->idle_rangeH)) < 0) {
-		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		return 1;
-	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_KP", "%.2f", tmp3->PID_Kp)) < 0) {
-		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		return 1;
-	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_KD", "%.2f", tmp3->PID_Kd)) < 0) {
-		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		return 1;
-	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_KI", "%.2f", tmp3->PID_Ki)) < 0) {
-		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		return 1;
-	    }
 	    if (tmp3->profile) {
 		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROFILE", "%s", tmp3->profile)) < 0) {
 		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
@@ -511,6 +491,130 @@
 		    return 1;
 		}
 	    }
+	    if (tmp3->PID_cool) {
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IMAX", "%.2f", tmp3->PID_cool->iMax)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IGAIN", "%.2f", tmp3->PID_cool->iGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+	        if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_PGAIN", "%.2f", tmp3->PID_cool->pGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+	        if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_DGAIN", "%.2f", tmp3->PID_cool->dGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IDLERANGE", "%.2f", tmp3->PID_cool->idleRange)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUT", "%.2f", tmp3->PID_cool->Input)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUTD", "%.2f", tmp3->PID_cool->InputD)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUTLAST", "%.2f", tmp3->PID_cool->InputLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERR", "%.2f", tmp3->PID_cool->Err)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERRLAST", "%.2f", tmp3->PID_cool->ErrLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERRLASTLAST", "%.2f", tmp3->PID_cool->ErrLastLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_SETP", "%.2f", tmp3->PID_cool->SetP)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_OUTP", "%.2f", tmp3->PID_cool->OutP)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_MODE", "%s", PIDMODE[tmp3->PID_cool->Mode])) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_TYPE", "COOL")) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+	    }
+	    if (tmp3->PID_heat) {
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IMAX", "%.2f", tmp3->PID_heat->iMax)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IDLERANGE", "%.2f", tmp3->PID_heat->idleRange)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IGAIN", "%.2f", tmp3->PID_heat->iGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_PGAIN", "%.2f", tmp3->PID_heat->pGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_DGAIN", "%.2f", tmp3->PID_heat->dGain)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUT", "%.2f", tmp3->PID_heat->Input)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUTD", "%.2f", tmp3->PID_heat->InputD)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUTLAST", "%.2f", tmp3->PID_heat->InputLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERR", "%.2f", tmp3->PID_heat->Err)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERRLAST", "%.2f", tmp3->PID_heat->ErrLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERRLASTLAST", "%.2f", tmp3->PID_heat->ErrLastLast)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_SETP", "%.2f", tmp3->PID_heat->SetP)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_OUTP", "%.2f", tmp3->PID_heat->OutP)) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_MODE", "%s", PIDMODE[tmp3->PID_heat->Mode])) < 0) {
+		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		    return 1;
+		}
+		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_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;
@@ -929,11 +1033,12 @@
     unit->heater_usage = unit->cooler_usage = unit->fan_usage = unit->light_usage = 0;
     unit->temp_set_min = 1.0;
     unit->temp_set_max = 30.0;
-    unit->idle_rangeH = 1.0;
-    unit->idle_rangeL = -1.0;
     unit->prof_started = unit->prof_paused = unit->prof_primary_done = (time_t)0;
     unit->prof_percent = 0;
-    unit->PID_dState = unit->PID_iState = unit->PID_Kp = unit->PID_Kd = unit->PID_Ki = 0.0;
+    unit->PID_cool = (pid_var *)malloc(sizeof(pid_var));
+    unit->PID_heat = (pid_var *)malloc(sizeof(pid_var));
+    InitPID(unit->PID_cool, PID_TYPE_COOL);
+    InitPID(unit->PID_heat, PID_TYPE_HEAT);
 
     cur = cur->xmlChildrenNode;
     while (cur != NULL) {
@@ -1088,34 +1193,198 @@
 		unit->temp_set_max = val;
 	    xmlFree(key);
 	}
-	if ((!xmlStrcmp(cur->name, (const xmlChar *)"IDLE_RANGE_L"))) {
-	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-	    if (sscanf((const char *)key, "%f", &val) == 1)
-		unit->idle_rangeL = val;
-	    xmlFree(key);
-	}
-	if ((!xmlStrcmp(cur->name, (const xmlChar *)"IDLE_RANGE_H"))) {
-	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-	    if (sscanf((const char *)key, "%f", &val) == 1)
-		unit->idle_rangeH = val;
-	    xmlFree(key);
-	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KP"))) {
 	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	    if (sscanf((const char *)key, "%f", &val) == 1)
-		unit->PID_Kp = val;
+		unit->PID_cool->pGain = unit->PID_heat->pGain = val;	/* Upgrade config	*/
 	    xmlFree(key);
 	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KD"))) {
 	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	    if (sscanf((const char *)key, "%f", &val) == 1)
-		unit->PID_Kd = val;
+		unit->PID_cool->dGain = unit->PID_heat->dGain = val;	/* Upgrade config       */
 	    xmlFree(key);
 	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KI"))) {
 	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 	    if (sscanf((const char *)key, "%f", &val) == 1)
-		unit->PID_Ki = val;
+		unit->PID_cool->iGain = unit->PID_heat->iGain = val;	/* Upgrade config       */
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IMAX"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->iMax = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IDLERANGE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->idleRange = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->iGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_PGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->pGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_DGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->dGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUT"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->Input = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUTD"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->InputD = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUTLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->InputLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERR"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->Err = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERRLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->ErrLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERRLASTLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->ErrLastLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_SETP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->SetP = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_OUTP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_cool->OutP = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_MODE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    for (i = 0; i < 3; i++) {
+		if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
+		    unit->PID_cool->Mode = i;
+		    break;
+	    	}
+	    }
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IMAX"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->iMax = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IDLERANGE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->idleRange = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->iGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_PGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->pGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_DGAIN"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->dGain = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUT"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->Input = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUTD"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->InputD = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUTLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->InputLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERR"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->Err = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERRLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->ErrLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERRLASTLAST"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->ErrLastLast = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_SETP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->SetP = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_OUTP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &val) == 1)
+		unit->PID_heat->OutP = val;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_MODE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    for (i = 0; i < 3; i++) {
+		if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
+		    unit->PID_heat->Mode = i;
+		    break;
+		}
+	    }
 	    xmlFree(key);
 	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROFILE"))) {
--- a/thermferm/server.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/server.c	Sat May 16 17:39:30 2015 +0200
@@ -112,6 +112,50 @@
 
 
 /*
+ * Argument is a buffer of size SS_BUFSIZE.
+ * Return -1 if error, else the number of received
+ * character. \n is line end, ignore \r.
+ */
+int srv_recv(char *buffer)
+{
+    int			bytesloaded = 0;
+    ssize_t		ret;
+    unsigned char	buf;
+    socklen_t		fromlen;
+
+    memset(buffer, 0, SS_BUFSIZE);
+
+    while(1) {
+	/*
+	 * read a single byte
+	 */
+	fromlen = sizeof(peeraddr_in);
+	ret = recvfrom(s, &buf, 1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+	if (ret < 1) {
+	    syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
+	    srv_send((char *)"518 recfrom(): %s", strerror(errno));
+	    return -1;	/* error */
+	}
+
+	if (buf == '\n')
+	    break;
+
+	if (buf != '\r') {
+	    buffer[bytesloaded] = buf; 
+	    bytesloaded++;
+	}
+    } 
+
+    if (debug) {
+	syslog(LOG_NOTICE, "recv: %d `%s'", bytesloaded, buffer);
+	fprintf(stdout, "recv: %d `%s'\n", bytesloaded, buffer);
+    }
+    return bytesloaded;
+}
+
+
+
+/*
  * Update the device inuse counter.
  */
 void device_count(int plus, char *uuid)
@@ -578,7 +622,6 @@
 {
     char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
     devices_list	*device, *tmpd;
-    socklen_t		fromlen;
     int			i, rc, rlen, ival;
     uuid_t		uu;
 
@@ -711,31 +754,11 @@
 	for (device = Config.devices; device; device = device->next) {
 	    if (strcmp(device->uuid, param) == 0) {
 		while (1) {
-		    memset((char *)&ibuf, 0, SS_BUFSIZE);
-		    fromlen = sizeof(peeraddr_in);
-		    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
-		    if (rlen == -1) {
-			syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-			srv_send((char *)"518 recfrom(): %s", strerror(errno));
+		    rlen = srv_recv(ibuf);
+    		    if (rlen == -1) {
 			return 1;
 		    }
-		    for (i = 0; i < strlen(ibuf); i++) {
-			if (ibuf[i] == '\n')
-			    ibuf[i] = '\0';
-			if (ibuf[i] == '\r')
-			    ibuf[i] = '\0';
-		    }
-		    for (i = strlen(ibuf) -1; i > 0; i--) {
-			if (ibuf[i] == ' ')
-			    ibuf[i] = '\0';
-			else
-			    break;
-		    }
 		    if (strlen(ibuf)) {
-			if (debug) {
-			    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-			    fprintf(stdout, "recv: \"%s\"\n", ibuf);
-			}
 			if (strcmp(ibuf, (char *)".") == 0) {
 			    srv_send((char *)"219 Accepted Device record");
 			    return 0;
@@ -908,8 +931,7 @@
 int cmd_global(char *buf)
 {
     char	*opt, *kwd, *val, ibuf[SS_BUFSIZE];
-    int		ival, i, rlen;
-    socklen_t	fromlen;
+    int		ival, rlen;
 
     opt = strtok(buf, " \0");
     opt = strtok(NULL, "\0");
@@ -941,31 +963,11 @@
 
     if (strcmp(opt, (char *)"PUT") == 0) {
 	while (1) {
-	    memset((char *)&ibuf, 0, SS_BUFSIZE);
-	    fromlen = sizeof(peeraddr_in);
-	    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+	    rlen = srv_recv(ibuf);
 	    if (rlen == -1) {
-		syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-		srv_send((char *)"518 recfrom(): %s", strerror(errno));
 		return 1;
 	    }
-	    for (i = 0; i < strlen(ibuf); i++) {
-		if (ibuf[i] == '\n')
-		    ibuf[i] = '\0';
-		if (ibuf[i] == '\r')
-		    ibuf[i] = '\0';
-	    }
-	    for (i = strlen(ibuf) -1; i > 0; i--) {
-		if (ibuf[i] == ' ')
-		    ibuf[i] = '\0';
-		else
-		    break;
-	    }
 	    if (strlen(ibuf)) {
-		if (debug) {
-		    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-		    fprintf(stdout, "recv: \"%s\"\n", ibuf);
-		}
 		if (strcmp(ibuf, (char *)".") == 0) {
 		    srv_send((char *)"219 Accepted Global record");
 		    return 0;
@@ -1206,9 +1208,8 @@
 int cmd_profile(char *buf)
 {
     char                ibuf[SS_BUFSIZE], *sstep, *rest, *targ, *param, *kwd, *val;
-    int                 i, j, rlen, istep, irest;
+    int                 j, rlen, istep, irest;
     float		ftarg, fval;
-    socklen_t           fromlen;
     char		*opt;
     profiles_list	*profile, *tmpp;
     prof_step		*step, *olds;
@@ -1298,31 +1299,11 @@
 	for (profile = Config.profiles; profile; profile = profile->next) {
 	    if (strcmp(profile->uuid, param) == 0) {
 		while (1) {
-		    memset((char *)&ibuf, 0, SS_BUFSIZE);
-		    fromlen = sizeof(peeraddr_in);
-		    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+		    rlen = srv_recv(ibuf);
 		    if (rlen == -1) {
-			syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-			srv_send((char *)"518 recfrom(): %s", strerror(errno));
 			return 1;
 		    }
-		    for (i = 0; i < strlen(ibuf); i++) {
-			if (ibuf[i] == '\n')
-			    ibuf[i] = '\0';
-			if (ibuf[i] == '\r')
-			    ibuf[i] = '\0';
-		    }
-		    for (i = strlen(ibuf) -1; i > 0; i--) {
-			if (ibuf[i] == ' ')
-			    ibuf[i] = '\0';
-			else
-			    break;
-		    }
 		    if (strlen(ibuf)) {
-			if (debug) {
-			    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-			    fprintf(stdout, "recv: \"%s\"\n", ibuf);
-			}
 			if (strcmp(ibuf, (char *)".") == 0) {
 			    srv_send((char *)"219 Accepted Profile record");
 			    return 0;
@@ -1384,31 +1365,11 @@
 
 		j = 0;
             	while (1) {	
-	    	    memset((char *)&ibuf, 0, SS_BUFSIZE);
-            	    fromlen = sizeof(peeraddr_in);
-	    	    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+		    rlen = srv_recv(ibuf);
 	    	    if (rlen == -1) {
-	    	    	syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-		    	srv_send((char *)"518 recfrom(): %s", strerror(errno));
 		    	return 1;
 	    	    } else {
-	    	    	for (i = 0; i < strlen(ibuf); i++) {
-		            if (ibuf[i] == '\n')
-		    	    	ibuf[i] = '\0';
-		    	    if (ibuf[i] == '\r')
-		    	    	ibuf[i] = '\0';
-	                }
-	                for (i = strlen(ibuf) -1; i > 0; i--) {
-		    	    if (ibuf[i] == ' ')
-		    	    	ibuf[i] = '\0';
-		    	    else
-		     	    	break;
-	    	    	}
 	    	    	if (strlen(ibuf)) {
-		    	    if (debug) {
-		    	    	syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-		    	    	fprintf(stdout, "recv: \"%s\"\n", ibuf);
-		    	    }
 		    	    if (strcmp(ibuf, (char *)".") == 0) {
 
 		    	    	srv_send((char *)"219 Accepted Profile steps");
@@ -1506,8 +1467,7 @@
 {
     char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
     simulator_list	*simulator, *tmps;
-    socklen_t		fromlen;
-    int			i, rc, rlen, ival;
+    int			rc, rlen, ival;
     float		fval;
     uuid_t		uu;
 
@@ -1624,31 +1584,11 @@
 	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
 	    if (strcmp(simulator->uuid, param) == 0) {
 		while (1) {
-		    memset((char *)&ibuf, 0, SS_BUFSIZE);
-		    fromlen = sizeof(peeraddr_in);
-		    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+		    rlen = srv_recv(ibuf);
 		    if (rlen == -1) {
-			syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-			srv_send((char *)"518 recfrom(): %s", strerror(errno));
 			return 1;
 		    }
-		    for (i = 0; i < strlen(ibuf); i++) {
-			if (ibuf[i] == '\n')
-			    ibuf[i] = '\0';
-			if (ibuf[i] == '\r')
-			    ibuf[i] = '\0';
-		    }
-		    for (i = strlen(ibuf) -1; i > 0; i--) {
-			if (ibuf[i] == ' ')
-			    ibuf[i] = '\0';
-			else
-			    break;
-		    }
 		    if (strlen(ibuf)) {
-			if (debug) {
-			    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-			    fprintf(stdout, "recv: \"%s\"\n", ibuf);
-			}
 			if (strcmp(ibuf, (char *)".") == 0) {
 			    srv_send((char *)"219 Accepted Simulator record");
 			    return 0;
@@ -1820,6 +1760,12 @@
 		if (current->profile)
 		    free(current->profile);
 		current->profile = NULL;
+		if (current->PID_cool)
+		    free(current->PID_cool);
+		current->PID_cool = NULL;
+		if (current->PID_heat)
+		    free(current->PID_heat);
+		current->PID_heat = NULL;
 		free(current);
 		return 1;
 	    } else {
@@ -1854,6 +1800,12 @@
 		if (current->profile)
 		    free(current->profile);
 		current->profile = NULL;
+		if (current->PID_cool)
+		    free(current->PID_cool);
+		current->PID_cool = NULL;
+		if (current->PID_heat)
+		    free(current->PID_heat);
+		current->PID_heat = NULL;
 		previous->next = current->next;
 		free(current);
 		current = previous->next;
@@ -1881,7 +1833,6 @@
     char                *opt, *param = NULL, *kwd, *val, ibuf[SS_BUFSIZE];
     units_list          *unit, *tmpu;
     uuid_t		uu;
-    socklen_t		fromlen;
     int			ival, i, rc, rlen;
     float		fval;
 
@@ -1931,11 +1882,12 @@
 	unit->heater_usage = unit->cooler_usage = unit->fan_usage = unit->light_usage = 0;
 	unit->temp_set_min = 1.0;
 	unit->temp_set_max = 30.0;
-	unit->idle_rangeH = 1.0;
-	unit->idle_rangeL = -1.0;
 	unit->prof_started = unit->prof_paused = unit->prof_primary_done = (time_t)0;
 	unit->prof_percent = 0;
-	unit->PID_dState = unit->PID_iState = unit->PID_Kp = unit->PID_Kd = unit->PID_Ki = 0.0;
+	unit->PID_cool = (pid_var *)malloc(sizeof(pid_var));
+	unit->PID_heat = (pid_var *)malloc(sizeof(pid_var));
+	InitPID(unit->PID_cool, PID_TYPE_COOL);
+	InitPID(unit->PID_heat, PID_TYPE_HEAT);
 
 	/*
 	 * Block main process
@@ -2011,10 +1963,24 @@
 		srv_send((char *)"HEATER_STATE,%d", unit->heater_state);
 		srv_send((char *)"HEATER_DELAY,%d", unit->heater_delay);
 		srv_send((char *)"HEATER_USAGE,%d", unit->heater_usage);
+		if (unit->PID_heat) {
+		    srv_send((char *)"PIDH_IMAX,%.1f", unit->PID_heat->iMax);
+		    srv_send((char *)"PIDH_PGAIN,%.2f", unit->PID_heat->pGain);
+		    srv_send((char *)"PIDH_IGAIN,%.2f", unit->PID_heat->iGain);
+		    srv_send((char *)"PIDH_DGAIN,%.2f", unit->PID_heat->dGain);
+		    srv_send((char *)"PIDH_IDLERANGE,%.2f", unit->PID_heat->idleRange);
+		}
 		srv_send((char *)"COOLER_ADDRESS,%s", unit->cooler_address);
 		srv_send((char *)"COOLER_STATE,%d", unit->cooler_state);
 		srv_send((char *)"COOLER_DELAY,%d", unit->cooler_delay);
 		srv_send((char *)"COOLER_USAGE,%d", unit->cooler_usage);
+		if (unit->PID_cool) {
+		    srv_send((char *)"PIDC_IMAX,%.1f", unit->PID_cool->iMax);
+		    srv_send((char *)"PIDC_PGAIN,%.2f", unit->PID_cool->pGain);
+		    srv_send((char *)"PIDC_IGAIN,%.2f", unit->PID_cool->iGain);
+		    srv_send((char *)"PIDC_DGAIN,%.2f", unit->PID_cool->dGain);
+		    srv_send((char *)"PIDC_IDLERANGE,%.2f", unit->PID_cool->idleRange);
+		}
 		srv_send((char *)"FAN_ADDRESS,%s", unit->fan_address);
 		srv_send((char *)"FAN_STATE,%d", unit->fan_state);
 		srv_send((char *)"FAN_DELAY,%d", unit->fan_delay);
@@ -2043,11 +2009,6 @@
 		srv_send((char *)"PROF_PRIMARY_DONE,%d", (int)unit->prof_primary_done);
 		srv_send((char *)"TEMP_SET_MIN,%.1f", unit->temp_set_min);
 		srv_send((char *)"TEMP_SET_MAX,%.1f", unit->temp_set_max);
-		srv_send((char *)"IDLE_RANGE_L,%.2f", unit->idle_rangeL);
-		srv_send((char *)"IDLE_RANGE_H,%.2f", unit->idle_rangeH);
-		srv_send((char *)"PID_KP,%.2f", unit->PID_Kp);
-		srv_send((char *)"PID_KI,%.2f", unit->PID_Ki);
-		srv_send((char *)"PID_KD,%.2f", unit->PID_Kd);
 		srv_send((char *)".");
 		return 1;
 	    }
@@ -2057,36 +2018,28 @@
     }
 
     if (strcmp(opt, (char *)"PUT") == 0) {
+	/*
+	 * Block main process
+	 */
+	run_pause = TRUE;
+	for (;;) {
+	    usleep(100000);
+	    if (run_hold)
+		break;
+	}
+
 	for (unit = Config.units ; unit; unit = unit->next) {
 	     if (strcmp(unit->uuid, param) == 0) {
 		while (1) {
-		    memset((char *)&ibuf, 0, SS_BUFSIZE);
-		    fromlen = sizeof(peeraddr_in);
-		    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+		    rlen = srv_recv(ibuf);
 		    if (rlen == -1) {
-			syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-			srv_send((char *)"518 recfrom(): %s", strerror(errno));
+			run_pause = FALSE;
 			return 1;
 		    }
-		    for (i = 0; i < strlen(ibuf); i++) {
-			if (ibuf[i] == '\n')
-			    ibuf[i] = '\0';
-			if (ibuf[i] == '\r')
-			    ibuf[i] = '\0';
-		    }
-		    for (i = strlen(ibuf) -1; i > 0; i--) {
-			if (ibuf[i] == ' ')
-			    ibuf[i] = '\0';
-			else
-			    break;
-		    }
 		    if (strlen(ibuf)) {
-			if (debug) {
-			    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
-			    fprintf(stdout, "recv: \"%s\"\n", ibuf);
-			}
 			if (strcmp(ibuf, (char *)".") == 0) {
 			    srv_send((char *)"219 Accepted Unit record");
+			    run_pause = FALSE;
 			    return 0;
 			}
 			kwd = strtok(ibuf, ",\0");
@@ -2281,7 +2234,8 @@
 					syslog(LOG_NOTICE, "Fermenter unit %s mode %s to %s", unit->uuid, UNITMODE[unit->mode], UNITMODE[i]);
 					unit->mode = i;
 					/* Allways turn everything off after a mode change */
-					unit->PID_iState = unit->PID_dState = 0.0;
+					unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
+					unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
 					unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
 					unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
 					device_out(unit->heater_address, unit->heater_state);
@@ -2313,25 +2267,74 @@
 				    unit->beer_set = fval;
 				}
 
-			    } else if (val && (strcmp(kwd, (char *)"PID_KP") == 0)) {
+			    } else if (val && (strcmp(kwd, (char *)"PIDC_IMAX") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_cool->iMax != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.1f to %.1f", unit->uuid, unit->PID_cool->iMax, fval);
+				    unit->PID_cool->iMax = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDC_PGAIN") == 0)) {
 				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
-				    if (unit->PID_Kp != fval)
-					syslog(LOG_NOTICE, "Fermenter unit %s PID Kp %.2f to %.2f", unit->uuid, unit->PID_Kp, fval);
-				    unit->PID_Kp = fval;
+				    if (unit->PID_cool->pGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool pGain %.2f to %.2f", unit->uuid, unit->PID_cool->pGain, fval);
+				    unit->PID_cool->pGain = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDC_DGAIN") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_cool->dGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool dGain %.2f to %.2f", unit->uuid, unit->PID_cool->dGain, fval);
+				    unit->PID_cool->dGain = fval;
+				}
+	
+			    } else if (val && (strcmp(kwd, (char *)"PIDC_IGAIN") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_cool->iGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.2f to %.2f", unit->uuid, unit->PID_cool->iGain, fval);
+				    unit->PID_cool->iGain = fval;
 				}
 
-			    } else if (val && (strcmp(kwd, (char *)"PID_KD") == 0)) {
+			    } else if (val && (strcmp(kwd, (char *)"PIDC_IDLERANGE") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_cool->idleRange != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool idleRange %.2f to %.2f", unit->uuid, unit->PID_cool->idleRange, fval);
+				    unit->PID_cool->idleRange = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDH_IMAX") == 0)) {
 				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
-				    if (unit->PID_Kd != fval)
-					syslog(LOG_NOTICE, "Fermenter unit %s PID Kd %.2f to %.2f", unit->uuid, unit->PID_Kd, fval);
-				    unit->PID_Kd = fval;
+				    if (unit->PID_heat->iMax != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat iGain %.1f to %.1f", unit->uuid, unit->PID_heat->iMax, fval);
+				    unit->PID_heat->iMax = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDH_PGAIN") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_heat->pGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat pGain %.2f to %.2f", unit->uuid, unit->PID_heat->pGain, fval);
+				    unit->PID_heat->pGain = fval;
 				}
-	
-			    } else if (val && (strcmp(kwd, (char *)"PID_KI") == 0)) {
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDH_DGAIN") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_heat->dGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat dGain %.2f to %.2f", unit->uuid, unit->PID_heat->dGain, fval);
+				    unit->PID_heat->dGain = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDH_IGAIN") == 0)) {
 				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
-				    if (unit->PID_Ki != fval)
-					syslog(LOG_NOTICE, "Fermenter unit %s PID Ki %.2f to %.2f", unit->uuid, unit->PID_Ki, fval);
-				    unit->PID_Ki = fval;
+				    if (unit->PID_heat->iGain != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PIH_heat iGain %.2f to %.2f", unit->uuid, unit->PID_heat->iGain, fval);
+				    unit->PID_heat->iGain = fval;
+				}
+
+			    } else if (val && (strcmp(kwd, (char *)"PIDH_IDLERANGE") == 0)) {
+				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
+				    if (unit->PID_heat->idleRange != fval)
+					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat idleRange %.2f to %.2f", unit->uuid, unit->PID_heat->idleRange, fval);
+				    unit->PID_heat->idleRange = fval;
 				}
 
 			    } else if (strcmp(kwd, (char *)"PROFILE") == 0) {
@@ -2351,7 +2354,8 @@
 				    /*
 				     * Reset all output devices
 				     */
-				    unit->PID_iState = unit->PID_dState = 0.0;
+				    unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
+				    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
 				    unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
 				    unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
 				    device_out(unit->heater_address, unit->heater_state);
@@ -2411,20 +2415,6 @@
 				    unit->temp_set_max = fval;
 				}
 
-			    } else if (val && (strcmp(kwd, (char *)"IDLE_RANGE_L") == 0)) {
-				if (sscanf(val, "%f", &fval) == 1) {
-				    if (unit->idle_rangeL != fval)
-					syslog(LOG_NOTICE, "Fermenter unit %s idle range low %.2f to %.2f", unit->uuid, unit->idle_rangeL, fval);
-				    unit->idle_rangeL = fval;
-				}
-
-			    } else if (val && (strcmp(kwd, (char *)"IDLE_RANGE_H") == 0)) {
-				if (sscanf(val, "%f", &fval) == 1) {
-				    if (unit->idle_rangeH != fval)
-					syslog(LOG_NOTICE, "Fermenter unit %s idle range high %.2f to %.2f", unit->uuid, unit->idle_rangeH, fval);
-				    unit->idle_rangeH = fval;
-				}
-
 			    }
 			}
 		    }
@@ -2432,6 +2422,7 @@
 	    }
 	}
 	srv_send((char *)"440 No such unit");
+	run_pause = FALSE;
 	return 1;
     }
 
@@ -2444,33 +2435,11 @@
 void cmd_server(void)
 {
     char                buf[SS_BUFSIZE];
-    int                 i, rlen;
-    socklen_t           fromlen;
+    int                 rlen;
 
-    memset((char *)&buf, 0, SS_BUFSIZE);
-    fromlen = sizeof(peeraddr_in);
-    rlen = recvfrom(s, buf, sizeof(buf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
-    if (rlen == -1) {
-	syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
-    } else {		    
-	for (i = 0; i < strlen(buf); i++) {
-	    if (buf[i] == '\n')
-		buf[i] = '\0';
-	    if (buf[i] == '\r')
-	    	buf[i] = '\0';
-	}
-	for (i = strlen(buf) -1; i > 0; i--) {
-	    if (buf[i] == ' ')
-		buf[i] = '\0';
-	    else
-		break;
-	}
+    rlen = srv_recv(buf);
+    if (rlen != -1) {
 	if (strlen(buf)) {
-	    if (debug) {
-		syslog(LOG_NOTICE, "recv: \"%s\"", buf);
-		fprintf(stdout, "recv: \"%s\"\n", buf);
-	    }
-
 	    /*
 	     * Process commands from the client
 	     */
--- a/thermferm/simulator.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/simulator.c	Sat May 16 17:39:30 2015 +0200
@@ -29,6 +29,8 @@
 extern int		debug;
 extern sys_config	Config;
 
+int SIMcooling = 0;
+int SIMheating = 0;
 
 
 #ifdef HAVE_WIRINGPI_H
@@ -78,12 +80,18 @@
 	    	 * the plate is warmer then the air, calculate the cooling down temperature.
 	    	 * Finally, calculate the new air and plate temperature.
 	    	 */
+		if (SIMheating) {
+		    simulator->air_temperature += 0.01;
+		}
 
 	    	/* 
 	    	 * If cooling, calculate temperature of the cooling plate. If cooling is off but
 	    	 * the plate is colder then the air, calculate the warming up temperature.
 	    	 * Finsally, calculate the new air and plate temperature.
 	    	 */
+		if (SIMcooling) {
+		    simulator->air_temperature -= 0.01;
+		}
 
 	    	/*
 	    	 * Calculate the extra beer temperatur rise to simulate the heat produced by the
--- a/thermferm/thermferm.c	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/thermferm.c	Sat May 16 17:39:30 2015 +0200
@@ -841,7 +841,6 @@
     prof_step		*step;
     int			rc, run = 1, seconds = 0, minutes = 0, temp, deviation;
     int			run_seconds, run_minutes, run_hours, tot_minutes;
-    float		sp, pv, P_err = 0.0, Out;
 #ifdef HAVE_WIRINGPI_H
     struct tm		*tm;
     int			row, key;
@@ -850,7 +849,6 @@
 #endif
     int			current_step, valid_step, time_until_now;
     float		previous_target;
-    pid_var		*pid;
 
 
     if (lockprog((char *)"thermferm")) {
@@ -926,17 +924,17 @@
 	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
 	if (unit->mode == UNITMODE_PROFILE) {
 	    if (!unit->profile)
-		syslog(LOG_NOTICE, "Starting unit %s in profile mode, no profile defined.", unit->name);
+		syslog(LOG_NOTICE, "Starting unit `%s' in profile mode, no profile defined.", unit->name);
 	    else
-	    	syslog(LOG_NOTICE, "Starting unit %s in profile state %s.", unit->name, PROFSTATE[unit->prof_state]);
+	    	syslog(LOG_NOTICE, "Starting unit `%s' in profile state %s.", unit->name, PROFSTATE[unit->prof_state]);
 	} else if (unit->mode == UNITMODE_BEER) {
-	    syslog(LOG_NOTICE, "Starting unit %s beer cooler at %.1f degrees", unit->name, unit->beer_set);
+	    syslog(LOG_NOTICE, "Starting unit `%s' beer cooler at %.1f degrees", unit->name, unit->beer_set);
 	} else if (unit->mode == UNITMODE_FRIDGE) {
-	    syslog(LOG_NOTICE, "Starting unit %s as refridgerator at %.1f degrees", unit->name, unit->fridge_set);
+	    syslog(LOG_NOTICE, "Starting unit `%s' as refridgerator at %.1f degrees", unit->name, unit->fridge_set);
 	} else if (unit->mode == UNITMODE_NONE) {
-	    syslog(LOG_NOTICE, "Starting unit %s in inactive state", unit->name);
+	    syslog(LOG_NOTICE, "Starting unit `%s' in inactive state", unit->name);
 	} else {
-	    syslog(LOG_NOTICE, "Starting unit %s in off state", unit->name);
+	    syslog(LOG_NOTICE, "Starting unit `%s' in off state", unit->name);
 	}
     }
 
@@ -1379,80 +1377,63 @@
 	    	 * Temperature control in this unit
 	    	 */
 		if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
-		    int	usePid = TRUE;
 
-		    sp = unit->beer_set;
-		    pv = unit->beer_temperature / 1000.0;
+		    /*
+		     * Set both PID's to their input values.
+		     */
+		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
 		    if (unit->mode == UNITMODE_FRIDGE) {
-			sp = unit->fridge_set;
-			pv = unit->air_temperature / 1000.0;
-			usePid = FALSE;
+			unit->PID_cool->SetP = unit->PID_heat->SetP = unit->fridge_set;
+			unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
+			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
+		    } else if (unit->mode == UNITMODE_BEER) {
+			unit->PID_cool->SetP = unit->PID_heat->SetP = unit->beer_set;
+			unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
+			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
 		    } else if (unit->mode == UNITMODE_PROFILE) {
-			sp = unit->prof_target;
-		    }
-
-		    P_err = sp - pv;
-		    if (P_err < unit->idle_rangeH && P_err > unit->idle_rangeL && usePid == FALSE) {
-			P_err = 0.0;
+			unit->PID_cool->SetP = unit->PID_heat->SetP = unit->prof_target;
+			unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
+			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
 		    }
 
-		    if (usePid) {
-			/*
-			 * PID controller compute
-			 */
-		    	pid = (pid_var *)malloc(sizeof(pid_var));
-		    	pid->dState = unit->PID_dState;
-		    	pid->iState = unit->PID_iState;
-		    	pid->iMax = 100.0;
-		    	pid->iMin = -100.0;
-		    	pid->pGain = unit->PID_Kp;
-		    	pid->iGain = unit->PID_Ki;
-		    	pid->dGain = unit->PID_Kd;
+		    /*
+		     * PID controller compute
+		     */
+		    if (unit->heater_address) {
+			UpdatePID(unit->PID_heat);
 
-		    	Out = UpdatePID(pid, P_err, pv);
-
-		    	if (Out > 100.0)
-			    Out = 100.0;
-		    	if (Out < -100.0)
-			    Out = -100.0;
+			if (debug)
+			    fprintf(stdout, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n",
+				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP);
+			if (((unit->PID_heat->OutP >= 1) && unit->heater_address) || (seconds == 60) || unit->heater_state) {
+			    syslog(LOG_NOTICE, "Heat: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f",
+				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->InputD, unit->PID_heat->Err, unit->PID_heat->OutP);
+			}
+		    }
+		    if (unit->cooler_address) {
+		    	UpdatePID(unit->PID_cool);
 
 		    	if (debug)
-			    fprintf(stdout, "sp=%.2f pv=%.2f dState=%.2f P_err=%.2f iState=%.2f Out=%.2f\n",
-				sp, pv, unit->PID_dState, P_err, unit->PID_iState, Out);
-		    	if (((Out >= 1) && unit->heater_address) || ((Out <= -1) && unit->cooler_address) || 
-			    (seconds == 60) || unit->heater_state || unit->cooler_state) {
-			    syslog(LOG_NOTICE, "sp=%.2f pv=%.2f P_err=%.2f dState=%.2f iState=%.2f Out=%.2f",
-				sp, pv, P_err, unit->PID_dState, unit->PID_iState, Out);
+			    fprintf(stdout, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f\n",
+				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP);
+		    	if (((unit->PID_cool->OutP >= 1) && unit->cooler_address) || (seconds == 60) || unit->cooler_state) {
+			    syslog(LOG_NOTICE, "Cool: sp=%.2f Input=%.2f InputD=%.2f Err=%.2f Out=%.2f",
+				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->InputD, unit->PID_cool->Err, unit->PID_cool->OutP);
 		    	}
+		    }
 
-		    	unit->PID_iState = pid->iState;
-		    	unit->PID_dState = pid->dState;
-		    	free(pid);
-		    	pid = NULL;
-		    } else {
-			/*
-			 * Simple temperature control
-			 */
-			if (P_err > 0) {
-			    Out = 100.0;
-			} else if (P_err < 0) {
-			    Out = -100.0;
-			} else {
-			    Out = 0.0;
-			}
-//			if (((Out >= 1) && unit->heater_address) || ((Out <= -1) && unit->cooler_address) ||
-//			    (seconds == 60) || unit->heater_state || unit->cooler_state) {
-//			    syslog(LOG_NOTICE, "sp=%.2f pv=%.2f P_err=%.2f Out=%.2f", sp, pv, P_err, Out);
-//			}
+		    if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
+			syslog(LOG_NOTICE, "Heat and Cool lockdown");
+			unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
 		    }
 
 		    if (unit->heater_address && ! unit->cooler_state) {
-			if (Out >= 1) {
+			if (unit->PID_heat->OutP >= 1) {
 			    if (unit->heater_wait < unit->heater_delay) {
 				unit->heater_wait++;
 //				syslog(LOG_NOTICE, "heater_wait + %d/%d", unit->heater_wait, unit->heater_delay);
 			    } else {
-				int	power = round(Out);
+				int	power = round(unit->PID_heat->OutP);
 				if (unit->heater_state != power) {
 				    syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->name, unit->heater_state, power);
 				    unit->heater_state = power;
@@ -1476,12 +1457,12 @@
 		    }
 
 		    if (unit->cooler_address && ! unit->heater_state) {
-			if (Out <= -1) {
+			if (unit->PID_cool->OutP >= 1) {
 			    if (unit->cooler_wait < unit->cooler_delay) {
 				unit->cooler_wait++;
 //				syslog(LOG_NOTICE, "cooler_wait + %d/%d", unit->cooler_wait, unit->cooler_delay);
 			    } else {
-				int	power = round(0 - Out); 
+				int	power = round(unit->PID_cool->OutP); 
 				if (unit->cooler_state != power) {
 				    syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->name, unit->cooler_state, power);
 				    unit->cooler_state = power;
@@ -1533,11 +1514,8 @@
 			else
 			    device_out(unit->fan_address, 0);
 		    }
-
 		} else {
-		    P_err = 0.0;
-		    unit->PID_iState = 0.0;
-		    unit->PID_dState = 0.0;
+		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
 		} /* fridge beer or profile mode */
 	    } /* for units */
 
--- a/thermferm/thermferm.h	Thu May 14 22:03:35 2015 +0200
+++ b/thermferm/thermferm.h	Sat May 16 17:39:30 2015 +0200
@@ -6,6 +6,7 @@
 #define FALSE 0
 
 #include "../config.h"
+#include "pid.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -160,8 +161,6 @@
     float		fridge_set;		/* Fridge temperature setting	*/
     float		temp_set_min;		/* Minimum temperature		*/
     float		temp_set_max;		/* Maximum temperature		*/
-    float		idle_rangeL;		/* Idle temperature low		*/
-    float		idle_rangeH;		/* Idle temperature high	*/
     char		*profile;		/* Active profile uuid		*/
     time_t		prof_started;		/* Profile start time		*/
     int			prof_state;		/* Profile OFF|PAUSE|RUN|DONE	*/
@@ -171,11 +170,8 @@
     float		prof_peak_abs;		/* Profile absolute peak temp	*/
     float		prof_peak_rel;		/* Profile relative peak temp	*/
     time_t		prof_primary_done;	/* Profile primary is done	*/
-    double		PID_iState;		/* PID Integral state		*/
-    double		PID_dState;		/* PID last measured value	*/
-    float		PID_Kp;			/* PID Kp setting		*/
-    float		PID_Kd;			/* PID Kd setting		*/
-    float		PID_Ki;			/* PID Ki setting		*/
+    pid_var		*PID_cool;		/* PID cooler			*/
+    pid_var		*PID_heat;		/* PID heater			*/
 } units_list;
 
 #define	UNITMODE_OFF		0		/* Unit turned off		*/
--- a/www-thermferm/devices.php	Thu May 14 22:03:35 2015 +0200
+++ b/www-thermferm/devices.php	Sat May 16 17:39:30 2015 +0200
@@ -345,6 +345,14 @@
 		    if ($type == "SIM") {
 			$se = ($f[1] == "UNDEF")?" selected":"";
 			$outstr .= '         <option value="UNDEF"'.$se.'>Undefined</option>'.PHP_EOL;
+			$se = ($f[1] == "IN_BIN")?" selected":"";
+			$outstr .= '         <option value="IN_BIN"'.$se.'>Binary input</option>'.PHP_EOL;
+			$se = ($f[1] == "IN_ANALOG")?" selected":"";
+			$outstr .= '         <option value="IN_ANALOG"'.$se.'>Analog input</option>'.PHP_EOL;
+			$se = ($f[1] == "OUT_BIN")?" selected":"";
+			$outstr .= '         <option value="OUT_BIN"'.$se.'>Binary output</option>'.PHP_EOL;
+			$se = ($f[1] == "OUT_ANALOG")?" selected":"";
+			$outstr .= '         <option value="OUT_ANALOG"'.$se.'>Analog output</option>'.PHP_EOL;
 		    }
 		    $outstr .= '        </select></td>'.PHP_EOL;
 		}
--- a/www-thermferm/units.php	Thu May 14 22:03:35 2015 +0200
+++ b/www-thermferm/units.php	Sat May 16 17:39:30 2015 +0200
@@ -106,11 +106,16 @@
 	$cmd[] = "PSU_ADDRESS,".$_POST['PSUAddress'];
 	$cmd[] = "TEMP_SET_MIN,".$_POST['TempSetMin'];
 	$cmd[] = "TEMP_SET_MAX,".$_POST['TempSetMax'];
-	$cmd[] = "IDLE_RANGE_L,".$_POST['IdleRangeL'];
-	$cmd[] = "IDLE_RANGE_H,".$_POST['IdleRangeH'];
-	$cmd[] = "PID_KP,".$_POST['PID_Kp'];
-	$cmd[] = "PID_KD,".$_POST['PID_Kd'];
-	$cmd[] = "PID_KI,".$_POST['PID_Ki'];
+	$cmd[] = "PIDC_IMAX,".$_POST['PIDC_iMax'];
+	$cmd[] = "PIDC_PGAIN,".$_POST['PIDC_pGain'];
+	$cmd[] = "PIDC_DGAIN,".$_POST['PIDC_dGain'];
+	$cmd[] = "PIDC_IGAIN,".$_POST['PIDC_iGain'];
+	$cmd[] = "PIDC_IDLERANGE,".$_POST['PIDC_idleRange'];
+	$cmd[] = "PIDH_IMAX,".$_POST['PIDC_iMax'];
+	$cmd[] = "PIDH_PGAIN,".$_POST['PIDH_pGain'];
+	$cmd[] = "PIDH_DGAIN,".$_POST['PIDH_dGain'];
+	$cmd[] = "PIDH_IGAIN,".$_POST['PIDH_iGain'];
+	$cmd[] = "PIDH_IDLERANGE,".$_POST['PIDH_idleRange'];
 	$cmd[] = ".";
 	send_array($cmd);
     }
@@ -134,11 +139,16 @@
     unset($_POST['PSUAddress']);
     unset($_POST['TempSetMin']);
     unset($_POST['TempSetMax']);
-    unset($_POST['IdleRangeL']);
-    unset($_POST['IdleRangeH']);
-    unset($_POST['PID_Kp']);
-    unset($_POST['PID_Kd']);
-    unset($_POST['PID_Ki']);
+    unset($_POST['PIDC_iMax']);
+    unset($_POST['PIDC_pGain']);
+    unset($_POST['PIDC_dGain']);
+    unset($_POST['PIDC_iGain']);
+    unset($_POST['PIDC_idleRange']);
+    unset($_POST['PIDH_iMax']);
+    unset($_POST['PIDH_pGain']);
+    unset($_POST['PIDH_dGain']);
+    unset($_POST['PIDH_iGain']);
+    unset($_POST['PIDH_idleRange']);
     load('units.php');
 }
 
@@ -161,8 +171,8 @@
  *         6 = TempSetMin < -5 or > 15
  *         7 = HeaterDelay out of range
  *         8 = CoolerDelay out of range
- *         9 = IdleRangeL out of range
- *        10 = IdleRangeH out of range
+ *         9 = PIDC idleRange out of range
+ *        10 = PIDH idleRange out of range
  *        11 = LightDelay out of range
  *	  12 = FanDelay out of range
  *        99 = Cancel key
@@ -175,8 +185,10 @@
 	isset($_POST['BeerAddress']) && isset($_POST['HeaterAddress']) && isset($_POST['CoolerAddress']) && isset($_POST['LightAddress']) &&
 	isset($_POST['HeaterDelay']) && isset($_POST['CoolerDelay']) && isset($_POST['LightDelay']) && isset($_POST['PSUAddress']) &&
 	isset($_POST['FanAddress']) && isset($_POST['DoorAddress']) && isset($_POST['TempSetMin']) && isset($_POST['TempSetMax']) &&
-	isset($_POST['PID_Kp']) && isset($_POST['PID_Kd']) && isset($_POST['PID_Ki']) && isset($_POST['FanDelay']) &&
-	isset($_POST['IdleRangeL']) && isset($_POST['IdleRangeH']) && isset($_POST['key']) && isset($_POST['command'])) {
+	isset($_POST['PIDC_pGain']) && isset($_POST['PIDC_iGain']) && isset($_POST['PIDC_dGain']) && isset($_POST['PIDC_idleRange']) &&
+	isset($_POST['PIDH_pGain']) && isset($_POST['PIDH_iGain']) && isset($_POST['PIDH_dGain']) && isset($_POST['PIDH_idleRange']) &&
+	isset($_POST['PIDC_iMax']) && isset($_POST['PIDH_iMax']) &&
+	isset($_POST['FanDelay']) && isset($_POST['key']) && isset($_POST['command'])) {
 
 	if ($_POST['key'] == 'Cancel')
 	    return 99;
@@ -212,10 +224,10 @@
 	if (($_POST['CoolerDelay'] < 0) || ($_POST['CoolerDelay'] > 720))
 	    return 8;
 
-	if (($_POST['IdleRangeL'] > 0) || ($_POST['IdleRangeL'] < -5))
+	if (($_POST['PIDC_idleRange'] < 0) || ($_POST['PIDC_idleRange'] > 5))
 	    return 9;
 
-	if (($_POST['IdleRangeH'] < 0) || ($_POST['IdleRangeH'] > 5))
+	if (($_POST['PIDH_idleRange'] < 0) || ($_POST['PIDH_idleRange'] > 5))
 	    return 10;
 
 	if (($_POST['LightDelay'] < 0) || ($_POST['LightDelay'] > 720))
@@ -266,9 +278,9 @@
 		break;
 	case 8:	$error = 'Cooler Delay must be bewteen 0 and 720 seconds';
 		break;
-	case 9: $error = 'Idle Range Low must be between -5 en 0';
+	case 9: $error = 'PID cool Idle Range must be between 0 en 5';
 		break;
-	case 10: $error = 'Idle Range High must be between 0 and 5';
+	case 10: $error = 'PID heat Idle Range must be between 0 and 5';
 		break;
 	case 11: $error = 'Light Delay must be bewteen 0 and 720 seconds';
 		break;
@@ -408,6 +420,36 @@
 		$outstr .= '        <td class="editfield"><input type="text" name="HeaterDelay" size="5" value="'.$f[1].'"> seconds (0..720)</td>'.PHP_EOL;
 		$outstr .= '       </tr>'.PHP_EOL;
 	    }
+	    if ($f[0] == "PIDH_IMAX") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Heat Maximum</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDH_iMax" size="6" value="'.$f[1].'"> % (1..100)</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDH_PGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Heat pGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDH_pGain" size="6" value="'.$f[1].'"> Proportional</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDH_IGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Heat iGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDH_iGain" size="6" value="'.$f[1].'"> Intergral</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDH_DGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Heat dGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDH_dGain" size="6" value="'.$f[1].'"> Derivative</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDH_IDLERANGE") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Heat Idle Range</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDH_idleRange" size="6" value="'.$f[1].'"> &deg;C (Heater margin)</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
 	    if ($f[0] == "COOLER_ADDRESS") {
 		$outstr .= '       <tr class="editor">'.PHP_EOL;
 		$outstr .= '        <td class="editname">Cooler Switch Address</td>'.PHP_EOL;
@@ -435,6 +477,36 @@
 		$outstr .= '        <td class="editfield"><input type="text" name="CoolerDelay" size="5" value="'.$f[1].'"> seconds (0..720)</td>'.PHP_EOL;
 		$outstr .= '       </tr>'.PHP_EOL;
 	    }
+	    if ($f[0] == "PIDC_IMAX") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Cool Maximum</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDC_iMax" size="6" value="'.$f[1].'"> % (1..100)</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDC_PGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Cool pGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDC_pGain" size="6" value="'.$f[1].'"> Proportional</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDC_IGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Cool iGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDC_iGain" size="6" value="'.$f[1].'"> Intergral</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDC_DGAIN") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Cool dGain</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDC_dGain" size="6" value="'.$f[1].'"> Derivative</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
+	    if ($f[0] == "PIDC_IDLERANGE") {
+		$outstr .= '       <tr class="editor">'.PHP_EOL;
+		$outstr .= '        <td class="editname">PID Cool Idle Range</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="PIDC_idleRange" size="6" value="'.$f[1].'"> &deg;C (Cooler margin)</td>'.PHP_EOL;
+		$outstr .= '       </tr>'.PHP_EOL;
+	    }
 	    if ($f[0] == "FAN_ADDRESS") {
 		$outstr .= '       <tr class="editor">'.PHP_EOL;
 		$outstr .= '        <td class="editname">Fan Switch Address</td>'.PHP_EOL;
@@ -543,36 +615,6 @@
 		$outstr .= '        <td class="editfield"><input type="text" name="TempSetMax" size="5" value="'.$f[1].'"> &deg;C</td>'.PHP_EOL;
 		$outstr .= '       </tr>'.PHP_EOL;
 	    }
-	    if ($f[0] == "IDLE_RANGE_L") {
-		$outstr .= '       <tr class="editor">'.PHP_EOL;
-		$outstr .= '        <td class="editname">Idle Range Low</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="IdleRangeL" size="6" value="'.$f[1].'"> &deg;C (Cooler margin)</td>'.PHP_EOL;
-		$outstr .= '       </tr>'.PHP_EOL;
-	    }
-	    if ($f[0] == "IDLE_RANGE_H") {
-		$outstr .= '       <tr class="editor">'.PHP_EOL;
-		$outstr .= '        <td class="editname">Idle Range High</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="IdleRangeH" size="6" value="'.$f[1].'"> &deg;C (Heater margin)</td>'.PHP_EOL;
-		$outstr .= '       </tr>'.PHP_EOL;
-	    }
-	    if ($f[0] == "PID_KP") {
-		$outstr .= '       <tr class="editor">'.PHP_EOL;
-		$outstr .= '        <td class="editname">PID Kp</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="PID_Kp" size="6" value="'.$f[1].'"> Proportional</td>'.PHP_EOL;
-		$outstr .= '       </tr>'.PHP_EOL;
-	    }
-	    if ($f[0] == "PID_KI") {
-		$outstr .= '       <tr class="editor">'.PHP_EOL;
-		$outstr .= '        <td class="editname">PID Ki</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="PID_Ki" size="6" value="'.$f[1].'"> Intergral</td>'.PHP_EOL;
-		$outstr .= '       </tr>'.PHP_EOL;
-	    }
-	    if ($f[0] == "PID_KD") {
-		$outstr .= '       <tr class="editor">'.PHP_EOL;
-		$outstr .= '        <td class="editname">PID Kd</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="PID_Kd" size="6" value="'.$f[1].'"> Derivative</td>'.PHP_EOL;
-		$outstr .= '       </tr>'.PHP_EOL;
-	    }
 	    $i++;
 	}
     }
@@ -675,11 +717,16 @@
     $outstr .= '<input type="hidden" value="" name="PSUAdress">';
     $outstr .= '<input type="hidden" value="1.0" name="TempSetMin">';
     $outstr .= '<input type="hidden" value="30.0" name="TempSetMax">';
-    $outstr .= '<input type="hidden" value="-1.0" name="IdleRangeL">';
-    $outstr .= '<input type="hidden" value="1.0" name="IdleRangeH">';
-    $outstr .= '<input type="hidden" value="4.0" name="PID_Kp">';
-    $outstr .= '<input type="hidden" value="0.2" name="PID_Kd">';
-    $outstr .= '<input type="hidden" value="1.0" name="PID_Ki">';
+    $outstr .= '<input type="hidden" value="100" name="PIDC_iMax">';
+    $outstr .= '<input type="hidden" value="4.0" name="PIDC_pGain">';
+    $outstr .= '<input type="hidden" value="0.2" name="PIDC_dGain">';
+    $outstr .= '<input type="hidden" value="1.0" name="PIDC_iGain">';
+    $outstr .= '<input type="hidden" value="1.0" name="PIDC_idleRange">';
+    $outstr .= '<input type="hidden" value="100" name="PIDH_iMax">';
+    $outstr .= '<input type="hidden" value="4.0" name="PIDH_pGain">';
+    $outstr .= '<input type="hidden" value="0.2" name="PIDH_dGain">';
+    $outstr .= '<input type="hidden" value="1.0" name="PIDH_iGain">';
+    $outstr .= '<input type="hidden" value="1.0" name="PIDH_idleRange">';
     $outstr .= '<input type="hidden" value="testdata" name="action">';
     $outstr .= '<input type="hidden" value="add" name="command">';
     $outstr .= '<input type="hidden" value="00000000-0000-0000-0000-000000000000" name="UUID">';
--- a/www-thermferm/utilities.php	Thu May 14 22:03:35 2015 +0200
+++ b/www-thermferm/utilities.php	Sat May 16 17:39:30 2015 +0200
@@ -1,6 +1,6 @@
 <?php
 /*****************************************************************************
- * Copyright (C) 2014
+ * Copyright (C) 2014-2015
  *   
  * Michiel Broek <mbroek at mbse dot eu>
  *
@@ -49,7 +49,7 @@
     if ($sock == false) {
 	return "";
     }
-    socket_write($sock, $command, 4096);
+    socket_write($sock, $command . "\r\n", 4096);
 
     $answer = "";
     while (1) {
@@ -78,8 +78,8 @@
     }
 
     foreach($command as $cmd) {
-	socket_write($sock, $cmd, 4096);
-	usleep(20000);	/* Give server time to recognize lines */
+	socket_write($sock, $cmd . "\r\n", 4096);
+//	usleep(20000);	/* Give server time to recognize lines */
     }
 
     $answer = "";

mercurial