diff --git a/Documentation/ScreenFlowV3.dia b/Documentation/ScreenFlowV3.dia new file mode 100644 index 0000000..7f42e60 Binary files /dev/null and b/Documentation/ScreenFlowV3.dia differ diff --git a/Documentation/ScreenFlowV3.png b/Documentation/ScreenFlowV3.png new file mode 100644 index 0000000..32d9824 Binary files /dev/null and b/Documentation/ScreenFlowV3.png differ diff --git a/icons/OutputConfigs.xml b/icons/OutputConfigs.xml index cf8fe5b..ee04435 100644 --- a/icons/OutputConfigs.xml +++ b/icons/OutputConfigs.xml @@ -132,4 +132,37 @@ const uint8_t {0}Height splash + + true + true + true + Cpp + # + RotateNinety + true + false + Fixed + Tighest + AtColumn + RowMajor + MsbFirst + Hex + 0x + true + DisplayInBits + DisplayInBits + DisplayInBits + false + 80 + DisplayInBits + DisplayInBits + true + 2 + const uint8_t PROGMEM {0}Bitmaps + const FONT_CHAR_INFO PROGMEM {0}Descriptors + const FONT_INFO {0}FontInfo + const uint8_t {0}Width + const uint8_t {0}Height + Fonts + \ No newline at end of file diff --git a/src/OLED/ScreenManager.cpp b/src/OLED/ScreenManager.cpp index 6324f34..b7fe972 100644 --- a/src/OLED/ScreenManager.cpp +++ b/src/OLED/ScreenManager.cpp @@ -269,7 +269,6 @@ CScreenManager::begin(bool bNoClock) if(!bNoClock) menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock menuloop.push_back(new CPrimingScreen(*_pDisplay, *this)); // mode / priming -// menuloop.push_back(new CWiFiScreen(*_pDisplay, *this)); // comms info menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this)); // GPIO info menuloop.push_back(new CMenuTrunkScreen(*_pDisplay, *this)); _Screens.push_back(menuloop); diff --git a/src/OLED/SetClockScreen.cpp b/src/OLED/SetClockScreen.cpp index e8dd336..fcf160c 100644 --- a/src/OLED/SetClockScreen.cpp +++ b/src/OLED/SetClockScreen.cpp @@ -35,7 +35,7 @@ #include "../Utility/macros.h" -CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreenHeader(display, mgr) +CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen(display, mgr) { _initUI(); } @@ -43,7 +43,7 @@ CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : C void CSetClockScreen::onSelect() { - CScreenHeader::onSelect(); + CScreen::onSelect(); _initUI(); } @@ -68,18 +68,18 @@ CSetClockScreen::show() if(deltaT >= 0) { _nextT += 1000; - CScreenHeader::show(false); + CScreen::show(); + _display.clearDisplay(); char str[16]; int xPos, yPos; - _printInverted(0, 15, " Set Clock ", true); + _showTitle("Set Clock"); const BTCDateTime& now = Clock.get(); if(_rowSel == 0) { // update printable values working = now; - // DELIBERATE DROP THROUGH HERE } if(_SaveTime) { @@ -90,12 +90,13 @@ CSetClockScreen::show() } } else { - yPos = 28; + yPos = 20; xPos = 6; // date if(_rowSel==0) { - xPos = 20; + xPos = 18; _printMenuText(xPos, yPos, working.dowStr()); + xPos = 20; } sprintf(str, "%d", working.day()); @@ -107,7 +108,7 @@ CSetClockScreen::show() sprintf(str, "%d", working.year()); _printMenuText(xPos, yPos, str, _rowSel==3); // time - yPos = 40; + yPos = 32; xPos = 26; sprintf(str, "%02d", working.hour()); _printMenuText(xPos, yPos, str, _rowSel==4); @@ -123,22 +124,23 @@ CSetClockScreen::show() _printMenuText(xPos, yPos, str, _rowSel==6); if(_rowSel>=1) _printMenuText(_display.width()-border, yPos, "SET", _rowSel==7, eRightJustify); - } - // navigation line - xPos = _display.xCentre(); - if(_rowSel == 0) { - yPos = 53; - _printMenuText(_display.width(), yPos, "\030Edit", false, eRightJustify); - _printMenuText(xPos, yPos, " Exit ", true, eCentreJustify); - } - else { - _display.drawFastHLine(0, 52, 128, WHITE); - _printMenuText(xPos, 56, "\033\032 Sel \030\031 Adj", false, eCentreJustify); - if(_rowSel == 7) { - _printMenuText(xPos, 56, "Save", false, eCentreJustify); + + // navigation line + xPos = _display.xCentre(); + if(_rowSel == 0) { + yPos = 53; + _printMenuText(_display.width(), yPos, "\030Edit", false, eRightJustify); + _printMenuText(xPos, yPos, " Exit ", true, eCentreJustify); } else { - _printMenuText(xPos, 56, "Abort", false, eCentreJustify); + _display.drawFastHLine(0, 52, 128, WHITE); + _printMenuText(xPos, 56, "\033\032 Sel \030\031 Adj", false, eCentreJustify); + if(_rowSel == 7) { + _printMenuText(xPos, 56, "Save", false, eCentreJustify); + } + else { + _printMenuText(xPos, 56, "Abort", false, eCentreJustify); + } } } } diff --git a/src/OLED/SetClockScreen.h b/src/OLED/SetClockScreen.h index 45dafdc..142eb2f 100644 --- a/src/OLED/SetClockScreen.h +++ b/src/OLED/SetClockScreen.h @@ -30,7 +30,7 @@ class C128x64_OLED; class CScreenManager; class CProtocol; -class CSetClockScreen : public CScreenHeader { +class CSetClockScreen : public CScreen { int _rowSel; unsigned long _nextT; BTCDateTime working; diff --git a/src/OLED/SetTimerScreen.cpp b/src/OLED/SetTimerScreen.cpp index 16c0548..b97600d 100644 --- a/src/OLED/SetTimerScreen.cpp +++ b/src/OLED/SetTimerScreen.cpp @@ -72,7 +72,7 @@ CSetTimerScreen::show() int xPos, yPos; if(_rowSel == 0) { - NVstore.getTimerInfo(_timerID, _timerInfo); + NVstore.getTimerInfo(_timerID, _timerInfo); // ensure actual data when on base menu bar } sprintf(str, "Set Timer #%d", _timerID + 1); _showTitle(str); @@ -156,34 +156,11 @@ CSetTimerScreen::keyHandler(uint8_t event) static bool bHeld = false; // handle initial key press if(event & keyPressed) { + _repeatCount = 0; bHeld = false; // press CENTRE if(event & key_Centre) { - if(_rowSel == 0) { - _ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // exit: return to clock screen - } - else if(_rowSel == 2) { // exit from per day settings - _rowSel = 1; - _colSel = 4; - } - else { // in config fields, save new settings - // test if the setting conflict with an already defined timer - _conflictID = CTimerManager::conflictTest(_timerInfo); - if(_conflictID) { - _timerInfo.enabled = 0; // cancel enabled status - _ConflictTime = millis() + 1500; - _ScreenManager.reqUpdate(); - _rowSel = 1; - _colSel = 4; // select enable/disable - } - else { - _SaveTime = millis() + 1500; - _ScreenManager.reqUpdate(); - _rowSel = 0; - _colSel = 0; - } - CTimerManager::setTimer(_timerInfo); - } + // ON KEY RELEASE } // press LEFT - navigate fields, or screens if(event & key_Left) { @@ -225,8 +202,14 @@ CSetTimerScreen::keyHandler(uint8_t event) // handle held down keys if(event & keyRepeat) { + _repeatCount++; bHeld = true; if(_rowSel == 1) { + if(event & key_Centre) { + _ScreenManager.reqUpdate(); + _rowSel = 0; + _colSel = 0; + } if(_colSel < 4) { // fast repeat of hour/minute adjustments by holding up or down keys if(event & key_Down) _adjust(-1); @@ -244,9 +227,37 @@ CSetTimerScreen::keyHandler(uint8_t event) } if(event & keyReleased) { - if(!bHeld) { - int maskDOW = 0x01 << _colSel; + if(!bHeld) { + if(event & key_Centre) { + if(_rowSel == 0) { + _ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // exit: return to clock screen + } + else if(_rowSel == 2) { // exit from per day settings + _rowSel = 1; + _colSel = 4; + } + else { // in config fields, save new settings + // test if the setting conflict with an already defined timer + _conflictID = CTimerManager::conflictTest(_timerInfo); + if(_conflictID) { + _timerInfo.enabled = 0; // cancel enabled status + _ConflictTime = millis() + 1500; + _ScreenManager.reqUpdate(); + _rowSel = 1; + _colSel = 4; // select enable/disable + } + else { + _SaveTime = millis() + 1500; + _ScreenManager.reqUpdate(); + _rowSel = 0; + _colSel = 0; + } + CTimerManager::setTimer(_timerInfo); + } + } + + int maskDOW = 0x01 << _colSel; // released DOWN - can only leave adjustment by using OK (centre button) if(event & key_Down) { // adjust selected item diff --git a/src/OLED/SetTimerScreen.h b/src/OLED/SetTimerScreen.h index 20b5371..f5dcb33 100644 --- a/src/OLED/SetTimerScreen.h +++ b/src/OLED/SetTimerScreen.h @@ -33,6 +33,7 @@ class CProtocol; class CSetTimerScreen : public CScreen { int _rowSel; int _colSel; + int _repeatCount; int _timerID; unsigned long _SaveTime; unsigned long _ConflictTime; diff --git a/src/OLED/TimerChartScreen.cpp b/src/OLED/TimerChartScreen.cpp index b2563f4..7934a0c 100644 --- a/src/OLED/TimerChartScreen.cpp +++ b/src/OLED/TimerChartScreen.cpp @@ -35,6 +35,7 @@ #include "../../lib/RTClib/RTClib.h" #include "fonts/MiniFont.h" #include "../RTC/TimerManager.h" +#include "../RTC/Clock.h" static uint8_t condensed[7][120]; @@ -50,7 +51,6 @@ CTimerChartScreen::CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr, void CTimerChartScreen::onSelect() { - CTimerManager::createMap(); CTimerManager::condenseMap(condensed); } @@ -145,10 +145,27 @@ CTimerChartScreen::show() } } } + cursor(); return true; } +void +CTimerChartScreen::cursor() +{ + static bool bShowWhite = true; + // create masking based upon TODAY + const BTCDateTime tNow = Clock.get(); + int dow = tNow.dayOfTheWeek(); + int todayTime = tNow.hour() * 60 + tNow.minute(); + + int yPos = 6 + 7*dow; + int xPos = 9 + int(todayTime * 0.0833333); // 1/12 + + _display.drawFastVLine(xPos, yPos, 8, bShowWhite ? WHITE : BLACK); + + bShowWhite = !bShowWhite; +} bool CTimerChartScreen::keyHandler(uint8_t event) diff --git a/src/OLED/TimerChartScreen.h b/src/OLED/TimerChartScreen.h index b26e792..08c715d 100644 --- a/src/OLED/TimerChartScreen.h +++ b/src/OLED/TimerChartScreen.h @@ -34,6 +34,7 @@ class CTimerChartScreen : public CScreen { int _colSel; int _instance; unsigned long _SaveTime; + void cursor(); public: CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr, int instance); diff --git a/src/RTC/Clock.cpp b/src/RTC/Clock.cpp index af38dc1..c3daa2f 100644 --- a/src/RTC/Clock.cpp +++ b/src/RTC/Clock.cpp @@ -68,9 +68,9 @@ DebugPort.println("Using millis() based psuedo \"Real Time Clock\""); _nextRTCfetch = millis(); - CTimerManager::createMap(); - update(); + + CTimerManager::createMap(); } const BTCDateTime& diff --git a/src/RTC/TimerManager.cpp b/src/RTC/TimerManager.cpp index e378006..7830938 100644 --- a/src/RTC/TimerManager.cpp +++ b/src/RTC/TimerManager.cpp @@ -34,7 +34,7 @@ #include "../Utility/NVStorage.h" #include "../Utility/helpers.h" -uint8_t CTimerManager::weekTimerIDs[7][CTimerManager::_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID +uint8_t CTimerManager::weekMap[7][CTimerManager::_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID int CTimerManager::activeTimer = 0; int CTimerManager::activeDow = 0; @@ -42,6 +42,17 @@ int CTimerManager::nextTimer = 0; int CTimerManager::nextStart = 0; bool CTimerManager::timerChanged = false; +#define SET_MAPS() { \ + if(pTimerMap) { \ + pTimerMap[dayMinute] |= activeday; \ + if(pTimerIDs) \ + pTimerIDs[dayMinute] |= timerBit; \ + } \ + else { \ + weekMap[dow][dayMinute] = recordTimer; \ + } \ +} + // create a bitmap that describes the pattern of on/off times void CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs) @@ -52,7 +63,8 @@ CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs memset(pTimerIDs, 0, _dayMinutes*sizeof(uint16_t)); } else { - memset(weekTimerIDs, 0, _dayMinutes*7*sizeof(uint8_t)); + DebugPort.println("Erasing weekMap"); + memset(weekMap, 0, _dayMinutes*7*sizeof(uint8_t)); } for(int timerID=0; timerID < 14; timerID++) { @@ -74,15 +86,14 @@ CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs void CTimerManager::createMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs) { - int timerBit = 0x0001 << timer.timerID; - - const BTCDateTime tNow = Clock.get(); - int todayDoW = 1 << tNow.dayOfTheWeek(); - int todayTime = tNow.hour() * 60 + tNow.minute(); + if(createOneShotMap(timer, pTimerMap, pTimerIDs)) { + return; + } 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 + // create linear minutes of day values for start & stop + // note that if stop < start, the timer rolls over midnight + int timerBit = 0x0001 << timer.timerID; // bit required for timer ID map 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 < _dayMinutes; dayMinute++) { @@ -90,62 +101,43 @@ CTimerManager::createMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs int dayBit = 0x01 << dow; if(timer.enabled & dayBit || timer.enabled & 0x80) { // specific or everyday uint16_t activeday = dayBit; // may also hold non repeat flag later - uint8_t recordTimer = (timer.timerID + 1) | (timer.repeat ? 0x80 : 0x00); + uint8_t recordTimer = (timer.timerID + 1) | (timer.repeat ? 0x80 : 0x00); // full week timer ID map if(!timer.repeat) { // flag timers that should get cancelled activeday |= (activeday << 8); // combine one shot status in MS byte } - if(timer.enabled == 0x80 && !timer.repeat) { // on-shot next occurrence timer - if(todayTime > timestart) { - } - } + // SET_MAPS() macro saves values as below: + // + // activeday -> pTimerMap[dayMinute] (if pTimerMap != NULL) + // timerBit -> pTimerID[dayMinute] (if pTimerMap != NULL AND pTimerID != NULL) + // recordTimer -> weekMap[dow][dayMinute] (if pTimerMap == NULL) + // if(timestop > timestart) { // treat normal start < stop times (within same day) if((dayMinute >= timestart) && (dayMinute < timestop)) { - if(pTimerMap) { - pTimerMap[dayMinute] |= activeday; - if(pTimerIDs) - pTimerIDs[dayMinute] |= timerBit; - } - else { - weekTimerIDs[dow][dayMinute] = recordTimer; - } + SET_MAPS(); } } else { // time straddles a day, start > stop, special treatment required if(dayMinute >= timestart) { // true from start until midnight - if(pTimerMap) { - pTimerMap[dayMinute] |= activeday; - if(pTimerIDs) - pTimerIDs[dayMinute] |= timerBit; - } - else { - weekTimerIDs[dow][dayMinute] = recordTimer; - } + SET_MAPS(); } if(dayMinute < timestop) { // after midnight, before stop time, i.e. next day // adjust for next day, taking care to wrap week if(dow == 6) { // last day of week? - activeday >>= 6; // roll back to start of week - if(pTimerMap == NULL) { - weekTimerIDs[0][dayMinute] = recordTimer; - } + dow = 0; + // because activeday holds both cancel and day info, shift it + activeday >>= 6; // roll back to start of week - } else { + dow++; activeday <<= 1; // next day - if(pTimerMap == NULL) { - weekTimerIDs[dow+1][dayMinute] = recordTimer; - } - } - if(pTimerMap) { - pTimerMap[dayMinute] |= activeday; - if(pTimerIDs) - pTimerIDs[dayMinute] |= timerBit; } + SET_MAPS(); } } } @@ -210,7 +202,7 @@ CTimerManager::condenseMap(uint8_t timerMap[7][120]) uint8_t condense = 0; for(int subInterval = 0; subInterval < 12; subInterval++, dayMinute++) { if(!condense) - condense = weekTimerIDs[dow][dayMinute]; + condense = weekMap[dow][dayMinute]; } timerMap[dow][opIndex++] = condense; } @@ -228,7 +220,7 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow) int retval = 0; int dayMinute = (hour * 60) + minute; - int newID = weekTimerIDs[dow][dayMinute]; + int newID = weekMap[dow][dayMinute]; if(activeTimer != newID) { DebugPort.printf("Timer ID change detected: %d", activeTimer & 0x0f); @@ -292,8 +284,8 @@ CTimerManager::findNextTimer(int hour, int minute, int dow) int limit = 24*60*7; while(limit--) { - if(weekTimerIDs[dow][dayMinute] & 0x0f) { - nextTimer = weekTimerIDs[dow][dayMinute]; + if(weekMap[dow][dayMinute] & 0x0f) { + nextTimer = weekMap[dow][dayMinute]; nextStart = dow*_dayMinutes + dayMinute; return nextTimer; } @@ -352,3 +344,60 @@ CTimerManager::conflictTest(int ID) timerChanged = true; return conflictID; } + +// special handling for next occurence of one-shot, non-repeating timers +bool +CTimerManager::createOneShotMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs) +{ + if((timer.enabled == 0x80) && !timer.repeat) { // on-shot next occurrence timer + DebugPort.printf("One shot, next occurence timer #%d\r\n", timer.timerID+1); + int timerBit = 0x0001 << timer.timerID; // value required for full week map + int timestart = timer.start.hour * 60 + timer.start.min; // linear minute of day + int timestop = timer.stop.hour * 60 + timer.stop.min; + // create masking based upon TODAY + const BTCDateTime tNow = Clock.get(); + int dow = tNow.dayOfTheWeek(); + int todayTime = tNow.hour() * 60 + tNow.minute(); + // wrap to next day if start time falls behind current time + if(todayTime >= timestart) { + dow++; + WRAPUPPERLIMIT(dow, 6, 0); + } + // create masks and record values for the assorted target arrays + uint16_t activeday = 1 << dow; // set day bit + activeday |= activeday << 8; // and set non repeat flag in MSB + uint8_t recordTimer = (timer.timerID + 1); + + // SET_MAPS() macro saves values as below: + // + // activeday -> pTimerMap[dayMinute] (if pTimerMap != NULL) + // timerBit -> pTimerID[dayMinute] (if pTimerMap != NULL AND pTimerID != NULL) + // recordTimer -> weekMap[dow][dayMinute] (if pTimerMap == NULL) + // + if(timestart < timestop) { + // timer does not wrap midnight - easy linear workout :-) + for(int dayMinute = timestart; dayMinute < timestop; dayMinute++) { + SET_MAPS(); + } + } + else { + // timer rolls over midnight + // fill map up from start time till end of day + for(int dayMinute = timestart; dayMinute < _dayMinutes; dayMinute++) { + SET_MAPS(); + } + // advance to next day, wrapping if required + dow++; + WRAPUPPERLIMIT(dow, 6, 0); + activeday = 1 << dow; // set day bit + activeday |= activeday << 8; // and set non repeat flag in MSB + // complete map from midnight till stop time + for(int dayMinute = 0; dayMinute < timestop; dayMinute++) { + SET_MAPS(); + } + } + return true; + } + return false; +} + diff --git a/src/RTC/TimerManager.h b/src/RTC/TimerManager.h index e7bc2bb..60ec349 100644 --- a/src/RTC/TimerManager.h +++ b/src/RTC/TimerManager.h @@ -38,6 +38,7 @@ struct sTimer; class CTimerManager { public: static const int _dayMinutes = 24*60; + static bool createOneShotMap(sTimer& timer, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL); static void createMap(int timermask = 0x3fff, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL); static void createMap(sTimer& timer, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL); static void condenseMap(uint16_t timerMap[_dayMinutes], int factor); @@ -56,7 +57,7 @@ private: static int prevState; static int nextTimer; static int nextStart; - static uint8_t weekTimerIDs[7][_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID + static uint8_t weekMap[7][_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID static bool timerChanged; }; diff --git a/src/Utility/BTC_JSON.cpp b/src/Utility/BTC_JSON.cpp index 3517ce8..78d6ac9 100644 --- a/src/Utility/BTC_JSON.cpp +++ b/src/Utility/BTC_JSON.cpp @@ -562,7 +562,7 @@ void updateJSONclients(bool report) getBluetoothClient().send( expand.c_str() ); unsigned long tBT = millis() - tStart; bNewTimerInfo = true; - DebugPort.printf("JSON times : %ld,%ld,%ld\r\n", tJSON, tBT, tWF); +// DebugPort.printf("JSON times : %ld,%ld,%ld\r\n", tJSON, tBT, tWF); } } // request timer refesh upon clients @@ -577,7 +577,9 @@ void updateJSONclients(bool report) root.set("TimerRefresh", 1); root.printTo(jsonStr, 800); - DebugPort.printf("JSON send: %s\r\n", jsonStr); + if (report) { + DebugPort.printf("JSON send: %s\r\n", jsonStr); + } sendWebSocketString( jsonStr ); std::string expand = jsonStr; Expand(expand); diff --git a/src/Utility/FuelGauge.cpp b/src/Utility/FuelGauge.cpp index f058d1d..94e43f4 100644 --- a/src/Utility/FuelGauge.cpp +++ b/src/Utility/FuelGauge.cpp @@ -63,7 +63,7 @@ CFuelGauge::Integrate(float Hz) float fuelDelta = _pumpStrokes - _lastStoredVal; bool bStoppedSave = (Hz == 0) && (_pumpStrokes != _lastStoredVal); if(fuelDelta > 10 || bStoppedSave) { // record fuel usage every 10 minutes, or every 10 strokes - DebugPort.printf("Storing fuel gauge: %.2f strokes\r\n", _pumpStrokes); +// DebugPort.printf("Storing fuel gauge: %.2f strokes\r\n", _pumpStrokes); RTC_Store.setFuelGauge(_pumpStrokes); // uses RTC registers to store this _lastStoredVal = _pumpStrokes; }