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-02-10
Revision:
104:46ce1aaf8be7
Parent:
91:8b192efd0288
Child:
105:1899f7ed17ec

File content as of revision 104:46ce1aaf8be7:

#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)

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 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 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,    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()
{
      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;
    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;
static void controlBoilerCall()
{
    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;
    }
}
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)
    {
        BoilerPump = true;
        msTimerBoilerPumpRunOn = MsTimerCount;
    }
    else
    {
        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();
    if (BoilerCall) BOILER_CALL_SET;
    else            BOILER_CALL_CLR;
    
    controlBoilerPump();
    if (BoilerPump) BOILER_PUMP_SET;
    else            BOILER_PUMP_CLR;
    
    controlBoilerPumpSpeed();
    speedToPwm();
    PwmSet(BoilerPumpPwm);
    
    tweakDeltaTs();
    
}