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/program.c

Committer:
andrewboyson
Date:
2021-04-23
Revision:
106:41ed3ea0bbba
Parent:
95:97621bfbedfa

File content as of revision 106:41ed3ea0bbba:

#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#include "rtc.h"
#include "clktime.h"
#include "clk.h"
#include "log.h"
#include "fram.h"
#include "http.h"

#define PROGRAMS_COUNT    3
#define TRANSITIONS_COUNT 4
static int16_t programs[PROGRAMS_COUNT][TRANSITIONS_COUNT]; static int iPrograms;
static char    programDay[7];                               static int iDay;
static char    programNewDayHour;                           static int iNewDayHour;

int  ProgramGetDay       (int i)             { return (int) programDay[i];    } 
int  ProgramGetNewDayHour()                  { return (int) programNewDayHour;}

void ProgramSetDay       (int i, int  value) { programDay       [i] = (char)value; FramWrite(iDay        + i, 1, &programDay       [i]); }
void ProgramSetNewDayHour(       int  value) { programNewDayHour    = (char)value; FramWrite(iNewDayHour,     1, &programNewDayHour   ); }

int ProgramInit()
{
    char def1;
    int address;
    int programSize = PROGRAMS_COUNT * TRANSITIONS_COUNT * sizeof(int16_t);
              address = FramLoad(7,            programDay,         NULL); if (address < 0) return -1; iDay        = address;
              address = FramLoad(programSize,  programs,           NULL); if (address < 0) return -1; iPrograms   = address; //3 x 4 x 2
    def1 = 2; address = FramLoad(1,           &programNewDayHour, &def1); if (address < 0) return -1; iNewDayHour = address;
    return 0;
}

/*
There are three programs available [0|1|2]; any of which can be allocated to a given day [0-6].
Each program contains four transitions with an index [0|1|2|3].
A transition is defined to be a short (16 bit) and consists of:
+---------+--------+--------+---------+
| 15 - 13 |   12   |   11   | 10 - 00 |
+---------+--------+--------+---------+
|         | in use | switch | minute  |
|         | yes/no | on/off | in day  |
|         |   1/0  |  1/0   | 0-1439  |
+---------+--------+--------+---------+
*/
static int16_t encodeTransition(bool inuse, bool onoff, int minutes)
{
    int16_t    transition  = minutes;
               transition &= 0x07FF;
    if (onoff) transition |= 0x0800;
    if (inuse) transition |= 0x1000;
    return transition;
}
static void decodeTransition(int16_t transition, bool* pinuse, bool* ponoff, int* pminutes)
{
    *pinuse   = transition & 0x1000;
    *ponoff   = transition & 0x0800;
    *pminutes = transition & 0x07FF;
}

static int compareTransition (const void * a, const void * b) //-ve a goes before b; 0 same; +ve a goes after b
{
    bool  inUseA,   inUseB;
    bool     onA,      onB;
    int minutesA, minutesB;
    decodeTransition(*(int16_t*)a, &inUseA, &onA, &minutesA);
    decodeTransition(*(int16_t*)b, &inUseB, &onB, &minutesB);
    
    if (!inUseA && !inUseB) return  0;
    if (!inUseA)            return +1;
    if (!inUseB)            return -1;
    
    if (minutesA < programNewDayHour * 60) minutesA += 1440;
    if (minutesB < programNewDayHour * 60) minutesB += 1440;
    
    if (minutesA < minutesB) return -1;
    if (minutesA > minutesB) return +1;
    return 0;
}
static void sort(int16_t* pProgram)
{
    qsort (pProgram, 4, sizeof(int16_t), compareTransition);
}

