Switched to PID code from Arduino

Sat, 05 Dec 2015 21:46:22 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 05 Dec 2015 21:46:22 +0100
changeset 446
78e9d5234d15
parent 445
3ec477cda546
child 447
b48368855ec4

Switched to PID code from Arduino

brewco/Makefile file | annotate | diff | comparison | revisions
brewco/brewco.c file | annotate | diff | comparison | revisions
brewco/futil.c file | annotate | diff | comparison | revisions
brewco/futil.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/rdsession.c file | annotate | diff | comparison | revisions
brewco/setup.c file | annotate | diff | comparison | revisions
brewco/slcd.c file | annotate | diff | comparison | revisions
brewco/util.c file | annotate | diff | comparison | revisions
brewco/util.h file | annotate | diff | comparison | revisions
--- a/brewco/Makefile	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/Makefile	Sat Dec 05 21:46:22 2015 +0100
@@ -54,18 +54,18 @@
 
 # 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
+setup.o: brewco.h slcd.h setup.h prompt.h xutil.h keyboard.h
 devices.o: brewco.h devices.h xutil.h keyboard.h
-setup.o: brewco.h slcd.h setup.h prompt.h xutil.h keyboard.h
-futil.o: brewco.h futil.h
-brewco.o: brewco.h rdconfig.h rdsession.h futil.h xutil.h lcd-pcf8574.h slcd.h lock.h devices.h keyboard.h simulator.h prompt.h setup.h
+xutil.o: brewco.h xutil.h
+brewco.o: brewco.h rdconfig.h rdsession.h util.h xutil.h lcd-pcf8574.h slcd.h lock.h devices.h keyboard.h simulator.h prompt.h setup.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
+pid.o: brewco.h pid.h util.h
+util.o: brewco.h util.h
 keyboard.o: brewco.h lcd-pcf8574.h slcd.h keyboard.h
 simulator.o: brewco.h simulator.h
-rdconfig.o: rdconfig.h brewco.h futil.h xutil.h
-rdsession.o: brewco.h rdsession.h futil.h xutil.h
+rdconfig.o: rdconfig.h brewco.h util.h xutil.h
+rdsession.o: brewco.h rdsession.h util.h xutil.h
+slcd.o: brewco.h slcd.h util.h xutil.h
 prompt.o: brewco.h slcd.h prompt.h
 # End of generated dependencies
--- a/brewco/brewco.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/brewco.c	Sat Dec 05 21:46:22 2015 +0100
@@ -23,7 +23,7 @@
 #include "brewco.h"
 #include "rdconfig.h"
 #include "rdsession.h"
-#include "futil.h"
+#include "util.h"
 #include "xutil.h"
 #include "lcd-pcf8574.h"
 #include "slcd.h"
--- a/brewco/futil.c	Fri Dec 04 22:57:23 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * 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 "futil.h"
-
-
-/*
- * Make directory tree, the name must end with a /
- */
-int mkdirs(char *name, mode_t mode)
-{
-    char	buf[PATH_MAX], *p, *q;
-    int		last = 0, oldmask;
-
-    memset(&buf, 0, sizeof(buf));
-    strncpy(buf, name, sizeof(buf)-1);
-    buf[sizeof(buf)-1] = '\0';
-
-    p = buf+1;
-
-    oldmask = umask(000);
-    while ((q = strchr(p, '/'))) {
-	*q = '\0';
-	mkdir(buf, mode);
-	last = errno;
-	*q = '/';
-	p = q+1;
-    }
-
-    umask(oldmask);
-											
-    if ((last == 0) || (last == EEXIST)) {
-	return TRUE;
-    } else {
-	syslog(LOG_NOTICE, "mkdirs(%s)", name);
-	return FALSE;
-    }
-}
-
-
-
-/*
- * Test if the given file exists. The second option is:
- * R_OK - test for Read rights 
- * W_OK - test for Write rights
- * X_OK - test for eXecute rights
- * F_OK - test file presence only
- */ 
-int file_exist(char *path, int mode)
-{
-    if (access(path, mode) != 0)
-	return errno;
-
-    return 0;
-}
-
-
-/* From ArdBir */
-float Arrotonda025(float Num){
-    // Appoggio la parte intera
-    int Appoggio= (int)Num;
-
-    // Arrotondo il valore con peso 0.25
-    return Appoggio+(int)((Num-Appoggio)*1000/225)*0.25;
-}
-
-float ConvertiCtoF(float Num){        
-    Num = Num/16;              // Recupero il valore
-    Num = (Num*1.8)+32;          // Converto in °F
-    Num = Arrotonda025(Num);
-    return Num*16;              // Preparo il valore per la registrazione
-}
-float ConvertiFtoC(float Num){
-    Num = Num/16;              // Recupero il valore
-    Num = (Num-32)/1.8;            // Converto in °C
-    Num = Arrotonda025(Num);
-    return Num*16;              // Preparo il valore per la registrazione
-}
-
--- a/brewco/futil.h	Fri Dec 04 22:57:23 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef	FUTIL_H
-#define	FUTIL_H
-
-
-int  mkdirs(char *, mode_t);
-int  file_exist(char *, int);
-
-#endif
--- a/brewco/pid.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/pid.c	Sat Dec 05 21:46:22 2015 +0100
@@ -18,77 +18,237 @@
  * 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.
