From c1207e66effb0e1cbc3e4f9719c4265f15aad52b Mon Sep 17 00:00:00 2001 From: Ray Jones Date: Sun, 28 Jul 2019 17:40:12 +1000 Subject: [PATCH] Extreme makeover to timer manager, now properly shows one shot next day timers in timer chart. Added cursor showing time of day on Timer chart. --- Documentation/ScreenFlowV3.dia | Bin 0 -> 6351 bytes Documentation/ScreenFlowV3.png | Bin 0 -> 35802 bytes icons/OutputConfigs.xml | 33 ++++++++ src/OLED/ScreenManager.cpp | 1 - src/OLED/SetClockScreen.cpp | 46 ++++++----- src/OLED/SetClockScreen.h | 2 +- src/OLED/SetTimerScreen.cpp | 67 +++++++++------- src/OLED/SetTimerScreen.h | 1 + src/OLED/TimerChartScreen.cpp | 19 ++++- src/OLED/TimerChartScreen.h | 1 + src/RTC/Clock.cpp | 4 +- src/RTC/TimerManager.cpp | 141 ++++++++++++++++++++++----------- src/RTC/TimerManager.h | 3 +- src/Utility/BTC_JSON.cpp | 6 +- src/Utility/FuelGauge.cpp | 2 +- 15 files changed, 221 insertions(+), 105 deletions(-) create mode 100644 Documentation/ScreenFlowV3.dia create mode 100644 Documentation/ScreenFlowV3.png diff --git a/Documentation/ScreenFlowV3.dia b/Documentation/ScreenFlowV3.dia new file mode 100644 index 0000000000000000000000000000000000000000..7f42e60c1d695741df8af9e900ed24cbd373dc6f GIT binary patch literal 6351 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 RM)U74{vS)_*<)oB0szhYg1`U( literal 0 HcmV?d00001 diff --git a/Documentation/ScreenFlowV3.png b/Documentation/ScreenFlowV3.png new file mode 100644 index 0000000000000000000000000000000000000000..32d9824d764605e9b25d55e51eba1354b3f05141 GIT binary patch literal 35802 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 GHZdo|`Vq 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 y;z#uzicnKgQPJ9`@9Rb3HaHwkMMZ@(^Z)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; }