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:
- 104:46ce1aaf8be7
- Parent:
- 91:8b192efd0288
- Child:
- 105:1899f7ed17ec
diff -r 15583327fcdd -r 46ce1aaf8be7 heating/boiler.c --- a/heating/boiler.c Tue Jan 19 19:05:06 2021 +0000 +++ b/heating/boiler.c Wed Feb 10 17:24:36 2021 +0000 @@ -6,6 +6,8 @@ #include "mstimer.h" #include "ds18b20.h" #include "fram.h" +#include "pwm.h" +#include "log.h" #define BOILER_PUMP_DIR FIO2DIR(4) // P2.4 == p22 #define BOILER_PUMP_PIN FIO2PIN(4) @@ -21,26 +23,85 @@ static char* outputRom; static int iOutputRom; static char* returnRom; static int iReturnRom; -static int32_t tankSetPoint; static int iTankSetPoint; -static int32_t tankHysteresis; static int iTankHysteresis; -static int32_t runOnResidual16ths; static int iRunOnResidual; -static int32_t runOnTime; static int iRunOnTime; +static int16_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 int8_t boilerTarget; static int iBoilerTarget; +static int8_t pumpSpeedCalling; static int iPumpSpeedCalling; +static int8_t pumpSpeedRunOn; static int iPumpSpeedRunOn; + +static int16_t rise16thsAt0; static int iRiseAt0; +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 runOnTime; } +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; } 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, 4, &tankSetPoint ); } -void BoilerSetTankHysteresis (int value) { tankHysteresis = value; FramWrite(iTankHysteresis, 4, &tankHysteresis ); } -void BoilerSetRunOnResidual16ths(int value) { runOnResidual16ths = value; FramWrite(iRunOnResidual, 4, &runOnResidual16ths ); } -void BoilerSetRunOnTime (int value) { runOnTime = value; FramWrite(iRunOnTime, 4, &runOnTime ); } +void BoilerSetTankSetPoint (int value) { tankSetPoint = value; FramWrite(iTankSetPoint, 2, &tankSetPoint ); } +void BoilerSetTankHysteresis (int value) { tankHysteresis = value; FramWrite(iTankHysteresis, 2, &tankHysteresis ); } +void BoilerSetRunOnResidual16ths(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 ); } + +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) +{ + 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; + } +} int BoilerInit() { @@ -60,18 +121,33 @@ DS18B20RomCount++; int address; + uint8_t def1; + int16_t def2; int32_t def4; - 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; - def4 = 80; address = FramLoad( 4, &tankSetPoint, &def4); if (address < 0) return -1; iTankSetPoint = address; - def4 = 5; address = FramLoad( 4, &tankHysteresis, &def4); if (address < 0) return -1; iTankHysteresis = address; - def4 = 2; address = FramLoad( 4, &runOnResidual16ths, &def4); if (address < 0) return -1; iRunOnResidual = address; - def4 = 360; address = FramLoad( 4, &runOnTime, &def4); if (address < 0) return -1; iRunOnTime = address; + 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; + 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; + def1 = 100; address = FramLoad( 1, &pumpSpeedCalling, &def1); if (address < 0) return -1; iPumpSpeedCalling = address; + def1 = 10; address = FramLoad( 1, &pumpSpeedRunOn, &def1); if (address < 0) return -1; iPumpSpeedRunOn = address; + def1 = 65; address = FramLoad( 1, &boilerTarget, &def1); if (address < 0) return -1; iBoilerTarget = address; + def2 = 10<<4; address = FramLoad( 2, &rise16thsAt0, &def2); if (address < 0) return -1; iRiseAt0 = address; + def2 = 15<<4; address = FramLoad( 2, &rise16thsAt50, &def2); if (address < 0) return -1; iRiseAt50 = address; + def2 = 20<<4; address = FramLoad( 2, &rise16thsAt100, &def2); if (address < 0) return -1; iRiseAt100 = address; BOILER_PUMP_DIR = 1; //Set the direction to 1 == output BOILER_CALL_DIR = 1; //Set the direction to 1 == output + PwmInit(400, 100); + + for (int deltaT = 30; deltaT > 0; deltaT--) + { + LogF("DeltaT %d ==> speed %d\r\n", deltaT, calculatePoint(deltaT << 4)); + } + return 0; } bool BoilerCall = false; @@ -91,10 +167,10 @@ bool BoilerPump = false; static void controlBoilerPump() { - int boilerOutput16ths = DS18B20ValueFromRom(outputRom); - int boilerReturn16ths = DS18B20ValueFromRom(returnRom); - int boilerResidual16ths = boilerOutput16ths - boilerReturn16ths; - int boilerTempsAreValid = DS18B20IsValidValue(boilerOutput16ths) && DS18B20IsValidValue(boilerReturn16ths); + 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) @@ -104,10 +180,107 @@ } else { - if (MsTimerRelative(msTimerBoilerPumpRunOn, runOnTime * 1000)) BoilerPump = false; - if (boilerTempsAreValid && boilerResidual16ths < runOnResidual16ths) BoilerPump = false; + if (MsTimerRelative(msTimerBoilerPumpRunOn, runOnTime2s * 2000)) BoilerPump = false; + if (boilerTempsAreValid && boilerResidual16ths < runOnResidual16ths ) BoilerPump = false; + } +} +int BoilerPumpSpeed = 0; +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; + + int target16ths = (int)boilerTarget << 4; + int targetRise16ths = target16ths - boilerReturn16ths; //eg 65 - eg 45 = 20*16 16ths + + _autoSpeed = calculatePoint(targetRise16ths); +} +static void controlBoilerPumpSpeed() +{ + static uint32_t msTimerReduction = 0; + calculateAutoSpeed(); + if (BoilerCall) + { + if (pumpSpeedCalling > 100 || pumpSpeedCalling < 0) BoilerPumpSpeed = _autoSpeed; //Auto + else BoilerPumpSpeed = pumpSpeedCalling; //Manual + msTimerReduction = MsTimerCount; + } + else + { + if (BoilerPumpSpeed > pumpSpeedRunOn) + { + if (MsTimerRepetitive(&msTimerReduction, 250)) BoilerPumpSpeed--; + } + else + { + BoilerPumpSpeed = pumpSpeedRunOn; + } } } +static void speedToPwm() +{ + /* + PWM input signal [%] Pump status + ≤ 10 Maximum speed + > 10 / ≤ 84 Variable speed from minimum to maximum + speed + > 84 / ≤ 91 Minimum speed + > 91/95 Hysteresis area: on/off + > 95 / ≤ 100 Standby mode: off + + 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; +} +#define TIME_BEFORE_TWEAK_SECS 120 +static void tweakDeltaTs() +{ + 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; + + static int speedLastScan = -1; + + if (speedLastScan == 0 && BoilerPumpSpeed > 0) + { + if (rise16thsAt0 > boilerResidual16ths) rise16thsAt0--; + if (rise16thsAt0 < boilerResidual16ths) rise16thsAt0++; + } + + else if (speedLastScan <= 50 && BoilerPumpSpeed > 50) + { + if (rise16thsAt50 > boilerResidual16ths) rise16thsAt50--; + if (rise16thsAt50 < boilerResidual16ths) rise16thsAt50++; + } + + else if (speedLastScan < 100 && BoilerPumpSpeed == 100) + { + if (rise16thsAt100 > boilerResidual16ths) rise16thsAt100--; + if (rise16thsAt100 < boilerResidual16ths) rise16thsAt100++; + } + + speedLastScan = BoilerPumpSpeed; +} void BoilerMain() { controlBoilerCall(); @@ -118,4 +291,10 @@ if (BoilerPump) BOILER_PUMP_SET; else BOILER_PUMP_CLR; + controlBoilerPumpSpeed(); + speedToPwm(); + PwmSet(BoilerPumpPwm); + + tweakDeltaTs(); + } \ No newline at end of file