Andrew Boyson / oldheating

Dependencies:   net 1-wire lpc1768 crypto clock web fram log

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers boiler.c Source File

boiler.c

00001 #include <string.h>
00002 #include <stdint.h>
00003 #include <stdbool.h>
00004 
00005 #include "gpio.h"
00006 #include "mstimer.h"
00007 #include "ds18b20.h"
00008 #include "fram.h"
00009 #include "pwm.h"
00010 #include "log.h"
00011 
00012 #define BOILER_PUMP_DIR FIO2DIR(4) // P2.4 == p22
00013 #define BOILER_PUMP_PIN FIO2PIN(4)
00014 #define BOILER_PUMP_SET FIO2SET(4)
00015 #define BOILER_PUMP_CLR FIO2CLR(4)
00016 
00017 #define BOILER_CALL_DIR FIO2DIR(5) // P2.5 == p21
00018 #define BOILER_CALL_PIN FIO2PIN(5)
00019 #define BOILER_CALL_SET FIO2SET(5)
00020 #define BOILER_CALL_CLR FIO2CLR(5)
00021 
00022 #define PUMP_SPEED_CALLING_AUTO_ONLY  -1
00023 #define PUMP_SPEED_CALLING_AUTO_TWEAK -2
00024 
00025 #define MAX_SPEED 100
00026 
00027 static char*     tankRom; static int iTankRom;
00028 static char*   outputRom; static int iOutputRom;
00029 static char*   returnRom; static int iReturnRom;
00030 
00031 static int8_t  fullSpeedSecs;      static int iFullSpeedSecs;
00032 
00033 static int8_t  tankSetPoint;       static int iTankSetPoint;
00034 static int16_t tankHysteresis;     static int iTankHysteresis;
00035 static int16_t _runOnDelta16ths; static int iRunOnResidual;
00036 static uint8_t runOnTime2s;        static int iRunOnTime;
00037 
00038 static int8_t  boilerTarget;       static int iBoilerTarget;
00039 static int8_t  pumpSpeedCalling;   static int iPumpSpeedCalling;
00040 static int8_t  _rampDownTime;     static int _iRampDownTime;
00041 
00042 static int8_t  _minSpeed;             static int _iMinSpeed;
00043 static int8_t  _midSpeedPwm;          static int _iMidSpeedPwm;
00044 static int16_t _fullSpeedDeltaT16ths; static int _iFullSpeedDeltaT;
00045 
00046 //Set in main scan
00047 static int16_t _boilerOutput16ths   = DS18B20_ERROR_VALUE_NOT_SET;
00048 static int16_t _boilerReturn16ths   = DS18B20_ERROR_VALUE_NOT_SET;
00049 static int16_t _boilerRtnDel16ths   = DS18B20_ERROR_VALUE_NOT_SET;
00050 static int16_t _boilerDeltaT16ths   = DS18B20_ERROR_VALUE_NOT_SET;
00051 static bool    _boilerDeltaTisValid = false;
00052 
00053 int16_t  BoilerGetTankDS18B20Value  () { return DS18B20ValueFromRom(tankRom);   } 
00054 int16_t  BoilerGetOutputDS18B20Value() { return _boilerOutput16ths;    } 
00055 int16_t  BoilerGetReturnDS18B20Value() { return _boilerReturn16ths;    }
00056 int16_t  BoilerGetRtnDelDS18B20Value() { return _boilerRtnDel16ths;    }
00057 int16_t  BoilerGetDeltaTDS18B20Value() { return _boilerDeltaT16ths;    } 
00058 int      BoilerGetFullSpeedSecs     () { return fullSpeedSecs;         }
00059 int      BoilerGetTankSetPoint      () { return tankSetPoint;          }
00060 int      BoilerGetTankHysteresis    () { return tankHysteresis;        } 
00061 int      BoilerGetRunOnDeltaT       () { return _runOnDelta16ths;      }
00062 int      BoilerGetRunOnTime         () { return runOnTime2s << 1;      }
00063 int      BoilerGetPumpSpeedCalling  () { return pumpSpeedCalling;      }
00064 int      BoilerGetRampDownTime      () { return _rampDownTime;        }
00065 int      BoilerGetOutputTarget      () { return boilerTarget;          }
00066 int      BoilerGetMinSpeed          () { return _minSpeed;             }
00067 int      BoilerGetMidSpeedPwm       () { return _midSpeedPwm;          }
00068 int      BoilerGetFullSpeedDeltaT   () { return _fullSpeedDeltaT16ths; }
00069 
00070 static void setTankRom          (char* value) { memcpy(tankRom,         value, 8); FramWrite(iTankRom,         8,  tankRom              ); }
00071 static void setOutputRom        (char* value) { memcpy(outputRom,       value, 8); FramWrite(iOutputRom,       8,  outputRom            ); }
00072 static void setReturnRom        (char* value) { memcpy(returnRom,       value, 8); FramWrite(iReturnRom,       8,  returnRom            ); }
00073 void BoilerSetFullSpeedSecs     (int   value) { fullSpeedSecs         = value;     FramWrite(iFullSpeedSecs,   1, &fullSpeedSecs        ); }
00074 void BoilerSetTankSetPoint      (int   value) { tankSetPoint          = value;     FramWrite(iTankSetPoint,    1, &tankSetPoint         ); }
00075 void BoilerSetTankHysteresis    (int   value) { tankHysteresis        = value;     FramWrite(iTankHysteresis,  2, &tankHysteresis       ); }
00076 void BoilerSetRunOnDeltaT       (int   value) { _runOnDelta16ths      = value;     FramWrite(iRunOnResidual,   2, &_runOnDelta16ths     ); }
00077 void BoilerSetRunOnTime         (int   value) { runOnTime2s           = value >> 1;FramWrite(iRunOnTime,       1, &runOnTime2s          ); }
00078 void BoilerSetPumpSpeedCalling  (int   value) { pumpSpeedCalling      = value;     FramWrite(iPumpSpeedCalling,1, &pumpSpeedCalling     ); }
00079 void BoilerSetRampDownTime      (int   value) { _rampDownTime         = value;     FramWrite(_iRampDownTime,   1, &_rampDownTime        ); }
00080 void BoilerSetOutputTarget      (int   value) { boilerTarget          = value;     FramWrite(iBoilerTarget,    1, &boilerTarget         ); }
00081 void BoilerSetMinSpeed          (int   value) { _minSpeed             = value;     FramWrite(_iMinSpeed,       1, &_minSpeed            ); }
00082 void BoilerSetMidSpeedPwm       (int   value) { _midSpeedPwm          = value;     FramWrite(_iMidSpeedPwm,    1, &_midSpeedPwm         ); }
00083 void BoilerSetFullSpeedDeltaT   (int   value) { _fullSpeedDeltaT16ths = value;     FramWrite(_iFullSpeedDeltaT,2, &_fullSpeedDeltaT16ths); }
00084 
00085 static int calculateBetweenTwoPoints(int x, int xA, int xB, int yA, int yB)
00086 {
00087     float m = (float)(yB - yA) / (xB - xA);
00088     return  yA + m * (x - xA);
00089 }
00090 static int calculateSpeedFromDeltaT(int deltaT16ths)
00091 {
00092     if (deltaT16ths < _fullSpeedDeltaT16ths) return MAX_SPEED;   //Needed in case deltaT16ths is negative or zero
00093     int speed = MAX_SPEED * _fullSpeedDeltaT16ths / deltaT16ths; //eg for 20 deg ==> 100 * (10 << 4) / (20 << 4) == 50
00094     if (speed > MAX_SPEED) speed = MAX_SPEED;
00095     if (speed < _minSpeed) speed = _minSpeed;
00096     return speed;
00097 }
00098 static int calculateDeltaTFromSpeed(int speed)
00099 {
00100     int deltaT16ths = MAX_SPEED * _fullSpeedDeltaT16ths / speed; //eg for speed = 50 ==> 100 * (10 << 4) / 50 == 20 << 4
00101     return deltaT16ths;
00102 }
00103 
00104 int BoilerInit()
00105 {
00106       tankRom = DS18B20Roms + 8 * DS18B20RomCount;
00107     DS18B20RomSetters[DS18B20RomCount] = setTankRom;
00108     DS18B20RomNames[DS18B20RomCount] = "Tank";
00109     DS18B20RomCount++;
00110     
00111     outputRom = DS18B20Roms + 8 * DS18B20RomCount;
00112     DS18B20RomSetters[DS18B20RomCount] = setOutputRom;
00113     DS18B20RomNames[DS18B20RomCount] = "BlrOut";
00114     DS18B20RomCount++;
00115     
00116     returnRom = DS18B20Roms + 8 * DS18B20RomCount;
00117     DS18B20RomSetters[DS18B20RomCount] = setReturnRom;
00118     DS18B20RomNames[DS18B20RomCount] = "BlrRtn";
00119     DS18B20RomCount++;
00120     
00121     int address;
00122     uint8_t def1;
00123     int16_t def2;
00124     int32_t def4;
00125                   address = FramLoad( 8,  tankRom,                   0); if (address < 0) return -1; iTankRom          = address;
00126                   address = FramLoad( 8,  outputRom,                 0); if (address < 0) return -1; iOutputRom        = address;
00127                   address = FramLoad( 8,  returnRom,                 0); if (address < 0) return -1; iReturnRom        = address;
00128     def1 =   100; address = FramLoad( 1, &fullSpeedSecs,         &def1); if (address < 0) return -1; iFullSpeedSecs    = address;
00129     def1 =    65; address = FramLoad( 1, &tankSetPoint,          &def1); if (address < 0) return -1; iTankSetPoint     = address;
00130     def2 =     5; address = FramLoad( 2, &tankHysteresis,        &def2); if (address < 0) return -1; iTankHysteresis   = address;
00131     def2 =     2; address = FramLoad( 2, &_runOnDelta16ths,      &def2); if (address < 0) return -1; iRunOnResidual    = address;
00132     def1 =   180; address = FramLoad( 1, &runOnTime2s,           &def1); if (address < 0) return -1; iRunOnTime        = address;
00133     def1 =   100; address = FramLoad( 1, &pumpSpeedCalling,      &def1); if (address < 0) return -1; iPumpSpeedCalling = address;
00134     def1 =    10; address = FramLoad( 1, &_rampDownTime,        &def1); if (address < 0) return -1; _iRampDownTime   = address;
00135     def1 =    65; address = FramLoad( 1, &boilerTarget,          &def1); if (address < 0) return -1; iBoilerTarget     = address;
00136     def1 =    50; address = FramLoad( 1, &_minSpeed,             &def1); if (address < 0) return -1; _iMinSpeed        = address;
00137                             FramAllocate(1);
00138     def1 =    50; address = FramLoad( 1, &_midSpeedPwm,          &def1); if (address < 0) return -1; _iMidSpeedPwm     = address;
00139                             FramAllocate(1);
00140     def2 = 10<<4; address = FramLoad( 2, &_fullSpeedDeltaT16ths, &def2); if (address < 0) return -1; _iFullSpeedDeltaT = address;
00141     
00142     BOILER_PUMP_DIR = 1; //Set the direction to 1 == output
00143     BOILER_CALL_DIR = 1; //Set the direction to 1 == output
00144 
00145     PwmInit(400, 100);
00146 
00147     return 0;
00148 }
00149 bool BoilerCallEnable = true;
00150 bool BoilerCall = false;
00151 static void controlBoilerCall()
00152 {
00153     if (BoilerCallEnable)
00154     {
00155         int tankTemp16ths = DS18B20ValueFromRom(tankRom);
00156         if (DS18B20IsValidValue(tankTemp16ths)) //Ignore values which are likely to be wrong
00157         {
00158             int  tankUpper16ths = tankSetPoint   << 4;
00159             int hysteresis16ths = tankHysteresis << 4;
00160             int  tankLower16ths = tankUpper16ths - hysteresis16ths;
00161         
00162             if (tankTemp16ths >= tankUpper16ths) BoilerCall = false;
00163             if (tankTemp16ths <= tankLower16ths) BoilerCall = true;
00164         }
00165     }
00166     else
00167     {
00168         BoilerCall = false;
00169     }
00170 }
00171 bool BoilerPump = false;
00172 static void controlBoilerPump()
00173 {
00174     static uint32_t msTimerBoilerPumpRunOn = 0;
00175     if (BoilerCall)
00176     {
00177         BoilerPump = true;
00178         msTimerBoilerPumpRunOn = MsTimerCount;
00179     }
00180     else
00181     {
00182         if (MsTimerRelative(msTimerBoilerPumpRunOn,    runOnTime2s * 2000)) BoilerPump = false;
00183         if (_boilerDeltaTisValid && _boilerDeltaT16ths < _runOnDelta16ths ) BoilerPump = false;
00184     }
00185 }
00186 int BoilerPumpFlow  = MAX_SPEED;
00187 int BoilerPumpSpeed = MAX_SPEED;
00188 int BoilerPumpPwm   = 0;
00189 static int _autoSpeed = 0;
00190 static void calculateAutoSpeed()
00191 {
00192     if (!DS18B20IsValidValue(_boilerReturn16ths)) return;
00193     
00194     int target16ths = (int)boilerTarget << 4;
00195     int targetRise16ths = target16ths - _boilerReturn16ths; //eg 65 - eg 45 = 20*16 16ths
00196     
00197     _autoSpeed = calculateSpeedFromDeltaT(targetRise16ths);
00198 }
00199 static void controlBoilerPumpSpeed()
00200 {
00201     static uint32_t msTimerReduction = 0;
00202     calculateAutoSpeed();
00203     if (BoilerCall)
00204     {
00205         if (pumpSpeedCalling < 0) BoilerPumpSpeed = _autoSpeed;        //Auto
00206         else                      BoilerPumpSpeed = pumpSpeedCalling;  //Manual
00207         msTimerReduction = MsTimerCount;
00208     }
00209     else
00210     {
00211         if (BoilerPumpSpeed > _minSpeed)
00212         {
00213             int msPerUnit = 1000 * _rampDownTime / (MAX_SPEED - _minSpeed);
00214             if (MsTimerRepetitive(&msTimerReduction, msPerUnit)) BoilerPumpSpeed--;
00215         }
00216         else
00217         {
00218             BoilerPumpSpeed = _minSpeed;
00219         }
00220     }
00221     if (BoilerPumpSpeed < _minSpeed) BoilerPumpSpeed = _minSpeed;
00222     if (BoilerPumpSpeed > MAX_SPEED) BoilerPumpSpeed = MAX_SPEED;
00223 }
00224 static int speedToPwm(int speed)
00225 {
00226     #define MAX_SPEED_PWM 10
00227     #define MIN_SPEED_PWM 84
00228     /*
00229     PWM input signal [%] Pump status
00230     ≤ 10 Maximum speed
00231     > 10 / ≤ 84 Variable speed from minimum to maximum
00232     speed
00233     > 84 / ≤ 91 Minimum speed
00234     > 91/95 Hysteresis area: on/off
00235     > 95 / ≤ 100 Standby mode: off
00236     
00237     Max speed 100 is at fitted = 74; pwm = 10
00238     Min speed   0 is at fitted =  0; pwm = 84
00239     */
00240     if (speed <= _minSpeed) return MIN_SPEED_PWM;
00241     if (speed >= MAX_SPEED) return MAX_SPEED_PWM;
00242     int midSpeed = (_minSpeed + MAX_SPEED) / 2;
00243     if (speed < midSpeed) return calculateBetweenTwoPoints(speed, _minSpeed,  midSpeed, MIN_SPEED_PWM,   _midSpeedPwm);
00244     else                  return calculateBetweenTwoPoints(speed,  midSpeed, MAX_SPEED,  _midSpeedPwm,  MAX_SPEED_PWM);
00245     //int pwm = calculateBetweenTwoPoints(BoilerPumpSpeed, _minSpeed, MAX_SPEED, 84, 10);
00246     //if (pwm < 10) pwm = 10;
00247     //if (pwm > 84) pwm = 84;
00248     //BoilerPumpPwm = pwm;
00249 }
00250 #define TIME_BEFORE_TWEAK_SECS 120
00251 static void tweakDeltaTs()
00252 {
00253     if (pumpSpeedCalling != PUMP_SPEED_CALLING_AUTO_TWEAK) return;
00254     
00255     static uint32_t msTimerBoilerHeating = 0;
00256     if (!BoilerCall) msTimerBoilerHeating = MsTimerCount;
00257     if (!MsTimerRelative(msTimerBoilerHeating, TIME_BEFORE_TWEAK_SECS * 1000)) return;
00258     
00259     if (!_boilerDeltaTisValid) return;
00260     
00261     static int speedLastScan = -1;
00262     
00263     if (speedLastScan < MAX_SPEED && BoilerPumpSpeed == MAX_SPEED)
00264     {
00265         if (_fullSpeedDeltaT16ths > _boilerDeltaT16ths) _fullSpeedDeltaT16ths--;
00266         if (_fullSpeedDeltaT16ths < _boilerDeltaT16ths) _fullSpeedDeltaT16ths++;
00267     }
00268     
00269     speedLastScan = BoilerPumpSpeed;
00270 }
00271 
00272 #define TIME_BEFORE_DELTA_T_ALARM_SECS 300
00273 #define DELTA_T_LIMIT (3 << 4)
00274 static void checkDeltaTs()
00275 {   
00276     static uint32_t msTimerDeltaTNonConform = 0;
00277     if (!BoilerCall)
00278     {
00279         msTimerDeltaTNonConform = MsTimerCount;
00280         return;
00281     }
00282     
00283     int expectedDeltaT16ths = calculateDeltaTFromSpeed(BoilerPumpSpeed);
00284     
00285     bool deltaTisOk = _boilerDeltaTisValid &&
00286                       _boilerDeltaT16ths > (expectedDeltaT16ths - DELTA_T_LIMIT) &&
00287                       _boilerDeltaT16ths < (expectedDeltaT16ths + DELTA_T_LIMIT);
00288     
00289     
00290     static bool deltaTwasOk = true;
00291     
00292     /*
00293     if (deltaTwasOk != deltaTisOk)
00294     {
00295         LogTimeF("Boiler delta T ");
00296         DS18B20Log(_boilerDeltaT16ths);
00297         if (deltaTisOk)
00298         {
00299             Log(" is inside expected value ");
00300         }
00301         else
00302         {
00303             Log(" is outside expected value ");
00304         }
00305         DS18B20Log(expectedDeltaT16ths);
00306         Log("\r\n");
00307     }
00308     
00309     deltaTwasOk = deltaTisOk;
00310     */
00311     
00312     static bool hadAlarm = false;
00313     if (deltaTisOk) msTimerDeltaTNonConform = MsTimerCount;
00314     bool haveAlarm = MsTimerRelative(msTimerDeltaTNonConform, TIME_BEFORE_DELTA_T_ALARM_SECS * 1000);
00315     if (haveAlarm && !hadAlarm)
00316     {
00317         LogTimeF("Boiler delta T would have tripped after not being ok for %d seconds\r\n", TIME_BEFORE_DELTA_T_ALARM_SECS);
00318     }
00319     hadAlarm = haveAlarm;
00320 }
00321 #define NUMBER_OF_STEPS 10
00322 static int16_t _returns16ths[NUMBER_OF_STEPS]; //0 is last, 9th is first
00323 static void delayLine()
00324 {
00325     static uint32_t msTimerDelay = 0;
00326     if (BoilerPump)
00327     {
00328         int msTotal = 1000 * fullSpeedSecs * MAX_SPEED / BoilerPumpSpeed; //speed 10 ==> 10000; speed 100 ==> 1000
00329         int msPerStep = msTotal / NUMBER_OF_STEPS;
00330         if (MsTimerRelative(msTimerDelay, msPerStep))
00331         {
00332             for (int i = 0; i < NUMBER_OF_STEPS - 1; i++) _returns16ths[i] = _returns16ths[i + 1];
00333             _returns16ths[NUMBER_OF_STEPS - 1] = _boilerReturn16ths;
00334             msTimerDelay = MsTimerCount;
00335             //LogTimeF("Ms per step = %d, delayed boiler return = ", msPerStep);
00336             //DS18B20Log(_returns16ths[0]);
00337             //Log("\r\n");
00338         }
00339     }
00340     else
00341     {
00342         msTimerDelay = MsTimerCount;
00343         for (int i = 0; i < NUMBER_OF_STEPS; i++) _returns16ths[i] = DS18B20_ERROR_VALUE_NOT_SET;
00344     }
00345 }
00346 
00347 void BoilerMain()
00348 {
00349     delayLine();
00350     _boilerOutput16ths   = DS18B20ValueFromRom(outputRom);
00351     _boilerReturn16ths   = DS18B20ValueFromRom(returnRom);
00352     _boilerRtnDel16ths   = _returns16ths[0];
00353     _boilerDeltaTisValid = DS18B20IsValidValue(_boilerOutput16ths) && DS18B20IsValidValue(_boilerRtnDel16ths);
00354     if (_boilerDeltaTisValid) _boilerDeltaT16ths   = _boilerOutput16ths - _boilerRtnDel16ths;
00355     else                      _boilerDeltaT16ths   = DS18B20_ERROR_VALUE_NOT_SET;
00356     
00357     controlBoilerCall();
00358     if (BoilerCall) BOILER_CALL_SET;
00359     else            BOILER_CALL_CLR;
00360     
00361     controlBoilerPump();
00362     if (BoilerPump) BOILER_PUMP_SET;
00363     else            BOILER_PUMP_CLR;
00364     
00365     controlBoilerPumpSpeed();
00366     BoilerPumpPwm = speedToPwm(BoilerPumpSpeed);
00367     PwmSet(BoilerPumpPwm);
00368     
00369     tweakDeltaTs();
00370     checkDeltaTs();
00371 }