+ *
+ * Based on the Arduino PID Library 1.1.1 by Brett Beauregard <br3ttb@gmail.com>
+ * This Library is licensed under a GPLv3 License
  *****************************************************************************/
 
 #include "brewco.h"
 #include "pid.h"
+#include "util.h"
+
+
+/*
+ * The parameters specified here are those for for which we can't set up
+ * reliable defaults, so we need to have the user set them.
+ */
+void PID_init(pid_var *pid, double *Input, double *Output, double *Setpoint, double Kp, double Ki, double Kd, int ControllerDirection)
+{
+    pid->myOutput = Output;
+    pid->myInput = Input;
+    pid->mySetpoint = Setpoint;
+    pid->inAuto = FALSE;
+    PID_setOutputLimits(pid, 0, 255);
+    pid->SampleTime = 100;
+    PID_setControllerDirection(pid, ControllerDirection);
+    PID_setTunings(pid, Kp, Ki, Kd);
+    pid->lastTime = millis() - pid->SampleTime;
+}
+
+
+
+/*
+ * Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
+ * when the transition from manual to auto occurs, the controller is
+ * automatically initialized
+ */
+void PID_setMode(pid_var *pid, int Mode)
+{
+    int		newAuto = (Mode == P_AUTOMATIC);
+
+    if (newAuto != pid->inAuto) {
+	/*
+	 * we just went from manual to auto
+	 */
+	pid->ITerm = *pid->myOutput;
+	pid->lastInput = *pid->myInput;
+	if (pid->ITerm > pid->outMax)
+	    pid->ITerm = pid->outMax;
+	else if (pid->ITerm < pid->outMin)
+	    pid->ITerm = pid->outMin;
+    }
+    pid->inAuto = newAuto;
+}
+
 
 
-void InitPID(pid_var *pid)
+/*
+ * This function will be used far more often than SetInputLimits.  while
+ * the input to the controller will generally be in the 0-1023 range (which is
+ * the default already,)  the output will be a little different.  maybe they'll
+ * be doing a time window and will need 0-8000 or something.  or maybe they'll
+ * want to clamp it from 0-125.  who knows.  at any rate, that can all be done
+ * here.
+ */
+void PID_setOutputLimits(pid_var *pid, double Min, double Max)
 {
-    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;
-    pid->Times = PID_TIMES;
+    if (Min >= Max)
+	return;
+
+    pid->outMin = Min;
+    pid->outMax = Max;
+
+    if (pid->inAuto == P_AUTOMATIC) {
+	if (*pid->myOutput > pid->outMax)
+	    *pid->myOutput = pid->outMax;
+	else if (*pid->myOutput < pid->outMin)
+	    *pid->myOutput = pid->outMin;
+
+	if (pid->ITerm > pid->outMax)
+	    pid->ITerm = pid->outMax;
+	else if (pid->ITerm < pid->outMin)
+	    pid->ITerm = pid->outMin;
+    }
+}
+
+
+
+/*
+ * This function allows the controller's dynamic performance to be adjusted.
+ * it's called automatically from the constructor, but tunings can also
+ * be adjusted on the fly during normal operation.
+ */
+void PID_setTunings(pid_var *pid, double Kp, double Ki, double Kd)
+{
+    if (Kp < 0 || Ki < 0 || Kd < 0)
+	return;
+    pid->dispKp = Kp;
+    pid->dispKi = Ki;
+    pid->dispKd = Kd;
+
+    double SampleTimeInSec = ((double)pid->SampleTime) / 1000;
+    pid->Kp = Kp;
+    pid->Ki = Ki * SampleTimeInSec;
+    pid->Kd = Kd / SampleTimeInSec;
+
+    if (pid->Direction == P_REVERSE) {
+	pid->Kp = (0 - pid->Kp);
+	pid->Ki = (0 - pid->Ki);
+	pid->Kd = (0 - pid->Kd);
+    }
 }
 
 
 
