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@95:97621bfbedfa, 2020-10-02 (annotated)
- Committer:
- andrewboyson
- Date:
- Fri Oct 02 15:40:53 2020 +0000
- Revision:
- 95:97621bfbedfa
- Parent:
- 48:6eac12df3ad5
Added 11pm override cancel for the radiators as we kept forgetting to remove the override if used in the summer. Space in the Fram memory for the minute in the day to cancel was found. Still to do the web UI.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewboyson | 0:3c04f4b47041 | 1 | #include <stdint.h> |
andrewboyson | 0:3c04f4b47041 | 2 | #include <stdlib.h> |
andrewboyson | 0:3c04f4b47041 | 3 | #include <stdbool.h> |
andrewboyson | 0:3c04f4b47041 | 4 | |
andrewboyson | 95:97621bfbedfa | 5 | #include "rtc.h" |
andrewboyson | 20:904a4f043f2c | 6 | #include "clktime.h" |
andrewboyson | 95:97621bfbedfa | 7 | #include "clk.h" |
andrewboyson | 95:97621bfbedfa | 8 | #include "log.h" |
andrewboyson | 20:904a4f043f2c | 9 | #include "fram.h" |
andrewboyson | 48:6eac12df3ad5 | 10 | #include "http.h" |
andrewboyson | 0:3c04f4b47041 | 11 | |
andrewboyson | 48:6eac12df3ad5 | 12 | #define PROGRAMS_COUNT 3 |
andrewboyson | 47:229338b3adcb | 13 | #define TRANSITIONS_COUNT 4 |
andrewboyson | 47:229338b3adcb | 14 | static int16_t programs[PROGRAMS_COUNT][TRANSITIONS_COUNT]; static int iPrograms; |
andrewboyson | 47:229338b3adcb | 15 | static char programDay[7]; static int iDay; |
andrewboyson | 47:229338b3adcb | 16 | static char programNewDayHour; static int iNewDayHour; |
andrewboyson | 0:3c04f4b47041 | 17 | |
andrewboyson | 0:3c04f4b47041 | 18 | int ProgramGetDay (int i) { return (int) programDay[i]; } |
andrewboyson | 0:3c04f4b47041 | 19 | int ProgramGetNewDayHour() { return (int) programNewDayHour;} |
andrewboyson | 0:3c04f4b47041 | 20 | |
andrewboyson | 0:3c04f4b47041 | 21 | void ProgramSetDay (int i, int value) { programDay [i] = (char)value; FramWrite(iDay + i, 1, &programDay [i]); } |
andrewboyson | 0:3c04f4b47041 | 22 | void ProgramSetNewDayHour( int value) { programNewDayHour = (char)value; FramWrite(iNewDayHour, 1, &programNewDayHour ); } |
andrewboyson | 0:3c04f4b47041 | 23 | |
andrewboyson | 0:3c04f4b47041 | 24 | int ProgramInit() |
andrewboyson | 0:3c04f4b47041 | 25 | { |
andrewboyson | 0:3c04f4b47041 | 26 | char def1; |
andrewboyson | 0:3c04f4b47041 | 27 | int address; |
andrewboyson | 47:229338b3adcb | 28 | int programSize = PROGRAMS_COUNT * TRANSITIONS_COUNT * sizeof(int16_t); |
andrewboyson | 0:3c04f4b47041 | 29 | address = FramLoad(7, programDay, NULL); if (address < 0) return -1; iDay = address; |
andrewboyson | 0:3c04f4b47041 | 30 | address = FramLoad(programSize, programs, NULL); if (address < 0) return -1; iPrograms = address; //3 x 4 x 2 |
andrewboyson | 0:3c04f4b47041 | 31 | def1 = 2; address = FramLoad(1, &programNewDayHour, &def1); if (address < 0) return -1; iNewDayHour = address; |
andrewboyson | 0:3c04f4b47041 | 32 | return 0; |
andrewboyson | 0:3c04f4b47041 | 33 | } |
andrewboyson | 0:3c04f4b47041 | 34 | |
andrewboyson | 0:3c04f4b47041 | 35 | /* |
andrewboyson | 0:3c04f4b47041 | 36 | There are three programs available [0|1|2]; any of which can be allocated to a given day [0-6]. |
andrewboyson | 0:3c04f4b47041 | 37 | Each program contains four transitions with an index [0|1|2|3]. |
andrewboyson | 0:3c04f4b47041 | 38 | A transition is defined to be a short (16 bit) and consists of: |
andrewboyson | 0:3c04f4b47041 | 39 | +---------+--------+--------+---------+ |
andrewboyson | 0:3c04f4b47041 | 40 | | 15 - 13 | 12 | 11 | 10 - 00 | |
andrewboyson | 0:3c04f4b47041 | 41 | +---------+--------+--------+---------+ |
andrewboyson | 0:3c04f4b47041 | 42 | | | in use | switch | minute | |
andrewboyson | 0:3c04f4b47041 | 43 | | | yes/no | on/off | in day | |
andrewboyson | 0:3c04f4b47041 | 44 | | | 1/0 | 1/0 | 0-1439 | |
andrewboyson | 0:3c04f4b47041 | 45 | +---------+--------+--------+---------+ |
andrewboyson | 0:3c04f4b47041 | 46 | */ |
andrewboyson | 0:3c04f4b47041 | 47 | static int16_t encodeTransition(bool inuse, bool onoff, int minutes) |
andrewboyson | 0:3c04f4b47041 | 48 | { |
andrewboyson | 0:3c04f4b47041 | 49 | int16_t transition = minutes; |
andrewboyson | 0:3c04f4b47041 | 50 | transition &= 0x07FF; |
andrewboyson | 0:3c04f4b47041 | 51 | if (onoff) transition |= 0x0800; |
andrewboyson | 0:3c04f4b47041 | 52 | if (inuse) transition |= 0x1000; |
andrewboyson | 0:3c04f4b47041 | 53 | return transition; |
andrewboyson | 0:3c04f4b47041 | 54 | } |
andrewboyson | 0:3c04f4b47041 | 55 | static void decodeTransition(int16_t transition, bool* pinuse, bool* ponoff, int* pminutes) |
andrewboyson | 0:3c04f4b47041 | 56 | { |
andrewboyson | 0:3c04f4b47041 | 57 | *pinuse = transition & 0x1000; |
andrewboyson | 0:3c04f4b47041 | 58 | *ponoff = transition & 0x0800; |
andrewboyson | 0:3c04f4b47041 | 59 | *pminutes = transition & 0x07FF; |
andrewboyson | 0:3c04f4b47041 | 60 | } |
andrewboyson | 0:3c04f4b47041 | 61 | |
andrewboyson | 0:3c04f4b47041 | 62 | static int compareTransition (const void * a, const void * b) //-ve a goes before b; 0 same; +ve a goes after b |
andrewboyson | 0:3c04f4b47041 | 63 | { |
andrewboyson | 0:3c04f4b47041 | 64 | bool inUseA, inUseB; |
andrewboyson | 0:3c04f4b47041 | 65 | bool onA, onB; |
andrewboyson | 0:3c04f4b47041 | 66 | int minutesA, minutesB; |
andrewboyson | 0:3c04f4b47041 | 67 | decodeTransition(*(int16_t*)a, &inUseA, &onA, &minutesA); |
andrewboyson | 0:3c04f4b47041 | 68 | decodeTransition(*(int16_t*)b, &inUseB, &onB, &minutesB); |
andrewboyson | 0:3c04f4b47041 | 69 | |
andrewboyson | 0:3c04f4b47041 | 70 | if (!inUseA && !inUseB) return 0; |
andrewboyson | 0:3c04f4b47041 | 71 | if (!inUseA) return +1; |
andrewboyson | 0:3c04f4b47041 | 72 | if (!inUseB) return -1; |
andrewboyson | 0:3c04f4b47041 | 73 | |
andrewboyson | 0:3c04f4b47041 | 74 | if (minutesA < programNewDayHour * 60) minutesA += 1440; |
andrewboyson | 0:3c04f4b47041 | 75 | if (minutesB < programNewDayHour * 60) minutesB += 1440; |
andrewboyson | 0:3c04f4b47041 | 76 | |
andrewboyson | 0:3c04f4b47041 | 77 | if (minutesA < minutesB) return -1; |
andrewboyson | 0:3c04f4b47041 | 78 | if (minutesA > minutesB) return +1; |
andrewboyson | 0:3c04f4b47041 | 79 | return 0; |
andrewboyson | 0:3c04f4b47041 | 80 | } |
andrewboyson | 0:3c04f4b47041 | 81 | static void sort(int16_t* pProgram) |
andrewboyson | 0:3c04f4b47041 | 82 | { |
andrewboyson | 0:3c04f4b47041 | 83 | qsort (pProgram, 4, sizeof(int16_t), compareTransition); |
andrewboyson | 0:3c04f4b47041 | 84 | } |
andrewboyson | 0:3c04f4b47041 | 85 | |
andrewboyson | 0:3c04f4b47041 | 86 | //[+|-][00-23][00-59]; |
andrewboyson | 0:3c04f4b47041 | 87 | void ProgramToString(int program, int buflen, char* buffer) |
andrewboyson | 0:3c04f4b47041 | 88 | { |
andrewboyson | 0:3c04f4b47041 | 89 | if (buflen < 25) return; |
andrewboyson | 0:3c04f4b47041 | 90 | char* p = buffer; |
andrewboyson | 47:229338b3adcb | 91 | for (int i = 0; i < TRANSITIONS_COUNT; i++) |
andrewboyson | 0:3c04f4b47041 | 92 | { |
andrewboyson | 0:3c04f4b47041 | 93 | int16_t transition = programs[program][i]; |
andrewboyson | 0:3c04f4b47041 | 94 | bool inuse; |
andrewboyson | 0:3c04f4b47041 | 95 | bool on; |
andrewboyson | 0:3c04f4b47041 | 96 | int minuteUnits; |
andrewboyson | 0:3c04f4b47041 | 97 | decodeTransition(transition, &inuse, &on, &minuteUnits); |
andrewboyson | 0:3c04f4b47041 | 98 | if (!inuse) continue; |
andrewboyson | 0:3c04f4b47041 | 99 | |
andrewboyson | 0:3c04f4b47041 | 100 | int minuteTens = minuteUnits / 10; minuteUnits %= 10; |
andrewboyson | 0:3c04f4b47041 | 101 | int hourUnits = minuteTens / 6; minuteTens %= 6; |
andrewboyson | 0:3c04f4b47041 | 102 | int hourTens = hourUnits / 10; hourUnits %= 10; |
andrewboyson | 0:3c04f4b47041 | 103 | |
andrewboyson | 0:3c04f4b47041 | 104 | if (p > buffer) *p++ = ' '; |
andrewboyson | 0:3c04f4b47041 | 105 | *p++ = on ? '+' : '-'; |
andrewboyson | 0:3c04f4b47041 | 106 | *p++ = hourTens + '0'; |
andrewboyson | 0:3c04f4b47041 | 107 | *p++ = hourUnits + '0'; |
andrewboyson | 0:3c04f4b47041 | 108 | *p++ = minuteTens + '0'; |
andrewboyson | 0:3c04f4b47041 | 109 | *p++ = minuteUnits + '0'; |
andrewboyson | 0:3c04f4b47041 | 110 | } |
andrewboyson | 0:3c04f4b47041 | 111 | *p = 0; |
andrewboyson | 0:3c04f4b47041 | 112 | } |
andrewboyson | 48:6eac12df3ad5 | 113 | void ProgramSendAjax() |
andrewboyson | 48:6eac12df3ad5 | 114 | { |
andrewboyson | 48:6eac12df3ad5 | 115 | for (int program = 0; program < PROGRAMS_COUNT; program++) |
andrewboyson | 48:6eac12df3ad5 | 116 | { |
andrewboyson | 48:6eac12df3ad5 | 117 | for (int transition = 0; transition < TRANSITIONS_COUNT; transition++) |
andrewboyson | 48:6eac12df3ad5 | 118 | { |
andrewboyson | 48:6eac12df3ad5 | 119 | bool inuse; |
andrewboyson | 48:6eac12df3ad5 | 120 | bool on; |
andrewboyson | 48:6eac12df3ad5 | 121 | int minuteUnits; |
andrewboyson | 48:6eac12df3ad5 | 122 | decodeTransition(programs[program][transition], &inuse, &on, &minuteUnits); |
andrewboyson | 48:6eac12df3ad5 | 123 | if (!inuse) continue; |
andrewboyson | 48:6eac12df3ad5 | 124 | |
andrewboyson | 48:6eac12df3ad5 | 125 | int minuteTens = minuteUnits / 10; minuteUnits %= 10; |
andrewboyson | 48:6eac12df3ad5 | 126 | int hourUnits = minuteTens / 6; minuteTens %= 6; |
andrewboyson | 48:6eac12df3ad5 | 127 | int hourTens = hourUnits / 10; hourUnits %= 10; |
andrewboyson | 48:6eac12df3ad5 | 128 | |
andrewboyson | 48:6eac12df3ad5 | 129 | if (transition) HttpAddChar(' '); |
andrewboyson | 48:6eac12df3ad5 | 130 | HttpAddChar(on ? '+' : '-'); |
andrewboyson | 48:6eac12df3ad5 | 131 | HttpAddChar(hourTens + '0'); |
andrewboyson | 48:6eac12df3ad5 | 132 | HttpAddChar(hourUnits + '0'); |
andrewboyson | 48:6eac12df3ad5 | 133 | HttpAddChar(minuteTens + '0'); |
andrewboyson | 48:6eac12df3ad5 | 134 | HttpAddChar(minuteUnits + '0'); |
andrewboyson | 48:6eac12df3ad5 | 135 | } |
andrewboyson | 48:6eac12df3ad5 | 136 | HttpAddChar('\n'); |
andrewboyson | 48:6eac12df3ad5 | 137 | } |
andrewboyson | 48:6eac12df3ad5 | 138 | } |
andrewboyson | 0:3c04f4b47041 | 139 | static void handleParseDelim(int program, int* pIndex, bool* pInUse, bool* pOn, int* pHourTens, int* pHourUnits, int* pMinuteTens, int* pMinuteUnits) |
andrewboyson | 0:3c04f4b47041 | 140 | { |
andrewboyson | 0:3c04f4b47041 | 141 | int hour = *pHourTens * 10 + *pHourUnits; |
andrewboyson | 0:3c04f4b47041 | 142 | if (hour < 0) *pInUse = false; |
andrewboyson | 0:3c04f4b47041 | 143 | if (hour > 23) *pInUse = false; |
andrewboyson | 0:3c04f4b47041 | 144 | |
andrewboyson | 0:3c04f4b47041 | 145 | int minute = *pMinuteTens * 10 + *pMinuteUnits; |
andrewboyson | 0:3c04f4b47041 | 146 | if (minute < 0) *pInUse = false; |
andrewboyson | 0:3c04f4b47041 | 147 | if (minute > 59) *pInUse = false; |
andrewboyson | 0:3c04f4b47041 | 148 | |
andrewboyson | 0:3c04f4b47041 | 149 | int minutes = hour * 60 + minute; |
andrewboyson | 0:3c04f4b47041 | 150 | |
andrewboyson | 0:3c04f4b47041 | 151 | int16_t transition = encodeTransition(*pInUse, *pOn, minutes); |
andrewboyson | 0:3c04f4b47041 | 152 | programs[program][*pIndex] = transition; |
andrewboyson | 0:3c04f4b47041 | 153 | |
andrewboyson | 0:3c04f4b47041 | 154 | *pIndex += 1; |
andrewboyson | 0:3c04f4b47041 | 155 | *pInUse = 0; |
andrewboyson | 0:3c04f4b47041 | 156 | *pOn = 0; |
andrewboyson | 0:3c04f4b47041 | 157 | *pHourTens = 0; |
andrewboyson | 0:3c04f4b47041 | 158 | *pHourUnits = 0; |
andrewboyson | 0:3c04f4b47041 | 159 | *pMinuteTens = 0; |
andrewboyson | 0:3c04f4b47041 | 160 | *pMinuteUnits = 0; |
andrewboyson | 0:3c04f4b47041 | 161 | |
andrewboyson | 0:3c04f4b47041 | 162 | } |
andrewboyson | 0:3c04f4b47041 | 163 | void ProgramParse(int program, char* p) |
andrewboyson | 0:3c04f4b47041 | 164 | { |
andrewboyson | 0:3c04f4b47041 | 165 | int i = 0; |
andrewboyson | 0:3c04f4b47041 | 166 | bool inUse = 0; bool on = 0; |
andrewboyson | 0:3c04f4b47041 | 167 | int hourUnits = 0; int hourTens = 0; |
andrewboyson | 0:3c04f4b47041 | 168 | int minuteUnits = 0; int minuteTens = 0; |
andrewboyson | 47:229338b3adcb | 169 | while (*p && i < TRANSITIONS_COUNT) |
andrewboyson | 0:3c04f4b47041 | 170 | { |
andrewboyson | 0:3c04f4b47041 | 171 | if (*p == '+') { on = true ; } |
andrewboyson | 0:3c04f4b47041 | 172 | else if (*p == '-') { on = false; } |
andrewboyson | 0:3c04f4b47041 | 173 | else if (*p >= '0' && *p <= '9') { inUse = true; hourTens = hourUnits; hourUnits = minuteTens; minuteTens = minuteUnits; minuteUnits = *p - '0'; } |
andrewboyson | 0:3c04f4b47041 | 174 | else if (*p == ' ') { handleParseDelim(program, &i, &inUse, &on, &hourTens, &hourUnits, &minuteTens, &minuteUnits); } |
andrewboyson | 0:3c04f4b47041 | 175 | p++; |
andrewboyson | 0:3c04f4b47041 | 176 | } |
andrewboyson | 47:229338b3adcb | 177 | while (i < TRANSITIONS_COUNT) handleParseDelim(program, &i, &inUse, &on, &hourTens, &hourUnits, &minuteTens, &minuteUnits); |
andrewboyson | 0:3c04f4b47041 | 178 | sort(&programs[program][0]); |
andrewboyson | 0:3c04f4b47041 | 179 | FramWrite(iPrograms + program * 8, 8, &programs[program]); |
andrewboyson | 0:3c04f4b47041 | 180 | } |
andrewboyson | 0:3c04f4b47041 | 181 | |
andrewboyson | 0:3c04f4b47041 | 182 | static bool readProgramTimerOutput() |
andrewboyson | 0:3c04f4b47041 | 183 | { |
andrewboyson | 0:3c04f4b47041 | 184 | |
andrewboyson | 20:904a4f043f2c | 185 | if (!ClkTimeIsSet()) return 0; |
andrewboyson | 0:3c04f4b47041 | 186 | |
andrewboyson | 0:3c04f4b47041 | 187 | struct tm tm; |
andrewboyson | 20:904a4f043f2c | 188 | ClkNowTmLocal(&tm); |
andrewboyson | 0:3c04f4b47041 | 189 | |
andrewboyson | 0:3c04f4b47041 | 190 | int dayOfWeek = tm.tm_wday; |
andrewboyson | 0:3c04f4b47041 | 191 | int minutesNow = tm.tm_hour * 60 + tm.tm_min; |
andrewboyson | 0:3c04f4b47041 | 192 | if (tm.tm_hour < programNewDayHour) //Before 2am should be matched against yesterday's program. |
andrewboyson | 0:3c04f4b47041 | 193 | { |
andrewboyson | 0:3c04f4b47041 | 194 | dayOfWeek--; |
andrewboyson | 0:3c04f4b47041 | 195 | if (dayOfWeek < 0) dayOfWeek = 6; |
andrewboyson | 0:3c04f4b47041 | 196 | } |
andrewboyson | 0:3c04f4b47041 | 197 | |
andrewboyson | 0:3c04f4b47041 | 198 | int program = programDay[dayOfWeek]; |
andrewboyson | 0:3c04f4b47041 | 199 | |
andrewboyson | 0:3c04f4b47041 | 200 | bool calling = 0; |
andrewboyson | 47:229338b3adcb | 201 | for (int i = 0; i < TRANSITIONS_COUNT; i++) |
andrewboyson | 0:3c04f4b47041 | 202 | { |
andrewboyson | 0:3c04f4b47041 | 203 | int16_t transition = programs[program][i]; |
andrewboyson | 0:3c04f4b47041 | 204 | bool inuse; |
andrewboyson | 0:3c04f4b47041 | 205 | bool on; |
andrewboyson | 0:3c04f4b47041 | 206 | int minutes; |
andrewboyson | 0:3c04f4b47041 | 207 | decodeTransition(transition, &inuse, &on, &minutes); |
andrewboyson | 0:3c04f4b47041 | 208 | if (!inuse) continue; |
andrewboyson | 0:3c04f4b47041 | 209 | if (minutes <= minutesNow) calling = on; |
andrewboyson | 0:3c04f4b47041 | 210 | } |
andrewboyson | 0:3c04f4b47041 | 211 | |
andrewboyson | 0:3c04f4b47041 | 212 | return calling; |
andrewboyson | 0:3c04f4b47041 | 213 | } |
andrewboyson | 0:3c04f4b47041 | 214 | |
andrewboyson | 0:3c04f4b47041 | 215 | bool ProgramTimerOutput; |
andrewboyson | 0:3c04f4b47041 | 216 | |
andrewboyson | 0:3c04f4b47041 | 217 | int ProgramMain() |
andrewboyson | 0:3c04f4b47041 | 218 | { |
andrewboyson | 0:3c04f4b47041 | 219 | ProgramTimerOutput = readProgramTimerOutput(); |
andrewboyson | 0:3c04f4b47041 | 220 | return 0; |
andrewboyson | 0:3c04f4b47041 | 221 | } |