//[+|-][00-23][00-59];
void ProgramToString(int program, int buflen, char* buffer)
{
    if (buflen < 25) return;
    char* p = buffer;
    for (int i = 0; i < TRANSITIONS_COUNT; i++)
    {
        int16_t transition = programs[program][i];
        bool  inuse;
        bool  on;
        int   minuteUnits;
        decodeTransition(transition, &inuse, &on, &minuteUnits);
        if (!inuse) continue;
        
        int minuteTens  = minuteUnits / 10; minuteUnits %= 10;
        int   hourUnits = minuteTens  /  6; minuteTens  %=  6;
        int   hourTens  =   hourUnits / 10;   hourUnits %= 10;
        
        if (p > buffer) *p++ = ' ';
        *p++ = on ? '+' : '-';
        *p++ = hourTens    + '0';
        *p++ = hourUnits   + '0';
        *p++ = minuteTens  + '0';
        *p++ = minuteUnits + '0';
    }
    *p = 0;
}
void ProgramSendAjax()
{
    for (int program = 0; program < PROGRAMS_COUNT; program++)
    {
        for (int transition = 0; transition < TRANSITIONS_COUNT; transition++)
        {
            bool  inuse;
            bool  on;
            int   minuteUnits;
            decodeTransition(programs[program][transition], &inuse, &on, &minuteUnits);
            if (!inuse) continue;
            
            int minuteTens  = minuteUnits / 10; minuteUnits %= 10;
            int   hourUnits = minuteTens  /  6; minuteTens  %=  6;
            int   hourTens  =   hourUnits / 10;   hourUnits %= 10;
            
            if (transition) HttpAddChar(' ');
            HttpAddChar(on ? '+' : '-');
            HttpAddChar(hourTens    + '0');
            HttpAddChar(hourUnits   + '0');
            HttpAddChar(minuteTens  + '0');
            HttpAddChar(minuteUnits + '0');
        }
        HttpAddChar('\n');
    }
}
static void handleParseDelim(int program, int* pIndex, bool* pInUse, bool* pOn, int* pHourTens,  int* pHourUnits, int* pMinuteTens, int* pMinuteUnits)
{
    int hour = *pHourTens * 10 + *pHourUnits;
    if (hour   <  0) *pInUse = false;
    if (hour   > 23) *pInUse = false;
    
    int minute = *pMinuteTens * 10 + *pMinuteUnits;
    if (minute <  0) *pInUse = false;
    if (minute > 59) *pInUse = false;
    
    int minutes = hour * 60 + minute;
    
    int16_t transition = encodeTransition(*pInUse, *pOn, minutes);
    programs[program][*pIndex] = transition;
    
    *pIndex      += 1;
    *pInUse       = 0;
    *pOn          = 0;
    *pHourTens    = 0;
    *pHourUnits   = 0;
    *pMinuteTens  = 0;
    *pMinuteUnits = 0;

}
void ProgramParse(int program, char* p)
{
    int            i = 0;
    bool       inUse = 0;  bool         on = 0;
    int    hourUnits = 0;  int    hourTens = 0;
    int  minuteUnits = 0;  int  minuteTens = 0;
    while (*p && i < TRANSITIONS_COUNT) 
    {
        if      (*p == '+')              { on = true ; }
        else if (*p == '-')              { on = false; }
        else if (*p >= '0' && *p <= '9') { inUse = true; hourTens = hourUnits; hourUnits = minuteTens; minuteTens = minuteUnits; minuteUnits = *p - '0'; }
        else if (*p == ' ')              { handleParseDelim(program, &i, &inUse, &on, &hourTens, &hourUnits, &minuteTens, &minuteUnits); }
        p++;
    }
    while (i < TRANSITIONS_COUNT) handleParseDelim(program, &i, &inUse, &on, &hourTens, &hourUnits, &minuteTens, &minuteUnits);
    sort(&programs[program][0]);
    FramWrite(iPrograms + program * 8, 8, &programs[program]);
}

static bool readProgramTimerOutput()
{   

    if (!ClkTimeIsSet()) return 0;

    struct tm tm;
    ClkNowTmLocal(&tm);
    
    int dayOfWeek = tm.tm_wday;
    int minutesNow = tm.tm_hour * 60 + tm.tm_min;
    if (tm.tm_hour < programNewDayHour) //Before 2am should be matched against yesterday's program.
    {
        dayOfWeek--;
        if (dayOfWeek < 0) dayOfWeek = 6;
    }
    
    int program = programDay[dayOfWeek];
    
    bool calling = 0;
    for (int i = 0; i < TRANSITIONS_COUNT; i++)
    {
        int16_t transition = programs[program][i];
        bool inuse;
        bool on;
        int  minutes;
        decodeTransition(transition, &inuse, &on, &minutes);
        if (!inuse) continue;
        if (minutes <= minutesNow) calling = on;
    }
    
    return calling;
}

bool ProgramTimerOutput;

int ProgramMain()
{    
    ProgramTimerOutput = readProgramTimerOutput();
    return 0;
}