-void UpdatePID(pid_var *pid)
+/*
+ * The PID will either be connected to a DIRECT acting process (+Output leads
+ * to +Input) or a REVERSE acting process(+Output leads to -Input.)  we need to
+ * know which one, because otherwise we may increase the output when we should
+ * be decreasing.  This is called from PID_init().
+ */
+void PID_setControllerDirection(pid_var *pid, int Direction)
 {
-    if (pid->Mode == PID_MODE_AUTO) {
-
-	double	pTerm, dTerm, iTerm;
-
-	pid->Err = pid->SetP - pid->Input;
+    if (pid->inAuto && Direction != pid->Direction) {
+	pid->Kp = (0 - pid->Kp);
+	pid->Ki = (0 - pid->Ki);
+	pid->Kd = (0 - pid->Kd);
+    }
+    pid->Direction = Direction;
+}
 
-	/*
-	 * 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;
+/*
+ * sets the period, in Milliseconds, at which the calculation is performed.
+ */
+void PID_setSampleTime(pid_var *pid, int NewSampleTime)
+{
+    if (NewSampleTime > 0) {
+	double	ratio = (double)NewSampleTime / (double)pid->SampleTime;
 
-    } else if (pid->Mode == PID_MODE_BOO) {
-	/*
-	 * Mode Bang On Off
-	 */
-	pid->ErrLast = pid->Err;
-	pid->Err = pid->SetP - pid->Input;
+	pid->Ki *= ratio;
+	pid->Kd /= ratio;
+	pid->SampleTime = NewSampleTime;
+    }
+}
 
-	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;
-    }
+/*
+ * Just because you set the Kp=-1 doesn't mean it actually happened.  these
+ * functions query the internal state of the PID.  they're here for display
+ * purposes.  this are the functions the PID Front-end uses for example
+ */
+double PID_getKp(pid_var *pid)
+{
+    return pid->dispKp;
+}
+
 
-    if (pid->OutP > pid->iMax)
-	pid->OutP = pid->iMax;
-    if (pid->OutP < 0.0)
-	pid->OutP = 0.0;
+
+double PID_getKi(pid_var *pid)
+{
+    return pid->dispKi;
+}
 
+
+
+double PID_getKd(pid_var *pid)
+{
+    return pid->dispKd;
 }
 
 
