Added more precision to the unit low and high black window margins. Switched to PID routine from Pid without a PhD. The PID compute routine is now in the one second loop. The switch delay times are now in seconds, 0..720.

Mon, 16 Feb 2015 19:52:42 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 16 Feb 2015 19:52:42 +0100
changeset 312
7b0f819a3805
parent 311
f3b0e9ac9bcb
child 313
8448fcf3d799

Added more precision to the unit low and high black window margins. Switched to PID routine from Pid without a PhD. The PID compute routine is now in the one second loop. The switch delay times are now in seconds, 0..720.

thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
www-thermferm/units.php file | annotate | diff | comparison | revisions
--- a/thermferm/rdconfig.c	Sun Feb 15 23:09:00 2015 +0100
+++ b/thermferm/rdconfig.c	Mon Feb 16 19:52:42 2015 +0100
@@ -461,11 +461,11 @@
 		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
 		return 1;
 	    }
-	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "IDLE_RANGE_L", "%.1f", tmp3->idle_rangeL)) < 0) {
+	    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", "%.1f", tmp3->idle_rangeH)) < 0) {
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "IDLE_RANGE_H", "%.2f", tmp3->idle_rangeH)) < 0) {
 		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
 		return 1;
 	    }
--- a/thermferm/server.c	Sun Feb 15 23:09:00 2015 +0100
+++ b/thermferm/server.c	Mon Feb 16 19:52:42 2015 +0100
@@ -1613,8 +1613,8 @@
 		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,%.1f", unit->idle_rangeL);
-		srv_send((char *)"IDLE_RANGE_H,%.1f", unit->idle_rangeH);
+		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);
@@ -1984,14 +1984,14 @@
 			    } 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 %.1f to %.1f", unit->uuid, 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 %.1f to %.1f", unit->uuid, 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;
 				}
 
--- a/thermferm/thermferm.c	Sun Feb 15 23:09:00 2015 +0100
+++ b/thermferm/thermferm.c	Mon Feb 16 19:52:42 2015 +0100
@@ -834,9 +834,9 @@
     units_list		*unit;
     profiles_list	*profile;
     prof_step		*step;
-    int			rc, run = 1, seconds = 0, minutes = 0, piddelay = 0, temp, deviation;
+    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, D_err, Out;
+    float		sp, pv, P_err = 0.0, Out;
 #ifdef HAVE_WIRINGPI_H
     struct tm		*tm;
     int			row, key;
@@ -1302,62 +1302,65 @@
 #endif
 	    }
 
