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
heating/boiler.c
- Committer:
- andrewboyson
- Date:
- 2021-04-23
- Revision:
- 106:41ed3ea0bbba
- Parent:
- 105:1899f7ed17ec
File content as of revision 106:41ed3ea0bbba:
#include <string.h> #include <stdint.h> #include <stdbool.h> #include "gpio.h" #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) #define BOILER_PUMP_SET FIO2SET(4) #define BOILER_PUMP_CLR FIO2CLR(4) #define BOILER_CALL_DIR FIO2DIR(5) // P2.5 == p21 #define BOILER_CALL_PIN FIO2PIN(5) #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 MAX_SPEED 100 static char* tankRom; static int iTankRom; static char* outputRom; static int iOutputRom; static char* returnRom; static int iReturnRom; static int8_t fullSpeedSecs; static int iFullSpeedSecs; static int8_t tankSetPoint; static int iTankSetPoint; static int16_t tankHysteresis; static int iTankHysteresis; static int16_t _runOnDelta16ths; 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 _rampDownTime; static int _iRampDownTime; static int8_t _minSpeed; static int _iMinSpeed; static int8_t _midSpeedPwm; static int _iMidSpeedPwm; static int16_t _fullSpeedDeltaT16ths; static int _iFullSpeedDeltaT; //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 _runOnDelta16ths; } int BoilerGetRunOnTime () { return runOnTime2s << 1; } int BoilerGetPumpSpeedCalling () { return pumpSpeedCalling; } int BoilerGetRampDownTime () { return _rampDownTime; } int BoilerGetOutputTarget () { return boilerTarget; } int BoilerGetMinSpeed () { return _minSpeed; } int BoilerGetMidSpeedPwm () { return _midSpeedPwm; } int BoilerGetFullSpeedDeltaT () { return _fullSpeedDeltaT16ths; } 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 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 BoilerSetRunOnDeltaT (int value) { _runOnDelta16ths = value; FramWrite(iRunOnResidual, 2, &_runOnDelta16ths ); } void BoilerSetRunOnTime (int value) { runOnTime2s = value >> 1;FramWrite(iRunOnTime, 1, &runOnTime2s ); } void BoilerSetPumpSpeedCalling (int value) { pumpSpeedCalling = value; FramWrite(iPumpSpeedCalling,1, &pumpSpeedCalling ); } void BoilerSetRampDownTime (int value) { _rampDownTime = value; FramWrite(_iRampDownTime, 1, &_rampDownTime ); } void BoilerSetOutputTarget (int value) { boilerTarget = value; FramWrite(iBoilerTarget, 1, &boilerTarget ); } void BoilerSetMinSpeed (int value) { _minSpeed = value; FramWrite(_iMinSpeed, 1, &_minSpeed ); } void BoilerSetMidSpeedPwm (int value) { _midSpeedPwm = value; FramWrite(_iMidSpeedPwm, 1, &_midSpeedPwm ); } void BoilerSetFullSpeedDeltaT (int value) { _fullSpeedDeltaT16ths = value; FramWrite(_iFullSpeedDeltaT,2, &_fullSpeedDeltaT16ths); } 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 calculateSpeedFromDeltaT(int deltaT16ths) { if (deltaT16ths < _fullSpeedDeltaT16ths) return MAX_SPEED; //Needed in case deltaT16ths is negative or zero int speed = MAX_SPEED * _fullSpeedDeltaT16ths / deltaT16ths; //eg for 20 deg ==> 100 * (10 << 4) / (20 << 4) == 50 if (speed > MAX_SPEED) speed = MAX_SPEED; if (speed < _minSpeed) speed = _minSpeed; return speed; } static int calculateDeltaTFromSpeed(int speed) { int deltaT16ths = MAX_SPEED * _fullSpeedDeltaT16ths / speed; //eg for speed = 50 ==> 100 * (10 << 4) / 50 == 20 << 4 return deltaT16ths; } int BoilerInit() { tankRom = DS18B20Roms + 8 * DS18B20RomCount; DS18B20RomSetters[DS18B20RomCount] = setTankRom; DS18B20RomNames[DS18B20RomCount] = "Tank"; DS18B20RomCount++; outputRom = DS18B20Roms + 8 * DS18B20RomCount; DS18B20RomSetters[DS18B20RomCount] = setOutputRom; DS18B20RomNames[DS18B20RomCount] = "BlrOut"; DS18B20RomCount++; returnRom = DS18B20Roms + 8 * DS18B20RomCount; DS18B20RomSetters[DS18B20RomCount] = setReturnRom; DS18B20RomNames[DS18B20RomCount] = "BlrRtn"; 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; 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, &_runOnDelta16ths, &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, &_rampDownTime, &def1); if (address < 0) return -1; _iRampDownTime = address; def1 = 65; address = FramLoad( 1, &boilerTarget, &def1); if (address < 0) return -1; iBoilerTarget = address; def1 = 50; address = FramLoad( 1, &_minSpeed, &def1); if (address < 0) return -1; _iMinSpeed = address; FramAllocate(1); def1 = 50; address = FramLoad( 1, &_midSpeedPwm, &def1); if (address < 0) return -1; _iMidSpeedPwm = address; FramAllocate(1); def2 = 10<<4; address = FramLoad( 2, &_fullSpeedDeltaT16ths, &def2); if (address < 0) return -1; _iFullSpeedDeltaT = address; BOILER_PUMP_DIR = 1; //Set the direction to 1 == output BOILER_CALL_DIR = 1; //Set the direction to 1 == output PwmInit(400, 100); return 0; } bool BoilerCallEnable = true; bool BoilerCall = false; static void controlBoilerCall() { if (BoilerCallEnable) { 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() { static uint32_t msTimerBoilerPumpRunOn = 0; if (BoilerCall) { BoilerPump = true; msTimerBoilerPumpRunOn = MsTimerCount; } else { if (MsTimerRelative(msTimerBoilerPumpRunOn, runOnTime2s * 2000)) BoilerPump = false; if (_boilerDeltaTisValid && _boilerDeltaT16ths < _runOnDelta16ths ) BoilerPump = false; } } int BoilerPumpFlow = MAX_SPEED; int BoilerPumpSpeed = MAX_SPEED; int BoilerPumpPwm = 0; static int _autoSpeed = 0; static void calculateAutoSpeed() { if (!DS18B20IsValidValue(_boilerReturn16ths)) return; int target16ths = (int)boilerTarget << 4; int targetRise16ths = target16ths - _boilerReturn16ths; //eg 65 - eg 45 = 20*16 16ths _autoSpeed = calculateSpeedFromDeltaT(targetRise16ths); } static void controlBoilerPumpSpeed() { static uint32_t msTimerReduction = 0; calculateAutoSpeed(); if (BoilerCall) { if (pumpSpeedCalling < 0) BoilerPumpSpeed = _autoSpeed; //Auto else BoilerPumpSpeed = pumpSpeedCalling; //Manual msTimerReduction = MsTimerCount; } else { if (BoilerPumpSpeed > _minSpeed) { int msPerUnit = 1000 * _rampDownTime / (MAX_SPEED - _minSpeed); if (MsTimerRepetitive(&msTimerReduction, msPerUnit)) BoilerPumpSpeed--; } else { BoilerPumpSpeed = _minSpeed; } } if (BoilerPumpSpeed < _minSpeed) BoilerPumpSpeed = _minSpeed; if (BoilerPumpSpeed > MAX_SPEED) BoilerPumpSpeed = MAX_SPEED; } static int speedToPwm(int speed) { #define MAX_SPEED_PWM 10 #define MIN_SPEED_PWM 84 /* 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 */ if (speed <= _minSpeed) return MIN_SPEED_PWM; if (speed >= MAX_SPEED) return MAX_SPEED_PWM; int midSpeed = (_minSpeed + MAX_SPEED) / 2; if (speed < midSpeed) return calculateBetweenTwoPoints(speed, _minSpeed, midSpeed, MIN_SPEED_PWM, _midSpeedPwm); else return calculateBetweenTwoPoints(speed, midSpeed, MAX_SPEED, _midSpeedPwm, MAX_SPEED_PWM); //int pwm = calculateBetweenTwoPoints(BoilerPumpSpeed, _minSpeed, 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; if (!_boilerDeltaTisValid) return; static int speedLastScan = -1; if (speedLastScan < MAX_SPEED && BoilerPumpSpeed == MAX_SPEED) { if (_fullSpeedDeltaT16ths > _boilerDeltaT16ths) _fullSpeedDeltaT16ths--; if (_fullSpeedDeltaT16ths < _boilerDeltaT16ths) _fullSpeedDeltaT16ths++; } 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; controlBoilerPump(); if (BoilerPump) BOILER_PUMP_SET; else BOILER_PUMP_CLR; controlBoilerPumpSpeed(); BoilerPumpPwm = speedToPwm(BoilerPumpSpeed); PwmSet(BoilerPumpPwm); tweakDeltaTs(); checkDeltaTs(); }