+
+int PID_getMode(pid_var *pid)
+{
+    return pid->inAuto ? P_AUTOMATIC : P_MANUAL;
+}
+
+
+
+int PID_getDirection(pid_var *pid)
+{
+    return pid->Direction;
+}
+
+
+
+/*
+ * This, as they say, is where the magic happens.  this function should be called
+ * every time "void loop()" executes.  the function will decide for itself whether a new
+ * pid Output needs to be computed.  returns true when the output is computed,
+ * false when nothing has been done.
+ */
+int PID_compute(pid_var *pid)
+{
+    if (! pid->inAuto)
+	return FALSE;
+
+    long now = millis();
+    long timeChange = (now - pid->lastTime);
+
+    if (timeChange >= pid->SampleTime) {
+	/*
+	 * Compute all the working error variables
+	 */
+	double input = *pid->myInput;
+	double error = *pid->mySetpoint - input;
+	pid->ITerm += (pid->Ki * error);
+	if (pid->ITerm > pid->outMax)
+	    pid->ITerm = pid->outMax;
+	else if (pid->ITerm < pid->outMin)
+	    pid->ITerm = pid->outMin;
+	double dInput = input - pid->lastInput;
+
+	/*
+	 * Compute PID output
+	 */
+	double output = pid->Kp * error + pid->ITerm - pid->Kd * dInput;
+	if (output > pid->outMax)
+	    output = pid->outMax;
+	else if (output < pid->outMin)
+	    output = pid->outMin;
+
+	/*
+	 * Remember some variables for the next time
+	 */
+	pid->lastInput = input;
+	pid->lastTime = now;
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+
--- a/brewco/pid.h	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/pid.h	Sat Dec 05 21:46:22 2015 +0100
@@ -1,32 +1,45 @@
 #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				*/
-
+#define	P_MANUAL	0
+#define	P_AUTOMATIC	1
+#define	P_DIRECT	0
+#define	P_REVERSE	1
 
 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	*/
-	int		Times;		/* Calculations per minute			*/
+	double		Kp;		/* (P)roportional Tuning Parameter		*/
+	double		Ki;		/* (I)ntegral Tuning Parameter			*/
+	double		Kd;		/* (D)erivative Tuning Parameter		*/
+	double		dispKp;
+	double		dispKi;
+	double		dispKd;
+	int		Direction;	/* Controller direction				*/
+	double		*myInput;	/* Input parameter				*/
+	double		*myOutput;	/* Output parameter				*/
+	double		*mySetpoint;	/* Setpoint parameter				*/
+	long		lastTime;	/* Last compute time				*/
+	double		ITerm;
+	double		lastInput;
+	long		SampleTime;
+	double		outMin;
+	double		outMax;
+	int		inAuto;
 } pid_var;
 
 
-void InitPID( pid_var *);
-void UpdatePID( pid_var *);
+void	PID_init(pid_var *, double *, double *, double *, double, double, double, int);
+void	PID_setMode(pid_var *, int);
+void	PID_setOutputLimits(pid_var *, double, double);
+void	PID_setTunings(pid_var *, double, double, double);
+void	PID_setControllerDirection(pid_var *, int);
+void	PID_setSampleTime(pid_var *, int);
+double	PID_getKp(pid_var *);
+double	PID_getKi(pid_var *);
+double	PID_getKd(pid_var *);
+int	PID_getMode(pid_var *);
+int	PID_getDirection(pid_var *);
+int	PID_compute(pid_var *);
+
 
 #endif
--- a/brewco/rdconfig.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/rdconfig.c	Sat Dec 05 21:46:22 2015 +0100
@@ -22,7 +22,7 @@
 
 #include "rdconfig.h"
 #include "brewco.h"
-#include "futil.h"
+#include "util.h"
 #include "xutil.h"
 
 int		debug = TRUE;
@@ -34,8 +34,9 @@
 const char	DEVTYPE[6][5] = { "NA", "W1", "GPIO", "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	WHIRLPOOL_TYPE[3][5] = { "OFF", "COLD", "HOT" };
+const char	PIDDIRECTION[2][8] = { "DIRECT", "REVERSE" };
+const char	PIDMODE[2][10] = { "MANUAL", "AUTOMATIC" };
 
 
 void killconfig(void)
@@ -402,105 +403,41 @@
 	    }
 
             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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_KP", "%.2lf", unit->PID_hlt->Kp)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_KI", "%.2lf", unit->PID_hlt->Ki)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_KD", "%.2lf", unit->PID_hlt->Kd)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_DIRECTION", "%s", PIDDIRECTION[unit->PID_hlt->Direction])) < 0) {
                     syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
                     return 1;
                 }
-		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_HLT_TIMES", "%d", unit->PID_hlt->Times)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_KP", "%.2lf", unit->PID_mlt->Kp)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_KI", "%.2lf", unit->PID_mlt->Ki)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_KD", "%.2lf", unit->PID_mlt->Kd)) < 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) {
+                if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_DIRECTION", "%s", PIDDIRECTION[unit->PID_mlt->Direction])) < 0) {
                     syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
                     return 1;
                 }