-	    piddelay++;
-	    if (piddelay == 5) {
-		piddelay = 0;
+	    /*
+	     * PID controller per unit, each second
+	     */
+	    for (unit = Config.units; unit; unit = unit->next) {
+		if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
+		    double	pTerm, dTerm, iTerm;
+
+		    sp = unit->beer_set;
+		    pv = unit->beer_temperature / 1000.0;
+		    if (unit->mode == UNITMODE_FRIDGE) {
+			sp = unit->fridge_set;
+			pv = unit->air_temperature / 1000.0;
+		    } else if (unit->mode == UNITMODE_PROFILE) {
+			sp = unit->prof_target;
+		    }
+
+		    /*
+		     * PID controller compute
+		     */
+		    P_err = sp - pv;
+//		    if (P_err < unit->idle_rangeH && P_err > unit->idle_rangeL) {
+//			P_err = 0.0;
+//		    }
+		    pTerm = unit->PID_Kp * P_err;
 
-		for (unit = Config.units; unit; unit = unit->next) {
-		    if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {
-			/*
-			 * PID controller
-			 */
-			sp = unit->beer_set;
-			pv = unit->beer_temperature / 1000.0;
-			if (unit->mode == UNITMODE_FRIDGE) {
-				sp = unit->fridge_set;
-				pv = unit->air_temperature / 1000.0;
-			} else if (unit->mode == UNITMODE_PROFILE) {
-				sp = unit->prof_target;
-			}
+		    /*
+		     * Calculate the intergral state with appropriate limiting
+		     */
+		    unit->PID_I_err += P_err;
+		    /* Limit integral error */
+		    if (unit->PID_I_err < -100.0)
+			unit->PID_I_err = -100.0;
+		    if (unit->PID_I_err > 100.0)
+			unit->PID_I_err = 100.0;
+		    iTerm = unit->PID_I_err * unit->PID_Ki;
+		    dTerm = unit->PID_err_old * unit->PID_Kd;
 
-			P_err = sp - pv;
-			if (P_err < unit->idle_rangeH && P_err > unit->idle_rangeL) {
-			    P_err = 0.0;
-			}
-			unit->PID_I_err += (unit->PID_Ki * P_err);
-//			    unit->PID_I_err -= unit->PID_err_old;
-//			} else {
-//			    unit->PID_I_err += unit->PID_err_old;
+		    /*
+		     * A postive value means heating, a negative value cooling.
+		     * Start with Kp, Kd and Ki set to 0.
+		     * Increase Kp until small oscillation.
+		     * Increase Kd until a little damping.
+		     * Increase Ki after Kp and Kd are set until longterm convergence.
+		     */
+		    Out = pTerm + dTerm + iTerm;
+		    if (Out > 100.0)
+			Out = 100.0;
+		    if (Out < -100.0)
+			Out = -100.0;
+//			if /* (P_err != 0.0) */ (i == 1) {
+			    if (debug)
+			    	fprintf(stdout, "sp=%.2f pv=%.2f err_old=%.2f P_err=%.2f I_err=%.2f Out=%.2f\n",
+					sp, pv, unit->PID_err_old, P_err, unit->PID_I_err, Out);
+			    syslog(LOG_NOTICE, "sp=%.2f pv=%.2f err_old=%.2f P_err=%.2f I_err=%.2f Out=%.2f pTerm=%.2f iTerm=%.2f dTerm=%.2f, N=%.2f",
+					sp, pv, unit->PID_err_old, P_err, unit->PID_I_err, Out, pTerm, iTerm, dTerm, pTerm + dTerm + iTerm);
 //			}
-			/* Limit integral error */
-			if (unit->PID_I_err < -100.0)
-			    unit->PID_I_err = -100.0;
-			if (unit->PID_I_err > 100.0)
-			    unit->PID_I_err = 100.0;
-			D_err = P_err - unit->PID_err_old;
+		    unit->PID_err_old = P_err;
 
-			/*
-			 * A postive value means heating, a negative value cooling.
-			 * Start with Kp, Kd and Ki set to 0.
-			 * Increase Kp until small oscillation.
-			 * Increase Kd until a little damping.
-			 * Increase Ki after Kp and Kd are set until longterm convergence.
-			 */
-			Out = (unit->PID_Kp * P_err) + unit->PID_I_err - (unit->PID_Kd * D_err);
-			if (Out > 100.0)
-			    Out = 100.0;
-			if (Out < -100.0)
-			    Out = -100.0;
-//			if (P_err != 0.0) {
-			    if (debug)
-			    	fprintf(stdout, "sp=%.2f pv=%.2f err_old=%.2f P_err=%.2f I_err=%.2f D_err=%.2f Out=%.2f\n",
-					sp, pv, unit->PID_err_old, P_err, unit->PID_I_err, D_err, Out);
-			    syslog(LOG_NOTICE, "sp=%.2f pv=%.2f err_old=%.2f P_err=%.2f I_err=%.2f D_err=%.2f Out=%.2f",
-					sp, pv, unit->PID_err_old, P_err, unit->PID_I_err, D_err, Out);
-//			}
-			unit->PID_err_old = P_err;
-
-			if (unit->heater_address) {
+		    if (unit->heater_address) {
 			    if (Out >= 1) {
 				if (unit->heater_wait < unit->heater_delay) {
 				    unit->heater_wait++;
@@ -1380,8 +1383,8 @@
 				}
 			    }
 			    device_out(unit->heater_address, unit->heater_state);
-			}
-			if (unit->cooler_address) {
+		    }
+		    if (unit->cooler_address) {
 			    if (Out <= -1) {
 			    	if (unit->cooler_wait < unit->cooler_delay) {
 				    unit->cooler_wait++;
@@ -1404,8 +1407,8 @@
 				}
 			    }
 			    device_out(unit->cooler_address, unit->cooler_state);
-			}
-			if (unit->heater_address && unit->cooler_address && unit->fan_address) {
+		    }
+		    if (unit->heater_address && unit->cooler_address && unit->fan_address) {
 			    /*
 			     * If the temperature difference between air and beer is more then
 			     * xxx degrees, turn the fan on to make an airflow.
@@ -1424,12 +1427,11 @@
 				unit->fan_state = 0;
 			    }
 			    device_out(unit->fan_address, unit->fan_state);
-			}
-		    } else {
+		    }
+		} else {
 			P_err = 0.0;
 			unit->PID_I_err = 0.0;
 			unit->PID_err_old = 0.0;
-		    }
 		}
 	    }
 
--- a/www-thermferm/units.php	Sun Feb 15 23:09:00 2015 +0100
+++ b/www-thermferm/units.php	Mon Feb 16 19:52:42 2015 +0100
@@ -203,10 +203,10 @@
 	if (($_POST['TempSetMin'] < -5) || ($_POST['TempSetMin'] > 15))
 	    return 6;
 
-	if (($_POST['HeaterDelay'] < 0) || ($_POST['HeaterDelay'] > 45))
+	if (($_POST['HeaterDelay'] < 0) || ($_POST['HeaterDelay'] > 720))
 	    return 7;
 
-	if (($_POST['CoolerDelay'] < 0) || ($_POST['CoolerDelay'] > 45))
+	if (($_POST['CoolerDelay'] < 0) || ($_POST['CoolerDelay'] > 720))
 	    return 8;
 
 	if (($_POST['IdleRangeL'] > 0) || ($_POST['IdleRangeL'] < -5))
@@ -215,7 +215,7 @@
 	if (($_POST['IdleRangeH'] < 0) || ($_POST['IdleRangeH'] > 5))
 	    return 10;
 
-	if (($_POST['LightDelay'] < 0) || ($_POST['LightDelay'] > 45))
+	if (($_POST['LightDelay'] < 0) || ($_POST['LightDelay'] > 720))
 	    return 11;
 
     } else {
@@ -256,15 +256,15 @@
 		break;
 	case 6:	$error = 'Temperature Minimum must be between -5 and 15 &deg;C';
 		break;
-	case 7: $error = 'Heater Delay must be bewteen 0 and 45';
+	case 7: $error = 'Heater Delay must be bewteen 0 and 720 seconds';
 		break;
-	case 8:	$error = 'Cooler Delay must be bewteen 0 and 45';
+	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';
 		break;
 	case 10: $error = 'Idle Range High must be between 0 and 5';
 		break;
-	case 11: $error = 'Light Delay must be bewteen 0 and 45';
+	case 11: $error = 'Light Delay must be bewteen 0 and 720 seconds';
 		break;
 	case 99:        
 		load('units.php');
@@ -397,7 +397,7 @@
 	    if ($f[0] == "HEATER_DELAY") {
 		$outstr .= '       <tr class="editor">'.PHP_EOL;
 		$outstr .= '        <td class="editname">Heater Switch Delay</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="HeaterDelay" size="5" value="'.$f[1].'"> * 15 seconds</td>'.PHP_EOL;
+		$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] == "COOLER_ADDRESS") {
@@ -424,7 +424,7 @@
 	    if ($f[0] == "COOLER_DELAY") {
 		$outstr .= '       <tr class="editor">'.PHP_EOL;
 		$outstr .= '        <td class="editname">Cooler Switch Delay</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="CoolerDelay" size="5" value="'.$f[1].'"> * 15 seconds</td>'.PHP_EOL;
+		$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] == "FAN_ADDRESS") {
@@ -472,7 +472,7 @@
 	    if ($f[0] == "LIGHT_DELAY") {
 		$outstr .= '       <tr class="editor">'.PHP_EOL;
 		$outstr .= '        <td class="editname">Lights Delay</td>'.PHP_EOL;
-		$outstr .= '        <td class="editfield"><input type="text" name="LightDelay" size="5" value="'.$f[1].'"> * 15 seconds</td>'.PHP_EOL;
+		$outstr .= '        <td class="editfield"><input type="text" name="LightDelay" size="5" value="'.$f[1].'"> seconds (0..720)</td>'.PHP_EOL;
 		$outstr .= '       </tr>'.PHP_EOL;
 	    }
 	    if ($f[0] == "DOOR_ADDRESS") {
@@ -532,13 +532,13 @@
 	    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="5" value="'.$f[1].'"> &deg;C (Cooler margin)</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="5" value="'.$f[1].'"> &deg;C (Heater margin)</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") {
@@ -663,9 +663,9 @@
     $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="0.0" name="PID_Kp">';
-    $outstr .= '<input type="hidden" value="0.0" name="PID_Kd">';
-    $outstr .= '<input type="hidden" value="0.0" name="PID_Ki">';
+    $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="testdata" name="action">';
     $outstr .= '<input type="hidden" value="add" name="command">';
     $outstr .= '<input type="hidden" value="00000000-0000-0000-0000-000000000000" name="UUID">';

mercurial