diff --git a/Arduino/BTCDieselHeater/src/OLED/ScreenManager.cpp b/Arduino/BTCDieselHeater/src/OLED/ScreenManager.cpp index e9a5339..4232f14 100644 --- a/Arduino/BTCDieselHeater/src/OLED/ScreenManager.cpp +++ b/Arduino/BTCDieselHeater/src/OLED/ScreenManager.cpp @@ -10,6 +10,7 @@ #include "RebootScreen.h" #include "HeaterSettingsScreen.h" #include "SettingsScreen.h" +#include "TimerChartScreen.h" #include #include "../cfg/pins.h" #include "../cfg/BTCConfig.h" @@ -161,8 +162,21 @@ CScreenManager::begin(bool bNoClock) _Screens.push_back(new CWiFiScreen(*_pDisplay, *this)); // comms info _Screens.push_back(new CSettingsScreen(*_pDisplay, *this)); // tuning info _SetTimeScreen = new CSetClockScreen(*_pDisplay, *this); // clock set + _TimerScreens.push_back(new CTimerChartScreen(*_pDisplay, *this, 0)); // timer chart _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 0)); // set timer 1 _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 1)); // set timer 2 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 2)); // set timer 3 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 3)); // set timer 4 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 4)); // set timer 5 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 5)); // set timer 6 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 6)); // set timer 7 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 7)); // set timer 8 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 8)); // set timer 9 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 9)); // set timer 10 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 10)); // set timer 11 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 11)); // set timer 12 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 12)); // set timer 13 + _TimerScreens.push_back(new CSetTimerScreen(*_pDisplay, *this, 13)); // set timer 14 _SettingsScreens.push_back(new CFuelMixtureScreen(*_pDisplay, *this)); // tuning _SettingsScreens.push_back(new CHeaterSettingsScreen(*_pDisplay, *this)); // tuning diff --git a/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.cpp b/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.cpp index 4f50310..ff01a58 100644 --- a/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.cpp +++ b/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.cpp @@ -33,6 +33,7 @@ #include "../Protocol/helpers.h" #include "../Utility/NVStorage.h" #include +#include "../RTC/TimerManager.h" const char* briefDOW[] = { "S", "M", "T", "W", "T", "F", "S" }; @@ -41,13 +42,15 @@ CSetTimerScreen::CSetTimerScreen(C128x64_OLED& display, CScreenManager& mgr, int _rowSel = 0; _colSel = 0; _SaveTime = 0; - _instance = instance; + _ConflictTime = 0; + _conflictID = 0; + _timerID = instance; } void CSetTimerScreen::onSelect() { - NVstore.getTimerInfo(_instance, _timer); + NVstore.getTimerInfo(_timerID, _timerInfo); } bool @@ -55,13 +58,13 @@ CSetTimerScreen::show() { CScreenHeader::show(); - char str[16]; + char str[20]; int xPos, yPos; if(_rowSel == 0) { - NVstore.getTimerInfo(_instance, _timer); + NVstore.getTimerInfo(_timerID, _timerInfo); } - sprintf(str, " Set Timer %d ", _instance + 1); + sprintf(str, " Set Timer %d ", _timerID + 1); _printInverted(0, 16, str, true); if(_SaveTime) { @@ -72,6 +75,25 @@ CSetTimerScreen::show() _printInverted(_display.xCentre(), 39, " ", true, eCentreJustify); _printInverted(_display.xCentre(), 34, " STORING ", true, eCentreJustify); } + else if(_ConflictTime) { + long tDelta = millis() - _ConflictTime; + if(tDelta > 0) + _ConflictTime = 0; + sprintf(str, " with Timer %d ", _conflictID); + if(_conflictID >= 10) { + // extra space + _printInverted(_display.xCentre(), 26, " ", true, eCentreJustify); + _printInverted(_display.xCentre(), 45, " ", true, eCentreJustify); + _printInverted(_display.xCentre(), 30, " Conflicts ", true, eCentreJustify); + _printInverted(_display.xCentre(), 38, str, true, eCentreJustify); + } + else { + _printInverted(_display.xCentre(), 26, " ", true, eCentreJustify); + _printInverted(_display.xCentre(), 45, " ", true, eCentreJustify); + _printInverted(_display.xCentre(), 30, " Conflicts ", true, eCentreJustify); + _printInverted(_display.xCentre(), 38, str, true, eCentreJustify); + } + } else { // start xPos = 18; @@ -79,10 +101,10 @@ CSetTimerScreen::show() _printMenuText(xPos, yPos, "On", false, eRightJustify); _printMenuText(xPos+18, yPos, ":"); xPos += 6; - sprintf(str, "%02d", _timer.start.hour); + sprintf(str, "%02d", _timerInfo.start.hour); _printMenuText(xPos, yPos, str, _rowSel==1 && _colSel==0); xPos += 17; - sprintf(str, "%02d", _timer.start.min); + sprintf(str, "%02d", _timerInfo.start.min); _printMenuText(xPos, yPos, str, _rowSel==1 && _colSel==1); // stop @@ -91,10 +113,10 @@ CSetTimerScreen::show() _printMenuText(xPos, yPos, "Off", false, eRightJustify); _printMenuText(xPos+18, yPos, ":"); xPos += 6; - sprintf(str, "%02d", _timer.stop.hour); + sprintf(str, "%02d", _timerInfo.stop.hour); _printMenuText(xPos, yPos, str, _rowSel==1 && _colSel==2); xPos += 17; - sprintf(str, "%02d", _timer.stop.min); + sprintf(str, "%02d", _timerInfo.stop.min); _printMenuText(xPos, yPos, str, _rowSel==1 && _colSel==3); // control @@ -103,14 +125,14 @@ CSetTimerScreen::show() _printEnabledTimers(); yPos = 40; - if(_timer.repeat) + if(_timerInfo.repeat) msg = "Repeat"; else msg = "Once"; if(_rowSel == 1) _printMenuText(xPos, yPos, msg, _colSel==5, eRightJustify); else - _printInverted(xPos, yPos, msg, _timer.repeat, eRightJustify); + _printInverted(xPos, yPos, msg, _timerInfo.repeat, eRightJustify); } // navigation line yPos = 53; @@ -139,11 +161,21 @@ CSetTimerScreen::keyHandler(uint8_t event) _colSel = 4; } else { // in config fields, save new settings - _SaveTime = millis() + 1500; - NVstore.setTimerInfo(_instance, _timer); - NVstore.save(); + NVstore.setTimerInfo(_timerID, _timerInfo); + _conflictID = CTimerManager::conflictTest(_timerID); + if(_conflictID) { + _timerInfo.enabled = 0; // cancel enabled status + _ConflictTime = millis() + 1500; + _ScreenManager.reqUpdate(); + } + else { + _SaveTime = millis() + 1500; + _ScreenManager.reqUpdate(); + } _rowSel = 0; - _ScreenManager.reqUpdate(); + _colSel = 0; + NVstore.setTimerInfo(_timerID, _timerInfo); // may have got disabled + NVstore.save(); } } // press LEFT - navigate fields, or screens @@ -212,7 +244,7 @@ CSetTimerScreen::keyHandler(uint8_t event) } else if(_colSel == 4) { if(event & key_Right) { - _timer.enabled &= 0x7f; // strip next day flag + _timerInfo.enabled &= 0x7f; // strip next day flag _rowSel = 2; _colSel = 0; } @@ -248,8 +280,8 @@ CSetTimerScreen::keyHandler(uint8_t event) break;*/ case 2: // adjust selected item - _timer.enabled ^= maskDOW; - _timer.enabled &= 0x7f; + _timerInfo.enabled ^= maskDOW; + _timerInfo.enabled &= 0x7f; break; } } @@ -271,8 +303,8 @@ CSetTimerScreen::keyHandler(uint8_t event) break;*/ case 2: // adjust selected item - _timer.enabled ^= maskDOW; - _timer.enabled &= 0x7f; + _timerInfo.enabled ^= maskDOW; + _timerInfo.enabled &= 0x7f; break; } } @@ -292,37 +324,37 @@ CSetTimerScreen::adjust(int dir) switch(_colSel) { case 0: - _timer.start.hour += dir; - ROLLUPPERLIMIT(_timer.start.hour, 23, 0); - ROLLLOWERLIMIT(_timer.start.hour, 0, 23); + _timerInfo.start.hour += dir; + ROLLUPPERLIMIT(_timerInfo.start.hour, 23, 0); + ROLLLOWERLIMIT(_timerInfo.start.hour, 0, 23); break; case 1: - _timer.start.min += dir; - ROLLUPPERLIMIT(_timer.start.min, 59, 0); - ROLLLOWERLIMIT(_timer.start.min, 0, 59); + _timerInfo.start.min += dir; + ROLLUPPERLIMIT(_timerInfo.start.min, 59, 0); + ROLLLOWERLIMIT(_timerInfo.start.min, 0, 59); break; case 2: - _timer.stop.hour += dir; - ROLLUPPERLIMIT(_timer.stop.hour, 23, 0); - ROLLLOWERLIMIT(_timer.stop.hour, 0, 23); + _timerInfo.stop.hour += dir; + ROLLUPPERLIMIT(_timerInfo.stop.hour, 23, 0); + ROLLLOWERLIMIT(_timerInfo.stop.hour, 0, 23); break; case 3: - _timer.stop.min += dir; - ROLLUPPERLIMIT(_timer.stop.min, 59, 0); - ROLLLOWERLIMIT(_timer.stop.min, 0, 59); + _timerInfo.stop.min += dir; + ROLLUPPERLIMIT(_timerInfo.stop.min, 59, 0); + ROLLLOWERLIMIT(_timerInfo.stop.min, 0, 59); break; case 4: if(_rowSel == 1) { - _timer.enabled &= 0x80; // ensure specific day flags are cleared - _timer.enabled ^= 0x80; // toggle next day flag + _timerInfo.enabled &= 0x80; // ensure specific day flags are cleared + _timerInfo.enabled ^= 0x80; // toggle next day flag } if(_rowSel == 2) { - _timer.enabled &= 0x7f; // ensure next day flag is cleared - _timer.enabled ^= maskDOW; // toggle flag for day of week + _timerInfo.enabled &= 0x7f; // ensure next day flag is cleared + _timerInfo.enabled ^= maskDOW; // toggle flag for day of week } break; case 5: - _timer.repeat = !_timer.repeat; + _timerInfo.repeat = !_timerInfo.repeat; break; } } @@ -334,10 +366,10 @@ CSetTimerScreen::_printEnabledTimers() int xPos = _display.width() - border; int yPos = 28; - if(_timer.enabled == 0x00 && _rowSel != 2) { + if(_timerInfo.enabled == 0x00 && _rowSel != 2) { _printMenuText(xPos, yPos, "Disabled", _colSel==4, eRightJustify); } - else if(_timer.enabled & 0x80) { + else if(_timerInfo.enabled & 0x80) { if(_rowSel==1 && _colSel==4) _printMenuText(xPos, yPos, "Enabled", true, eRightJustify); else @@ -352,7 +384,7 @@ CSetTimerScreen::_printEnabledTimers() int xSel = xPos + _colSel * dayWidth; for(int i=0; i<7; i++) { int dayMask = 0x01 << i; - _printInverted(xPos, yPos, briefDOW[i], _timer.enabled & dayMask); + _printInverted(xPos, yPos, briefDOW[i], _timerInfo.enabled & dayMask); xPos += dayWidth; } if(_rowSel == 2) { diff --git a/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.h b/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.h index 2900472..cc70219 100644 --- a/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.h +++ b/Arduino/BTCDieselHeater/src/OLED/SetTimerScreen.h @@ -33,9 +33,11 @@ class CProtocol; class CSetTimerScreen : public CScreenHeader { int _rowSel; int _colSel; - int _instance; + int _timerID; unsigned long _SaveTime; - sTimer _timer; + unsigned long _ConflictTime; + int _conflictID; + sTimer _timerInfo; void adjust(int dir); void _printEnabledTimers(); diff --git a/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.cpp b/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.cpp new file mode 100644 index 0000000..e529f2d --- /dev/null +++ b/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.cpp @@ -0,0 +1,175 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/////////////////////////////////////////////////////////////////////////// +// +// CTimerChartScreen +// +// This screen shows the timers as a chart for the entire week +// +/////////////////////////////////////////////////////////////////////////// + +#include "TimerChartScreen.h" +#include "KeyPad.h" +#include "../Protocol/helpers.h" +#include "../Utility/NVStorage.h" +#include +#include "fonts/MiniFont.h" +#include "../RTC/TimerManager.h" + + +static uint16_t timerMap[24*60]; +static uint16_t timerIDs[24*60]; + +CTimerChartScreen::CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr, int instance) : CScreenHeader(display, mgr) +{ + _rowSel = 0; + _colSel = 0; + _SaveTime = 0; + _instance = instance; +} + +void +CTimerChartScreen::onSelect() +{ + CTimerManager::createMap(0x3fff, timerMap, timerIDs); + CTimerManager::condenseMap(timerMap, 12); +} + +bool +CTimerChartScreen::show() +{ + _display.clearDisplay(); + + CTransientFont AF(_display, &miniFontInfo); // temporarily use a mini font + + _printMenuText(0, 7, "S"); + _printMenuText(0, 14, "M"); + _printMenuText(0, 21, "T"); + _printMenuText(0, 28, "W"); + _printMenuText(0, 35, "T"); + _printMenuText(0, 42, "F"); + _printMenuText(0, 49, "S"); + + int hour0 = 8; + int linespacing = 7; + + for(int tick = 0; tick < 24; tick += 3) { + int xpos = tick * 5 + hour0; + _display.setCursor(xpos, 0); + _display.print(tick); + for(int dow = 0; dow < 7; dow++) { + int ypos = dow*linespacing + 8; + _display.drawFastVLine(xpos, ypos, 3, WHITE); // solid bar + } + } + + + for(int dow = 0; dow < 7; dow++) { + int day = 0x01 << dow; + int ypos = dow*linespacing + 7; // top of first line + int pixel = 0; + int subpixel = 0; + for(int interval = 0; interval < 120; interval++) { +// if(Chart[interval] & day) { + if(timerMap[interval] & day) { +// if(Chart[interval] & (day << 8)) { + if(timerMap[interval] & (day << 8)) { + // one shot timer - draw peppered + for(int yscan = interval & 1; yscan < 6; yscan+=2) + _display.drawPixel(interval+hour0, ypos+yscan, WHITE); // peppered vertical bar + } + else { + // repeating timer =- draw solid + _display.drawFastVLine(interval+hour0, ypos, 6, WHITE); // solid bar + } + } + else { + if(pixel == 0) // every 5th pixel draw a base line + _display.drawPixel(interval+hour0, ypos+2, subpixel ? WHITE : BLACK); // base line + } + pixel++; + if(pixel > 4) { + pixel = 0; + subpixel++; + ROLLUPPERLIMIT(subpixel, 2, 0); + } + } + } + + return true; +} + + +bool +CTimerChartScreen::keyHandler(uint8_t event) +{ + static bool bHeld = false; + // handle initial key press + if(event & keyPressed) { + bHeld = false; + // press CENTRE + if(event & key_Centre) { + _ScreenManager.selectTimerScreen(false); // exit: return to clock screen + } + // press LEFT - navigate fields, or screens + if(event & key_Left) { + _ScreenManager.prevScreen(); + } + // press RIGHT - navigate fields, or screens + if(event & key_Right) { + _ScreenManager.nextScreen(); + } + // press UP + if(event & key_Up) { + } + // press DOWN + if(event & key_Down) { + } + } + + // handle held down keys + if(event & keyRepeat) { + bHeld = true; + } + + if(event & keyReleased) { + if(!bHeld) { + if(event & key_Left) { + } + // released DOWN - can only leave adjustment by using OK (centre button) + if(event & key_Down) { + // adjust selected item + } + if(event & key_Right) { + } + // released UP + if(event & key_Up) { + } + } + } + + _ScreenManager.reqUpdate(); + return true; +} + + diff --git a/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.h b/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.h new file mode 100644 index 0000000..9080224 --- /dev/null +++ b/Arduino/BTCDieselHeater/src/OLED/TimerChartScreen.h @@ -0,0 +1,46 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __TIMERCHARTSCREEN_H__ +#define __TIMERCHARTSCREEN_H__ + +#include +#include "ScreenHeader.h" +#include "../Utility/NVStorage.h" + +class C128x64_OLED; +class CScreenManager; +class CProtocol; + +class CTimerChartScreen : public CScreenHeader { + int _rowSel; + int _colSel; + int _instance; + unsigned long _SaveTime; + +public: + CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr, int instance); + void onSelect(); + bool show(); + bool keyHandler(uint8_t event); +}; + +#endif \ No newline at end of file diff --git a/Arduino/BTCDieselHeater/src/OLED/fonts/MiniFont.c b/Arduino/BTCDieselHeater/src/OLED/fonts/MiniFont.c index 580dd4c..eb35ba9 100644 --- a/Arduino/BTCDieselHeater/src/OLED/fonts/MiniFont.c +++ b/Arduino/BTCDieselHeater/src/OLED/fonts/MiniFont.c @@ -155,6 +155,11 @@ const uint8_t miniFontBitmaps[] PROGMEM = 0xa8, // # # # 0x88, // # # + // @75 'W' (3 pixels wide) + 0xf8, // ##### + 0x40, // # + 0xf8, // ##### + }; // Character descriptors for a 3x5 font @@ -192,7 +197,7 @@ const FONT_CHAR_INFO miniFontDescriptors[] PROGMEM = {0, 0, 0}, // 'J' {0, 0, 0}, // 'K' {3, 5, 48}, // 'L' - {0, 0, 0}, // 'M' + {3, 5, 75}, // 'M' {0, 0, 0}, // 'N' {0, 0, 0}, // 'O' {3, 5, 51}, // 'P' diff --git a/Arduino/BTCDieselHeater/src/RTC/TimerManager.cpp b/Arduino/BTCDieselHeater/src/RTC/TimerManager.cpp new file mode 100644 index 0000000..e9b1d5f --- /dev/null +++ b/Arduino/BTCDieselHeater/src/RTC/TimerManager.cpp @@ -0,0 +1,138 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/////////////////////////////////////////////////////////////////////////// +// +// CTimerManager +// +// This provides management of the timers +// +/////////////////////////////////////////////////////////////////////////// + +#include +#include "TimerManager.h" +#include "../Utility/NVStorage.h" + +// create a bitmap that describes the pattern of on/off times +void +CTimerManager::createMap(int timerMask, uint16_t timerMap[24*60], uint16_t timerIDs[24*60]) +{ + int maxPoints = 24*60; + memset(timerMap, 0, 24*60*sizeof(uint16_t)); + memset(timerIDs, 0, 24*60*sizeof(uint16_t)); + + for(int timerID=0; timerID < 14; timerID++) { + // only process timer if it is nominated in timerMask (bitfield), timer0 = bit0 .. timerN = bitN + uint16_t timerBit = 0x0001 << timerID; + if(timerMask & timerBit) { + sTimer timer; + // get timer settings + NVstore.getTimerInfo(timerID, timer); + // and add info to map if enabled + if(timer.enabled) { + // create linear minute of day values for start & stop + // note that if stop < start, that is treated as a timer that rolls over midnight + int timestart = timer.start.hour * 60 + timer.start.min; // linear minute of day + int timestop = timer.stop.hour * 60 + timer.stop.min; + for(int dayMinute = 0; dayMinute < maxPoints; dayMinute++) { + for(int day = 0x01; day != 0x80; day <<= 1) { + if(timer.enabled & day || timer.enabled & 0x80) { // specific or everyday + uint16_t activeday = day; // may also hold non repeat flag later + if(!timer.repeat) { + // flag timers that should get cancelled + activeday |= (activeday << 8); // combine one shot status in MS byte + } + if(timestop > timestart) { + // treat normal start < stop times (within same day) + if((dayMinute >= timestart) && (dayMinute < timestop)) { + timerMap[dayMinute] |= activeday; + timerIDs[dayMinute] |= timerBit; + } + } + else { + // time straddles a day, start > stop, special treatment required + if(dayMinute >= timestart) { + // true from start until midnight + timerMap[dayMinute] |= activeday; + timerIDs[dayMinute] |= timerBit; + } + if(dayMinute < timestop) { + // after midnight, before stop time, i.e. next day + // adjust for next day, taking care to wrap week + if(day & 0x40) // last day of week? + activeday >>= 6; // roll back to start of week + else + activeday <<= 1; // next day + timerMap[dayMinute] |= activeday; + timerIDs[dayMinute] |= timerBit; + } + } + } + } + } + } + } + } +} + +void +CTimerManager::condenseMap(uint16_t timerMap[24*60], int factor) +{ + int maxPoints = 24*60; + + int opIndex = 0; + for(int dayMinute = 0; dayMinute < maxPoints; ) { + uint16_t condense = 0; + for(int subInterval = 0; subInterval < factor; subInterval++) { + condense |= timerMap[dayMinute++]; + if(dayMinute == maxPoints) { + break; + } + } + timerMap[opIndex++] = condense; + } +} + +uint16_t otherTimers[24*60]; +uint16_t selectedTimer[24*60]; +uint16_t timerIDs[24*60]; + +int +CTimerManager::conflictTest(int timerID) +{ + int selectedMask = 0x0001 << timerID; + + createMap(selectedMask, selectedTimer, timerIDs); // create a map for the nominated timer (under test) + createMap(0x3fff & ~selectedMask, otherTimers, timerIDs); // create a map for all other timers, and get their unique IDs + for(int i=0; i< 24*60; i++) { + if(otherTimers[i] & selectedTimer[i]) { // both have the same day bit set - CONFLICT! + uint16_t timerBit = timerIDs[i]; + int ID = 0; + while(timerBit) { + timerBit >>= 1; + ID++; + } + return ID; + } + } + return 0; // no conflicts :-) + } diff --git a/Arduino/BTCDieselHeater/src/RTC/TimerManager.h b/Arduino/BTCDieselHeater/src/RTC/TimerManager.h new file mode 100644 index 0000000..58e3f62 --- /dev/null +++ b/Arduino/BTCDieselHeater/src/RTC/TimerManager.h @@ -0,0 +1,43 @@ +/* + * This file is part of the "bluetoothheater" distribution + * (https://gitlab.com/mrjones.id.au/bluetoothheater) + * + * Copyright (C) 2018 Ray Jones + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/////////////////////////////////////////////////////////////////////////// +// +// CTimerManager +// +// This provides management of the timers +// +/////////////////////////////////////////////////////////////////////////// + +#ifndef __TIMERMANAGER_H__ +#define __TIMERMANAGER_H__ + +#include + +class CTimerManager { +public: + static void createMap(int timermask, uint16_t map[24*60], uint16_t timerIDs[24*60]); + static void condenseMap(uint16_t timerMap[24*60], int factor); + static int conflictTest(int timerID); +}; + +#endif //__TIMERMANAGER_H__ \ No newline at end of file diff --git a/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp b/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp index 3de7727..7aa09ca 100644 --- a/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp +++ b/Arduino/BTCDieselHeater/src/Utility/NVStorage.cpp @@ -178,7 +178,7 @@ CHeaterStorage::getGlowDrive() void CHeaterStorage::getTimerInfo(int idx, sTimer& timerInfo) { - if(idx >= 0 && idx <=1) { + if(idx >= 0 && idx < 14) { timerInfo = _calValues.timer[idx]; } } @@ -186,7 +186,7 @@ CHeaterStorage::getTimerInfo(int idx, sTimer& timerInfo) void CHeaterStorage::setTimerInfo(int idx, const sTimer& timerInfo) { - if(idx >= 0 && idx <=1) { + if(idx >= 0 && idx < 14) { _calValues.timer[idx] = timerInfo; } } @@ -239,7 +239,7 @@ CESP32HeaterStorage::load() { DebugPort.println("Reading from NV storage"); loadHeater(); - for(int i=0; i<2; i++) { + for(int i=0; i<14; i++) { loadTimer(i); } loadUI(); @@ -250,7 +250,7 @@ CESP32HeaterStorage::save() { DebugPort.println("Saving to NV storage"); saveHeater(); - for(int i=0; i<2; i++) { + for(int i=0; i<14; i++) { saveTimer(i); } saveUI(); @@ -302,7 +302,8 @@ CESP32HeaterStorage::loadTimer(int idx) validatedLoad("stopHour", timer.stop.hour, 0, s8inBounds, 0, 23); validatedLoad("stopMin", timer.stop.min, 0, s8inBounds, 0, 59); validatedLoad("enabled", timer.enabled, 0, u8inBounds, 0, 255); // all 8 bits used! - validatedLoad("repea*t", timer.repeat, 0, u8inBounds, 0, 1); + validatedLoad("repeat", timer.repeat, 0, u8inBounds, 0, 1); + validatedLoad("temperature", timer.temperature, 22, u8inBounds, 8, 35); preferences.end(); } @@ -319,6 +320,7 @@ CESP32HeaterStorage::saveTimer(int idx) preferences.putChar("stopMin", timer.stop.min); preferences.putUChar("enabled", timer.enabled); preferences.putUChar("repeat", timer.repeat); + preferences.putUChar("temperature", timer.temperature); preferences.end(); } diff --git a/Arduino/BTCDieselHeater/src/Utility/NVStorage.h b/Arduino/BTCDieselHeater/src/Utility/NVStorage.h index 1c4ce86..bca8bf6 100644 --- a/Arduino/BTCDieselHeater/src/Utility/NVStorage.h +++ b/Arduino/BTCDieselHeater/src/Utility/NVStorage.h @@ -77,15 +77,18 @@ struct sTimer { sHourMin stop; // stop time uint8_t enabled; // timer enabled - each bit is a day of week flag uint8_t repeat; // repeating timer + uint8_t temperature; sTimer() { enabled = 0; repeat = false; + temperature = 22; }; sTimer& operator=(const sTimer& rhs) { start = rhs.start; stop = rhs.stop; enabled = rhs.enabled; repeat = rhs.repeat; + temperature = rhs.temperature; }; void init() { start.hour = 0; @@ -94,6 +97,7 @@ struct sTimer { stop.min = 0; enabled = 0; repeat = 0; + temperature = 22; }; bool valid() { bool retval = true; @@ -102,6 +106,7 @@ struct sTimer { retval &= (stop.hour >= 0 && stop.hour < 24); retval &= (stop.min >= 0 && stop.min < 60); retval &= repeat <= 2; + retval &= (temperature >= 8 && temperature <= 35); return retval; }; }; @@ -111,7 +116,7 @@ struct sNVStore { sHeater Heater; long DimTime; uint8_t degF; - sTimer timer[2]; + sTimer timer[14]; bool valid(); void init(); };