-		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PID_MLT_TIMES", "%d", unit->PID_mlt->Times)) < 0)) {
-		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
-		    return 1;
-		}
             }
 
             if ((rc = xmlTextWriterEndElement(writer)) < 0) {
@@ -775,6 +712,7 @@
     xmlChar     *key;
     int         i, ival;
     float       fval;
+    double	dval;
     units_list  *unit, *tmp;
 
     unit = (units_list *)malloc(sizeof(units_list));
@@ -800,8 +738,6 @@
     unit->whirlpool = 1;
     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) {
@@ -1027,145 +963,41 @@
 	    xmlFree(key);
 	}
 
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_IMAX"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->iMax = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_IGAIN"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->iGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_PGAIN"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_KP"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->pGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_DGAIN"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->dGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_INPUT"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->Input = fval;
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_hlt->Kp = dval;
             xmlFree(key);
         }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ERR"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->Err = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ERRLAST"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_KI"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->ErrLast = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_ISTATE"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->iState = fval;
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_hlt->Ki = dval;
             xmlFree(key);
         }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_SETP"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->SetP = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_OUTP"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_HLT_KD"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_hlt->OutP = fval;
-            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;
-                }
-            }
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_hlt->Kd = dval;
             xmlFree(key);
         }
 
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_IMAX"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->iMax = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_IGAIN"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->iGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_PGAIN"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_KP"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->pGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_DGAIN"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->dGain = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_INPUT"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->Input = fval;
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_mlt->Kp = dval;
             xmlFree(key);
         }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ERR"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->Err = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ERRLAST"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_KI"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->ErrLast = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_ISTATE"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->iState = fval;
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_mlt->Ki = dval;
             xmlFree(key);
         }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_SETP"))) {
-            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->SetP = fval;
-            xmlFree(key);
-        }
-        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_OUTP"))) {
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_MLT_KD"))) {
             key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-            if (sscanf((const char *)key, "%f", &fval) == 1)
-                unit->PID_mlt->OutP = fval;
-            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;
-                }
-            }
+            if (sscanf((const char *)key, "%lf", &dval) == 1)
+                unit->PID_mlt->Kd = dval;
             xmlFree(key);
         }
 
--- a/brewco/rdsession.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/rdsession.c	Sat Dec 05 21:46:22 2015 +0100
@@ -22,7 +22,7 @@
 
 #include "brewco.h"
 #include "rdsession.h"
-#include "futil.h"
+#include "util.h"
 #include "xutil.h"
 
 extern int	debug;
--- a/brewco/setup.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/setup.c	Sat Dec 05 21:46:22 2015 +0100
@@ -151,6 +151,9 @@
 
 
 
+/*
+ * Edit a single unit
+ */
 void editUnit(units_list *unit)
 {
     int		index = 1, key;
@@ -252,7 +255,18 @@
 
 	if (key == KEY_ENTER) {
 	    switch(index) {
-
+		case 1:		// name
+				break;
+		case 2:		// HLT sensor
+				break;
+		case 3:		// HLT heater
+				break;
+		case 4:		// MLT sensor
+				break;
+		case 5:		// MLT heater
+				break;
+		case 6:		// MLT pump
+				break;
 		case 7:		toggleYesNo(&unit->hlt_heater_mltfirst, (char *)"MLT heat b4 HLT");
 				break;
 		case 8:		editInteger(&unit->pump_cycle, 5, 15, (char *)"Pump cycle");
@@ -277,30 +291,12 @@
 				break;
 		case 19:	toggleYesNo(&unit->whirlpool, (char *)"Do a whirlpool");
 				break;
-
+		case 20:	// PID_hlt
+				break;
+		case 21:	// PID_mlt
+				break;
 	    }
 	}
-    // name
-    // hlt_sensor  picklist
-    // hlt_heater  picklist + value range
-    // mlt_sensor  picklist
-    // mlt_heater  picklist + value range
-    // mlt_pump    picklist + value range
-    // hlt_heater_mltfirst  0/1
-    // pump_cycle 5..15
-    // pump_rest  1..5
-    // pump_premash 0/1
-    // pump_onmash  0/1
-    // pump_mashout 0/1
-    // pump_onboil  0/1
-    // pump_stop   80..110
-    // skip_add     0/1
-    // skip_remove  0/1
-    // skip_iodine  0/1
-    // iodine_time  0..90
-    // whirlpool    0/1
-    // PID_hlt
-    // PID_mlt
     }
 
     if (debug)
