# HG changeset patch # User Michiel Broek # Date 1449348382 -3600 # Node ID 78e9d5234d1598e95ba246d7c265ffc6de216480 # Parent 3ec477cda5460d55b88c02edbb55924e4b5c3fb3 Switched to PID code from Arduino diff -r 3ec477cda546 -r 78e9d5234d15 brewco/Makefile --- 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 diff -r 3ec477cda546 -r 78e9d5234d15 brewco/brewco.c --- 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" diff -r 3ec477cda546 -r 78e9d5234d15 brewco/futil.c --- 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 - * - * 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 -} - diff -r 3ec477cda546 -r 78e9d5234d15 brewco/futil.h --- 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 diff -r 3ec477cda546 -r 78e9d5234d15 brewco/pid.c --- 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 + * 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; +} + + diff -r 3ec477cda546 -r 78e9d5234d15 brewco/pid.h --- 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 diff -r 3ec477cda546 -r 78e9d5234d15 brewco/rdconfig.c --- 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); } diff -r 3ec477cda546 -r 78e9d5234d15 brewco/rdsession.c --- 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; diff -r 3ec477cda546 -r 78e9d5234d15 brewco/setup.c --- 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); diff -r 3ec477cda546 -r 78e9d5234d15 brewco/slcd.c --- 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" diff -r 3ec477cda546 -r 78e9d5234d15 brewco/util.c --- /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 + * + * 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 +} + diff -r 3ec477cda546 -r 78e9d5234d15 brewco/util.h --- /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