Central Heating controller using the real time clock, PHY module for internet, 1-wire interface for temperature sensors, a system log and a configuration file
Dependencies: net 1-wire lpc1768 crypto clock web fram log
/media/uploads/andrewboyson/heating.sch
/media/uploads/andrewboyson/heating.brd
/media/uploads/andrewboyson/eagle.epf
Diff: heating/boiler.c
- Revision:
- 105:1899f7ed17ec
- Parent:
- 104:46ce1aaf8be7
- Child:
- 106:41ed3ea0bbba
diff -r 46ce1aaf8be7 -r 1899f7ed17ec heating/boiler.c --- a/heating/boiler.c Wed Feb 10 17:24:36 2021 +0000 +++ b/heating/boiler.c Tue Feb 23 20:35:07 2021 +0000 @@ -19,14 +19,22 @@ #define BOILER_CALL_SET FIO2SET(5) #define BOILER_CALL_CLR FIO2CLR(5) +#define PUMP_SPEED_CALLING_AUTO_ONLY -1 +#define PUMP_SPEED_CALLING_AUTO_TWEAK -2 + +#define MIN_SPEED 40 +#define MAX_SPEED 100 + static char* tankRom; static int iTankRom; static char* outputRom; static int iOutputRom; static char* returnRom; static int iReturnRom; -static int16_t tankSetPoint; static int iTankSetPoint; +static int8_t fullSpeedSecs; static int iFullSpeedSecs; + +static int8_t tankSetPoint; static int iTankSetPoint; static int16_t tankHysteresis; static int iTankHysteresis; static int16_t runOnResidual16ths; static int iRunOnResidual; -static uint8_t runOnTime2s; static int iRunOnTime; +static uint8_t runOnTime2s; static int iRunOnTime; static int8_t boilerTarget; static int iBoilerTarget; static int8_t pumpSpeedCalling; static int iPumpSpeedCalling; @@ -36,71 +44,71 @@ static int16_t rise16thsAt50; static int iRiseAt50; static int16_t rise16thsAt100; static int iRiseAt100; -uint16_t BoilerGetTankDS18B20Value () { return DS18B20ValueFromRom(tankRom); } -uint16_t BoilerGetOutputDS18B20Value() { return DS18B20ValueFromRom(outputRom); } -uint16_t BoilerGetReturnDS18B20Value() { return DS18B20ValueFromRom(returnRom); } -int BoilerGetTankSetPoint () { return tankSetPoint; } -int BoilerGetTankHysteresis () { return tankHysteresis; } -int BoilerGetRunOnResidual16ths() { return runOnResidual16ths;} -int BoilerGetRunOnTime () { return runOnTime2s << 1; } -int BoilerGetPumpSpeedCalling () { return pumpSpeedCalling; } -int BoilerGetPumpSpeedRunOn () { return pumpSpeedRunOn; } -int BoilerGetOutputTarget () { return boilerTarget; } -int BoilerGetRise16thsAt0 () { return rise16thsAt0; } -int BoilerGetRise16thsAt50 () { return rise16thsAt50; } -int BoilerGetRise16thsAt100 () { return rise16thsAt100; } +//Set in main scan +static int16_t _boilerOutput16ths = DS18B20_ERROR_VALUE_NOT_SET; +static int16_t _boilerReturn16ths = DS18B20_ERROR_VALUE_NOT_SET; +static int16_t _boilerRtnDel16ths = DS18B20_ERROR_VALUE_NOT_SET; +static int16_t _boilerDeltaT16ths = DS18B20_ERROR_VALUE_NOT_SET; +static bool _boilerDeltaTisValid = false; + +int16_t BoilerGetTankDS18B20Value () { return DS18B20ValueFromRom(tankRom); } +int16_t BoilerGetOutputDS18B20Value() { return _boilerOutput16ths; } +int16_t BoilerGetReturnDS18B20Value() { return _boilerReturn16ths; } +int16_t BoilerGetRtnDelDS18B20Value() { return _boilerRtnDel16ths; } +int16_t BoilerGetDeltaTDS18B20Value() { return _boilerDeltaT16ths; } +int BoilerGetFullSpeedSecs () { return fullSpeedSecs; } +int BoilerGetTankSetPoint () { return tankSetPoint; } +int BoilerGetTankHysteresis () { return tankHysteresis; } +int BoilerGetRunOnDeltaT () { return runOnResidual16ths; } +int BoilerGetRunOnTime () { return runOnTime2s << 1; } +int BoilerGetPumpSpeedCalling () { return pumpSpeedCalling; } +int BoilerGetPumpSpeedRunOn () { return pumpSpeedRunOn; } +int BoilerGetOutputTarget () { return boilerTarget; } +int BoilerGetMinimumFlow () { return rise16thsAt0; } +int BoilerGetMidFlowSpeed () { return rise16thsAt50; } +int BoilerGetFullSpeedDeltaT () { return rise16thsAt100; } static void setTankRom (char* value) { memcpy(tankRom, value, 8); FramWrite(iTankRom, 8, tankRom ); } static void setOutputRom (char* value) { memcpy(outputRom, value, 8); FramWrite(iOutputRom, 8, outputRom ); } static void setReturnRom (char* value) { memcpy(returnRom, value, 8); FramWrite(iReturnRom, 8, returnRom ); } -void BoilerSetTankSetPoint (int value) { tankSetPoint = value; FramWrite(iTankSetPoint, 2, &tankSetPoint ); } +void BoilerSetFullSpeedSecs (int value) { fullSpeedSecs = value; FramWrite(iFullSpeedSecs, 1, &fullSpeedSecs ); } +void BoilerSetTankSetPoint (int value) { tankSetPoint = value; FramWrite(iTankSetPoint, 1, &tankSetPoint ); } void BoilerSetTankHysteresis (int value) { tankHysteresis = value; FramWrite(iTankHysteresis, 2, &tankHysteresis ); } -void BoilerSetRunOnResidual16ths(int value) { runOnResidual16ths = value; FramWrite(iRunOnResidual, 2, &runOnResidual16ths ); } +void BoilerSetRunOnDeltaT (int value) { runOnResidual16ths = value; FramWrite(iRunOnResidual, 2, &runOnResidual16ths ); } void BoilerSetRunOnTime (int value) { runOnTime2s = value >> 1;FramWrite(iRunOnTime, 1, &runOnTime2s ); } void BoilerSetPumpSpeedCalling (int value) { pumpSpeedCalling = value; FramWrite(iPumpSpeedCalling,1, &pumpSpeedCalling ); } void BoilerSetPumpSpeedRunOn (int value) { pumpSpeedRunOn = value; FramWrite(iPumpSpeedRunOn, 1, &pumpSpeedRunOn ); } void BoilerSetOutputTarget (int value) { boilerTarget = value; FramWrite(iBoilerTarget, 1, &boilerTarget ); } -void BoilerSetRise16thsAt0 (int value) { rise16thsAt0 = value; FramWrite(iRiseAt0, 2, &rise16thsAt0 ); } -void BoilerSetRise16thsAt50 (int value) { rise16thsAt50 = value; FramWrite(iRiseAt50, 2, &rise16thsAt50 ); } -void BoilerSetRise16thsAt100 (int value) { rise16thsAt100 = value; FramWrite(iRiseAt100, 2, &rise16thsAt100 ); } +void BoilerSetMinimumFlow (int value) { rise16thsAt0 = value; FramWrite(iRiseAt0, 2, &rise16thsAt0 ); } +void BoilerSetMidFlowSpeed (int value) { rise16thsAt50 = value; FramWrite(iRiseAt50, 2, &rise16thsAt50 ); } +void BoilerSetFullSpeedDeltaT (int value) { rise16thsAt100 = value; FramWrite(iRiseAt100, 2, &rise16thsAt100 ); } static int calculateBetweenTwoPoints(int x, int xA, int xB, int yA, int yB) { float m = (float)(yB - yA) / (xB - xA); return yA + m * (x - xA); } -static int oldcalculateBetweenTwoPoints(int point16ths, int pointBig16ths, int pointSmall16ths, int bigSpeed, int width) //width = 1 for 100; 2 for 50 and 4 for 25 -{ - //Expected to have to compensate the slope for the reduced width but didn't need to. - int slope = pointBig16ths - pointSmall16ths; - int diff16ths = point16ths - pointSmall16ths; - int diffSpeed = (diff16ths * slope * width) >> 8; //points are in 16ths so multiplying them needs a division by 256 - int requiredSpeed = bigSpeed - diffSpeed; - if (requiredSpeed > 100) requiredSpeed = 100; - if (requiredSpeed < 0) requiredSpeed = 0; - return requiredSpeed; -} - -static int calculatePoint(int targetRise16ths) +static int calculateSpeedFromDeltaT(int deltaT16ths) { - if (targetRise16ths > rise16thsAt0) //20 - { - return 0; - } - else if (targetRise16ths > rise16thsAt50) //15 - { - return calculateBetweenTwoPoints(targetRise16ths, rise16thsAt50, rise16thsAt0, 50, 0); - //return oldcalculateBetweenTwoPoints(targetRise16ths, rise16thsAt0, rise16thsAt50, 50, 2); - } - else if (targetRise16ths > rise16thsAt100) //10 - { - return calculateBetweenTwoPoints(targetRise16ths, rise16thsAt100, rise16thsAt50, 100, 50); - //return oldcalculateBetweenTwoPoints(targetRise16ths, rise16thsAt50, rise16thsAt100, 100, 2); - } - else - { - return 100; - } + if (deltaT16ths < rise16thsAt100) return MAX_SPEED; //Needed in case deltaT16ths is negative or zero + int flow = MAX_SPEED * rise16thsAt100 / deltaT16ths; //eg for 20 deg ==> 100 * (10 << 4) / (20 << 4) == 50 + if (flow > MAX_SPEED) flow = MAX_SPEED; + if (flow < MIN_SPEED) flow = MIN_SPEED; + return flow; + + //if (deltaT16ths > rise16thsAt0 ) return MIN_SPEED; + //else if (deltaT16ths > rise16thsAt50 ) return calculateBetweenTwoPoints(deltaT16ths, rise16thsAt50, rise16thsAt0, MID_SPEED, MIN_SPEED); + //else if (deltaT16ths > rise16thsAt100) return calculateBetweenTwoPoints(deltaT16ths, rise16thsAt100, rise16thsAt50, MAX_SPEED, MID_SPEED); + //else return MAX_SPEED; +} +static int calculateDeltaTFromSpeed(int speed) +{ + int deltaT16ths = MAX_SPEED * rise16thsAt100 / speed; //eg for speed = 50 ==> 100 * (10 << 4) / 50 == 20 << 4 + //if (speed >= MAX_SPEED) return rise16thsAt100; + //else if (speed > MID_SPEED) return calculateBetweenTwoPoints(speed, MID_SPEED, MAX_SPEED, rise16thsAt50, rise16thsAt100); + //else if (speed > MIN_SPEED) return calculateBetweenTwoPoints(speed, MIN_SPEED, MID_SPEED, rise16thsAt0, rise16thsAt50 ); + //else return rise16thsAt0; + return deltaT16ths; } int BoilerInit() @@ -127,7 +135,8 @@ address = FramLoad( 8, tankRom, 0); if (address < 0) return -1; iTankRom = address; address = FramLoad( 8, outputRom, 0); if (address < 0) return -1; iOutputRom = address; address = FramLoad( 8, returnRom, 0); if (address < 0) return -1; iReturnRom = address; - def2 = 65; address = FramLoad( 2, &tankSetPoint, &def2); if (address < 0) return -1; iTankSetPoint = address; + def1 = 100; address = FramLoad( 1, &fullSpeedSecs, &def1); if (address < 0) return -1; iFullSpeedSecs = address; + def1 = 65; address = FramLoad( 1, &tankSetPoint, &def1); if (address < 0) return -1; iTankSetPoint = address; def2 = 5; address = FramLoad( 2, &tankHysteresis, &def2); if (address < 0) return -1; iTankHysteresis = address; def2 = 2; address = FramLoad( 2, &runOnResidual16ths, &def2); if (address < 0) return -1; iRunOnResidual = address; def1 = 180; address = FramLoad( 1, &runOnTime2s, &def1); if (address < 0) return -1; iRunOnTime = address; @@ -143,35 +152,33 @@ PwmInit(400, 100); - for (int deltaT = 30; deltaT > 0; deltaT--) - { - LogF("DeltaT %d ==> speed %d\r\n", deltaT, calculatePoint(deltaT << 4)); - } - return 0; } +bool BoilerCallEnable = true; bool BoilerCall = false; static void controlBoilerCall() { - int tankTemp16ths = DS18B20ValueFromRom(tankRom); - if (DS18B20IsValidValue(tankTemp16ths)) //Ignore values which are likely to be wrong + if (BoilerCallEnable) { - int tankUpper16ths = tankSetPoint << 4; - int hysteresis16ths = tankHysteresis << 4; - int tankLower16ths = tankUpper16ths - hysteresis16ths; - - if (tankTemp16ths >= tankUpper16ths) BoilerCall = false; - if (tankTemp16ths <= tankLower16ths) BoilerCall = true; + int tankTemp16ths = DS18B20ValueFromRom(tankRom); + if (DS18B20IsValidValue(tankTemp16ths)) //Ignore values which are likely to be wrong + { + int tankUpper16ths = tankSetPoint << 4; + int hysteresis16ths = tankHysteresis << 4; + int tankLower16ths = tankUpper16ths - hysteresis16ths; + + if (tankTemp16ths >= tankUpper16ths) BoilerCall = false; + if (tankTemp16ths <= tankLower16ths) BoilerCall = true; + } + } + else + { + BoilerCall = false; } } bool BoilerPump = false; static void controlBoilerPump() { - int16_t boilerOutput16ths = DS18B20ValueFromRom(outputRom); - int16_t boilerReturn16ths = DS18B20ValueFromRom(returnRom); - int16_t boilerResidual16ths = boilerOutput16ths - boilerReturn16ths; - bool boilerTempsAreValid = DS18B20IsValidValue(boilerOutput16ths) && DS18B20IsValidValue(boilerReturn16ths); - static uint32_t msTimerBoilerPumpRunOn = 0; if (BoilerCall) { @@ -181,25 +188,21 @@ else { if (MsTimerRelative(msTimerBoilerPumpRunOn, runOnTime2s * 2000)) BoilerPump = false; - if (boilerTempsAreValid && boilerResidual16ths < runOnResidual16ths ) BoilerPump = false; + if (_boilerDeltaTisValid && _boilerDeltaT16ths < runOnResidual16ths ) BoilerPump = false; } } -int BoilerPumpSpeed = 0; +int BoilerPumpFlow = MIN_SPEED; +int BoilerPumpSpeed = MIN_SPEED; int BoilerPumpPwm = 0; static int _autoSpeed = 0; static void calculateAutoSpeed() { - int16_t boilerOutput16ths = DS18B20ValueFromRom(outputRom); - int16_t boilerReturn16ths = DS18B20ValueFromRom(returnRom); - int16_t boilerResidual16ths = boilerOutput16ths - boilerReturn16ths; - bool boilerTempsAreValid = DS18B20IsValidValue(boilerOutput16ths) && DS18B20IsValidValue(boilerReturn16ths); - - if (!boilerTempsAreValid) return; + if (!DS18B20IsValidValue(_boilerReturn16ths)) return; int target16ths = (int)boilerTarget << 4; - int targetRise16ths = target16ths - boilerReturn16ths; //eg 65 - eg 45 = 20*16 16ths + int targetRise16ths = target16ths - _boilerReturn16ths; //eg 65 - eg 45 = 20*16 16ths - _autoSpeed = calculatePoint(targetRise16ths); + _autoSpeed = calculateSpeedFromDeltaT(targetRise16ths); } static void controlBoilerPumpSpeed() { @@ -207,8 +210,8 @@ calculateAutoSpeed(); if (BoilerCall) { - if (pumpSpeedCalling > 100 || pumpSpeedCalling < 0) BoilerPumpSpeed = _autoSpeed; //Auto - else BoilerPumpSpeed = pumpSpeedCalling; //Manual + if (pumpSpeedCalling > MAX_SPEED || pumpSpeedCalling < MIN_SPEED) BoilerPumpSpeed = _autoSpeed; //Auto + else BoilerPumpSpeed = pumpSpeedCalling; //Manual msTimerReduction = MsTimerCount; } else @@ -223,6 +226,10 @@ } } } +static void flowToSpeed() +{ + //Do nothing yet +} static void speedToPwm() { /* @@ -237,52 +244,118 @@ Max speed 100 is at fitted = 74; pwm = 10 Min speed 0 is at fitted = 0; pwm = 84 */ - int speed = BoilerPumpSpeed; - if (speed < 0) speed = 0; - if (speed > 100) speed = 100; - speed *= 74; - speed <<= 16; - speed /= 100; - speed >>= 16; - BoilerPumpPwm = 84 - speed; + int pwm = calculateBetweenTwoPoints(BoilerPumpSpeed, MIN_SPEED, MAX_SPEED, 84, 10); + if (pwm < 10) pwm = 10; + if (pwm > 84) pwm = 84; + BoilerPumpPwm = pwm; } #define TIME_BEFORE_TWEAK_SECS 120 static void tweakDeltaTs() { + if (pumpSpeedCalling != PUMP_SPEED_CALLING_AUTO_TWEAK) return; + static uint32_t msTimerBoilerHeating = 0; if (!BoilerCall) msTimerBoilerHeating = MsTimerCount; if (!MsTimerRelative(msTimerBoilerHeating, TIME_BEFORE_TWEAK_SECS * 1000)) return; - int16_t boilerOutput16ths = DS18B20ValueFromRom(outputRom); - int16_t boilerReturn16ths = DS18B20ValueFromRom(returnRom); - int16_t boilerResidual16ths = boilerOutput16ths - boilerReturn16ths; - bool boilerTempsAreValid = DS18B20IsValidValue(boilerOutput16ths) && DS18B20IsValidValue(boilerReturn16ths); - if (!boilerTempsAreValid) return; + if (!_boilerDeltaTisValid) return; static int speedLastScan = -1; - if (speedLastScan == 0 && BoilerPumpSpeed > 0) - { - if (rise16thsAt0 > boilerResidual16ths) rise16thsAt0--; - if (rise16thsAt0 < boilerResidual16ths) rise16thsAt0++; - } - - else if (speedLastScan <= 50 && BoilerPumpSpeed > 50) + if (speedLastScan < MAX_SPEED && BoilerPumpSpeed == MAX_SPEED) { - if (rise16thsAt50 > boilerResidual16ths) rise16thsAt50--; - if (rise16thsAt50 < boilerResidual16ths) rise16thsAt50++; - } - - else if (speedLastScan < 100 && BoilerPumpSpeed == 100) - { - if (rise16thsAt100 > boilerResidual16ths) rise16thsAt100--; - if (rise16thsAt100 < boilerResidual16ths) rise16thsAt100++; + if (rise16thsAt100 > _boilerDeltaT16ths) rise16thsAt100--; + if (rise16thsAt100 < _boilerDeltaT16ths) rise16thsAt100++; } speedLastScan = BoilerPumpSpeed; } + +#define TIME_BEFORE_DELTA_T_ALARM_SECS 300 +#define DELTA_T_LIMIT (3 << 4) +static void checkDeltaTs() +{ + static uint32_t msTimerDeltaTNonConform = 0; + if (!BoilerCall) + { + msTimerDeltaTNonConform = MsTimerCount; + return; + } + + int expectedDeltaT16ths = calculateDeltaTFromSpeed(BoilerPumpSpeed); + + bool deltaTisOk = _boilerDeltaTisValid && + _boilerDeltaT16ths > (expectedDeltaT16ths - DELTA_T_LIMIT) && + _boilerDeltaT16ths < (expectedDeltaT16ths + DELTA_T_LIMIT); + + + static bool deltaTwasOk = true; + + /* + if (deltaTwasOk != deltaTisOk) + { + LogTimeF("Boiler delta T "); + DS18B20Log(_boilerDeltaT16ths); + if (deltaTisOk) + { + Log(" is inside expected value "); + } + else + { + Log(" is outside expected value "); + } + DS18B20Log(expectedDeltaT16ths); + Log("\r\n"); + } + + deltaTwasOk = deltaTisOk; + */ + + static bool hadAlarm = false; + if (deltaTisOk) msTimerDeltaTNonConform = MsTimerCount; + bool haveAlarm = MsTimerRelative(msTimerDeltaTNonConform, TIME_BEFORE_DELTA_T_ALARM_SECS * 1000); + if (haveAlarm && !hadAlarm) + { + LogTimeF("Boiler delta T would have tripped after not being ok for %d seconds\r\n", TIME_BEFORE_DELTA_T_ALARM_SECS); + } + hadAlarm = haveAlarm; +} +#define NUMBER_OF_STEPS 10 +static int16_t _returns16ths[NUMBER_OF_STEPS]; //0 is last, 9th is first +static void delayLine() +{ + static uint32_t msTimerDelay = 0; + if (BoilerPump) + { + int msTotal = 1000 * fullSpeedSecs * MAX_SPEED / BoilerPumpSpeed; //speed 10 ==> 10000; speed 100 ==> 1000 + int msPerStep = msTotal / NUMBER_OF_STEPS; + if (MsTimerRelative(msTimerDelay, msPerStep)) + { + for (int i = 0; i < NUMBER_OF_STEPS - 1; i++) _returns16ths[i] = _returns16ths[i + 1]; + _returns16ths[NUMBER_OF_STEPS - 1] = _boilerReturn16ths; + msTimerDelay = MsTimerCount; + //LogTimeF("Ms per step = %d, delayed boiler return = ", msPerStep); + //DS18B20Log(_returns16ths[0]); + //Log("\r\n"); + } + } + else + { + msTimerDelay = MsTimerCount; + for (int i = 0; i < NUMBER_OF_STEPS; i++) _returns16ths[i] = DS18B20_ERROR_VALUE_NOT_SET; + } +} + void BoilerMain() { + delayLine(); + _boilerOutput16ths = DS18B20ValueFromRom(outputRom); + _boilerReturn16ths = DS18B20ValueFromRom(returnRom); + _boilerRtnDel16ths = _returns16ths[0]; + _boilerDeltaTisValid = DS18B20IsValidValue(_boilerOutput16ths) && DS18B20IsValidValue(_boilerRtnDel16ths); + if (_boilerDeltaTisValid) _boilerDeltaT16ths = _boilerOutput16ths - _boilerRtnDel16ths; + else _boilerDeltaT16ths = DS18B20_ERROR_VALUE_NOT_SET; + controlBoilerCall(); if (BoilerCall) BOILER_CALL_SET; else BOILER_CALL_CLR; @@ -292,9 +365,10 @@ else BOILER_PUMP_CLR; controlBoilerPumpSpeed(); + flowToSpeed(); speedToPwm(); PwmSet(BoilerPumpPwm); tweakDeltaTs(); - + checkDeltaTs(); } \ No newline at end of file