@@ -314,6 +310,7 @@
     units_list	*tmpu, *unit = (units_list *)malloc(sizeof(units_list));
     char	name[81];
     uuid_t	uu;
+    double	Input, Output, Setpoint;
 
     if (debug)
 	fprintf(stdout, "Adding new brewsystem %d\n", number);
@@ -346,8 +343,10 @@
     unit->whirlpool = 1;
     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);
+    Input = Setpoint = 20.0;
+    Output = 0;
+    PID_init(unit->PID_hlt, &Input, &Output, &Setpoint, 2, 5, 1, P_DIRECT);
+    PID_init(unit->PID_mlt, &Input, &Output, &Setpoint, 2, 5, 1, P_DIRECT);
 
     editUnit(unit);
 
--- a/brewco/slcd.c	Fri Dec 04 22:57:23 2015 +0100
+++ b/brewco/slcd.c	Sat Dec 05 21:46:22 2015 +0100
@@ -22,7 +22,7 @@
 
 #include "brewco.h"
 #include "slcd.h"
-#include "futil.h"
+#include "util.h"
 #include "xutil.h"
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/util.c	Sat Dec 05 21:46:22 2015 +0100
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * 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 "util.h"
+
+
+/*
+ * Make directory tree, the name must end with a /
+ */
+int mkdirs(char *name, mode_t mode)
+{
+    char	buf[PATH_MAX], *p, *q;
+    int		last = 0, oldmask;
+
+    memset(&buf, 0, sizeof(buf));
+    strncpy(buf, name, sizeof(buf)-1);
+    buf[sizeof(buf)-1] = '\0';
+
+    p = buf+1;
+
+    oldmask = umask(000);
+    while ((q = strchr(p, '/'))) {
+	*q = '\0';
+	mkdir(buf, mode);
+	last = errno;
+	*q = '/';
+	p = q+1;
+    }
+
+    umask(oldmask);
+											
+    if ((last == 0) || (last == EEXIST)) {
+	return TRUE;
+    } else {
+	syslog(LOG_NOTICE, "mkdirs(%s)", name);
+	return FALSE;
+    }
+}
+
+
+
+/*
+ * Test if the given file exists. The second option is:
+ * R_OK - test for Read rights 
+ * W_OK - test for Write rights
+ * X_OK - test for eXecute rights
+ * F_OK - test file presence only
+ */ 
+int file_exist(char *path, int mode)
+{
+    if (access(path, mode) != 0)
+	return errno;
+
+    return 0;
+}
+
+
+
+long millis(void)
+{
+    struct timespec	now;
+
+    clock_gettime(CLOCK_REALTIME , &now);
+    return ((now.tv_sec * 1000000000) + now.tv_nsec) / 1000000;
+}
+
+
+
+/* From ArdBir */
+float Arrotonda025(float Num){
+    // Appoggio la parte intera
+    int Appoggio= (int)Num;
+
+    // Arrotondo il valore con peso 0.25
+    return Appoggio+(int)((Num-Appoggio)*1000/225)*0.25;
+}
+
+float ConvertiCtoF(float Num){        
+    Num = Num/16;              // Recupero il valore
+    Num = (Num*1.8)+32;          // Converto in °F
+    Num = Arrotonda025(Num);
+    return Num*16;              // Preparo il valore per la registrazione
+}
+float ConvertiFtoC(float Num){
+    Num = Num/16;              // Recupero il valore
+    Num = (Num-32)/1.8;            // Converto in °C
+    Num = Arrotonda025(Num);
+    return Num*16;              // Preparo il valore per la registrazione
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/brewco/util.h	Sat Dec 05 21:46:22 2015 +0100
@@ -0,0 +1,10 @@
+#ifndef	_UTIL_H
+#define	_UTIL_H
+
+
+int  mkdirs(char *, mode_t);
+int  file_exist(char *, int);
+long millis(void);
+
+
+#endif

mercurial