Browse Source

Merge branch 'You69Man-feature/PR_display_graph_0_8_46' into development03

pull/1341/head
lumapu 9 months ago
parent
commit
42926c4d26
  1. 8
      src/config/settings.h
  2. 11
      src/plugins/Display/Display.h
  3. 161
      src/plugins/Display/Display_Mono.h
  4. 4
      src/plugins/Display/Display_Mono_128X32.h
  5. 100
      src/plugins/Display/Display_Mono_128X64.h
  6. 4
      src/plugins/Display/Display_Mono_64X48.h
  7. 93
      src/plugins/Display/Display_Mono_84X48.h
  8. 2
      src/plugins/Display/Display_data.h
  9. 2
      src/web/RestApi.h
  10. 18
      src/web/html/setup.html
  11. 2
      src/web/web.h

8
src/config/settings.h

@ -165,6 +165,8 @@ typedef struct {
uint8_t type; uint8_t type;
bool pwrSaveAtIvOffline; bool pwrSaveAtIvOffline;
uint8_t screenSaver; uint8_t screenSaver;
uint8_t graph_ratio;
uint8_t graph_size;
uint8_t rot; uint8_t rot;
//uint16_t wakeUp; //uint16_t wakeUp;
//uint16_t sleepAt; //uint16_t sleepAt;
@ -461,6 +463,8 @@ class settings {
mCfg.plugin.display.pwrSaveAtIvOffline = false; mCfg.plugin.display.pwrSaveAtIvOffline = false;
mCfg.plugin.display.contrast = 60; mCfg.plugin.display.contrast = 60;
mCfg.plugin.display.screenSaver = 1; // default: 1 .. pixelshift for OLED for downward compatibility mCfg.plugin.display.screenSaver = 1; // default: 1 .. pixelshift for OLED for downward compatibility
mCfg.plugin.display.graph_ratio = 50;
mCfg.plugin.display.graph_size = 2;
mCfg.plugin.display.rot = 0; mCfg.plugin.display.rot = 0;
mCfg.plugin.display.disp_data = DEF_PIN_OFF; // SDA mCfg.plugin.display.disp_data = DEF_PIN_OFF; // SDA
mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL
@ -697,6 +701,8 @@ class settings {
disp[F("type")] = mCfg.plugin.display.type; disp[F("type")] = mCfg.plugin.display.type;
disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline; disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline;
disp[F("screenSaver")] = mCfg.plugin.display.screenSaver; disp[F("screenSaver")] = mCfg.plugin.display.screenSaver;
disp[F("graph_ratio")] = mCfg.plugin.display.graph_ratio;
disp[F("graph_size")] = mCfg.plugin.display.graph_size;
disp[F("rotation")] = mCfg.plugin.display.rot; disp[F("rotation")] = mCfg.plugin.display.rot;
//disp[F("wake")] = mCfg.plugin.display.wakeUp; //disp[F("wake")] = mCfg.plugin.display.wakeUp;
//disp[F("sleep")] = mCfg.plugin.display.sleepAt; //disp[F("sleep")] = mCfg.plugin.display.sleepAt;
@ -713,6 +719,8 @@ class settings {
getVal<uint8_t>(disp, F("type"), &mCfg.plugin.display.type); getVal<uint8_t>(disp, F("type"), &mCfg.plugin.display.type);
getVal<bool>(disp, F("pwrSafe"), &mCfg.plugin.display.pwrSaveAtIvOffline); getVal<bool>(disp, F("pwrSafe"), &mCfg.plugin.display.pwrSaveAtIvOffline);
getVal<uint8_t>(disp, F("screenSaver"), &mCfg.plugin.display.screenSaver); getVal<uint8_t>(disp, F("screenSaver"), &mCfg.plugin.display.screenSaver);
getVal<uint8_t>(disp, F("graph_ratio"), &mCfg.plugin.display.graph_ratio);
getVal<uint8_t>(disp, F("graph_size"), &mCfg.plugin.display.graph_size);
getVal<uint8_t>(disp, F("rotation"), &mCfg.plugin.display.rot); getVal<uint8_t>(disp, F("rotation"), &mCfg.plugin.display.rot);
//mCfg.plugin.display.wakeUp = disp[F("wake")]; //mCfg.plugin.display.wakeUp = disp[F("wake")];
//mCfg.plugin.display.sleepAt = disp[F("sleep")]; //mCfg.plugin.display.sleepAt = disp[F("sleep")];

11
src/plugins/Display/Display.h

@ -54,7 +54,7 @@ class Display {
default: mMono = NULL; break; default: mMono = NULL; break;
} }
if(mMono) { if(mMono) {
mMono->config(mCfg->pwrSaveAtIvOffline, mCfg->screenSaver, mCfg->contrast); mMono->config(mCfg->pwrSaveAtIvOffline, mCfg->screenSaver, mCfg->contrast, mCfg->graph_ratio, mCfg->graph_size);
mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, &mDisplayData); mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, &mDisplayData);
} }
@ -75,10 +75,12 @@ class Display {
} }
void tickerSecond() { void tickerSecond() {
bool request_refresh = false;
if (mMono != NULL) if (mMono != NULL)
mMono->loop(mCfg->contrast, motionSensorActive()); request_refresh = mMono->loop(mCfg->contrast, motionSensorActive());
if (mNewPayload || (((++mLoopCnt) % 5) == 0)) { if (mNewPayload || (((++mLoopCnt) % 5) == 0) || request_refresh) {
DataScreen(); DataScreen();
mNewPayload = false; mNewPayload = false;
mLoopCnt = 0; mLoopCnt = 0;
@ -165,6 +167,9 @@ class Display {
else else
mDisplayData.utcTs = 0; mDisplayData.utcTs = 0;
mDisplayData.pGraphStartTime = mApp->getSunrise();
mDisplayData.pGraphEndTime = mApp->getSunset();
if (mMono ) { if (mMono ) {
mMono->disp(); mMono->disp();
} }

161
src/plugins/Display/Display_Mono.h

@ -26,12 +26,12 @@ class DisplayMono {
DisplayMono() {}; DisplayMono() {};
virtual void init(uint8_t type, uint8_t rot, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) = 0; virtual void init(uint8_t type, uint8_t rot, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) = 0;
virtual void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum) = 0; virtual void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum, uint8_t graph_ratio, uint8_t graph_size) = 0;
virtual void disp(void) = 0; virtual void disp(void) = 0;
// Common loop function, manages display on/off functions for powersave and screensaver with motionsensor // Common loop function, manages display on/off functions for powersave and screensaver with motionsensor
// can be overridden by subclasses // can be overridden by subclasses
virtual void loop(uint8_t lum, bool motion) { virtual bool loop(uint8_t lum, bool motion) {
bool dispConditions = (!mEnPowerSave || (mDisplayData->nrProducing > 0)) && bool dispConditions = (!mEnPowerSave || (mDisplayData->nrProducing > 0)) &&
((mScreenSaver != 2) || motion); // screensaver 2 .. motionsensor ((mScreenSaver != 2) || motion); // screensaver 2 .. motionsensor
@ -60,12 +60,23 @@ class DisplayMono {
mLuminance = lum; mLuminance = lum;
mDisplay->setContrast(mLuminance); mDisplay->setContrast(mLuminance);
} }
return(monoMaintainDispSwitchState());
} }
protected: protected:
U8G2* mDisplay; U8G2* mDisplay;
DisplayData *mDisplayData; DisplayData *mDisplayData;
float *mPgData=nullptr;
uint8_t mPgWidth=0;
uint8_t mPgHeight=0;
float mPgMaxPwr=0.0;
// float mPgMaxAvailPower = 0.0;
uint32_t mPgPeriod=0; // seconds
uint32_t mPgTimeOfDay=0;
uint8_t mPgLastPos=0;
uint8_t mType; uint8_t mType;
uint16_t mDispWidth; uint16_t mDispWidth;
uint16_t mDispHeight; uint16_t mDispHeight;
@ -73,6 +84,8 @@ class DisplayMono {
bool mEnPowerSave; bool mEnPowerSave;
uint8_t mScreenSaver = 1; // 0 .. off; 1 .. pixelShift; 2 .. motionsensor uint8_t mScreenSaver = 1; // 0 .. off; 1 .. pixelShift; 2 .. motionsensor
uint8_t mLuminance; uint8_t mLuminance;
uint8_t mGraphRatio;
uint8_t mGraphSize;
uint8_t mLoopCnt; uint8_t mLoopCnt;
uint8_t mLineXOffsets[5] = {}; uint8_t mLineXOffsets[5] = {};
@ -81,9 +94,16 @@ class DisplayMono {
uint8_t mExtra; uint8_t mExtra;
int8_t mPixelshift=0; int8_t mPixelshift=0;
TimeMonitor mDisplayTime = TimeMonitor(1000 * DISP_DEFAULT_TIMEOUT, true); TimeMonitor mDisplayTime = TimeMonitor(1000 * DISP_DEFAULT_TIMEOUT, true);
TimeMonitor mDispSwitchTime = TimeMonitor();
uint8_t mDispSwitchState;
bool mDisplayActive = true; // always start with display on bool mDisplayActive = true; // always start with display on
char mFmtText[DISP_FMT_TEXT_LEN]; char mFmtText[DISP_FMT_TEXT_LEN];
enum _dispSwitchState {
d_POWER_TEXT = 0,
d_POWER_GRAPH = 1,
};
// Common initialization function to be called by subclasses // Common initialization function to be called by subclasses
void monoInit(U8G2* display, uint8_t type, DisplayData *displayData) { void monoInit(U8G2* display, uint8_t type, DisplayData *displayData) {
mDisplay = display; mDisplay = display;
@ -95,8 +115,145 @@ class DisplayMono {
mDisplay->clearBuffer(); mDisplay->clearBuffer();
mDispWidth = mDisplay->getDisplayWidth(); mDispWidth = mDisplay->getDisplayWidth();
mDispHeight = mDisplay->getDisplayHeight(); mDispHeight = mDisplay->getDisplayHeight();
mDispSwitchTime.stopTimeMonitor();
mDispSwitchState = d_POWER_TEXT;
if (mGraphRatio == 100) // if graph ratio is 100% start in graph mode
mDispSwitchState = d_POWER_GRAPH;
else if (mGraphRatio != 0)
mDispSwitchTime.startTimeMonitor(150 * (100 - mGraphRatio)); // start display mode change only if ratio is neither 0 nor 100
}
bool monoMaintainDispSwitchState(void) {
bool change = false;
switch(mDispSwitchState) {
case d_POWER_TEXT:
if (mDispSwitchTime.isTimeout()) {
mDispSwitchState = d_POWER_GRAPH;
mDispSwitchTime.startTimeMonitor(150 * mGraphRatio); // mGraphRatio: 0-100 Gesamtperiode 15000 ms
change = true;
}
break;
case d_POWER_GRAPH:
if (mDispSwitchTime.isTimeout()) {
mDispSwitchState = d_POWER_TEXT;
mDispSwitchTime.startTimeMonitor(150 * (100 - mGraphRatio));
change = true;
}
break;
}
return change;
}
void initPowerGraph(uint8_t width, uint8_t height) {
mPgWidth = width;
mPgHeight = height;
mPgData = new float[mPgWidth];
//memset(mPgData, 0, mPgWidth);
resetPowerGraph();
/*
Inverter<> *iv;
mPgMaxAvailPower = 0;
uint8_t nInv = mSys->getNumInverters();
for (uint8_t i = 0; i < nInv; i++) {
iv = mSys->getInverterByPos(i);
if (iv == NULL)
continue;
for (uint8_t ch = 0; ch < 6; ch++) {
mPgMaxAvailPower += iv->config->chMaxPwr[ch];
}
}
DBGPRINTLN("max. Power = " + String(mPgMaxAvailPower));*/
}
void resetPowerGraph() {
if (mPgData != nullptr) {
mPgMaxPwr = 0.0;
mPgLastPos = 0;
for (uint8_t i = 0; i < mPgWidth; i++)
mPgData[i] = 0.0;
}
}
uint8_t sss2pgpos(uint seconds_since_start) {
return(seconds_since_start * (mPgWidth - 1) / (mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime));
}
void calcPowerGraphValues() {
mPgPeriod = mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime; // length of power graph for scaling of x-axis
uint32_t oldTimeOfDay = mPgTimeOfDay;
mPgTimeOfDay = (mDisplayData->utcTs > mDisplayData->pGraphStartTime) ? mDisplayData->utcTs - mDisplayData->pGraphStartTime : 0; // current time of day with respect to current sunrise time
if (oldTimeOfDay > mPgTimeOfDay) // new day -> reset old data
resetPowerGraph();
mPgLastPos = std::min((uint8_t) (mPgTimeOfDay * (mPgWidth - 1) / mPgPeriod), (uint8_t) (mPgWidth - 1)); // current datapoint based on currenct time of day
}
void addPowerGraphEntry(float val) {
if (mDisplayData->utcTs > 0) { // precondition: utc time available
calcPowerGraphValues();
//mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], (uint8_t) (val * 255.0 / mPgMaxAvailPower)); // normalizing of data to 0-255
mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], val);
mPgMaxPwr = std::max(mPgMaxPwr, val); // max value of stored data for scaling of y-axis
}
}
uint8_t getPowerGraphXpos(uint8_t p) { //
if ((p <= mPgLastPos) && (mPgLastPos > 0))
return((p * (mPgWidth - 1)) / mPgLastPos); // scaling of x-axis
else
return(0);
}
uint8_t getPowerGraphYpos(uint8_t p) {
if (p < mPgWidth)
//return(((uint32_t) mPgData[p] * (uint32_t) mPgMaxAvailPower) * (uint32_t) mPgHeight / mPgMaxPwr / 255); // scaling of normalized data (0-255) to graph height
return((mPgData[p] * (uint32_t) mPgHeight / mPgMaxPwr)); // scaling of data to graph height
else
return(0);
}
void plotPowerGraph(uint8_t xoff, uint8_t yoff) {
// draw axes
mDisplay->drawLine(xoff, yoff, xoff, yoff - mPgHeight); // vertical axis
mDisplay->drawLine(xoff, yoff, xoff + mPgWidth, yoff); // horizontal axis
// draw X scale
tmElements_t tm;
breakTime(mDisplayData->pGraphEndTime, tm);
uint8_t endHourPg = tm.Hour;
breakTime(mDisplayData->utcTs, tm);
uint8_t endHour = std::min(endHourPg, tm.Hour);
breakTime(mDisplayData->pGraphStartTime, tm);
tm.Hour += 1;
tm.Minute = 0;
tm.Second = 0;
for (; tm.Hour <= endHour; tm.Hour++) {
uint8_t x_pos_screen = getPowerGraphXpos(sss2pgpos((uint32_t) makeTime(tm) - mDisplayData->pGraphStartTime)); // scale horizontal axis
mDisplay->drawPixel(xoff + x_pos_screen, yoff - 1);
}
// draw Y scale
uint16_t scale_y = 10;
uint32_t maxpwr_int = static_cast<uint8_t>(std::round(mPgMaxPwr));
if (maxpwr_int > 100)
scale_y = 100;
for (uint32_t i = scale_y; i <= maxpwr_int; i += scale_y) {
uint8_t ypos = yoff - static_cast<uint8_t>(std::round(i * (float) mPgHeight / mPgMaxPwr)); // scale vertical axis
mDisplay->drawPixel(xoff + 1, ypos);
}
// draw curve
for (uint8_t i = 1; i <= mPgLastPos; i++) {
mDisplay->drawLine(xoff + getPowerGraphXpos(i - 1), yoff - getPowerGraphYpos(i - 1),
xoff + getPowerGraphXpos(i), yoff - getPowerGraphYpos(i));
}
// print max power value
mDisplay->setFont(u8g2_font_4x6_tr);
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%dW", static_cast<uint16_t>(std::round(mPgMaxPwr)));
mDisplay->drawStr(xoff + 3, yoff - mPgHeight + 5, mFmtText);
} }
// pixelshift screensaver with wipe effect
void calcPixelShift(int range) { void calcPixelShift(int range) {
int8_t mod = (millis() / 10000) % ((range >> 1) << 2); int8_t mod = (millis() / 10000) % ((range >> 1) << 2);
mPixelshift = mScreenSaver == 1 ? ((mod < range) ? mod - (range >> 1) : -(mod - range - (range >> 1) + 1)) : 0; mPixelshift = mScreenSaver == 1 ? ((mod < range) ? mod - (range >> 1) : -(mod - range - (range >> 1) + 1)) : 0;

4
src/plugins/Display/Display_Mono_128X32.h

@ -12,10 +12,12 @@ class DisplayMono128X32 : public DisplayMono {
mExtra = 0; mExtra = 0;
} }
void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum) { void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum, uint8_t graph_ratio, uint8_t graph_size) {
mEnPowerSave = enPowerSave; mEnPowerSave = enPowerSave;
mScreenSaver = screenSaver; mScreenSaver = screenSaver;
mLuminance = lum; mLuminance = lum;
mGraphRatio = graph_ratio;
mGraphSize = graph_size;
} }
void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) {

100
src/plugins/Display/Display_Mono_128X64.h

@ -12,10 +12,12 @@ class DisplayMono128X64 : public DisplayMono {
mExtra = 0; mExtra = 0;
} }
void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum) { void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum, uint8_t graph_ratio, uint8_t graph_size) {
mEnPowerSave = enPowerSave; mEnPowerSave = enPowerSave;
mScreenSaver = screenSaver; mScreenSaver = screenSaver;
mLuminance = lum; mLuminance = lum;
mGraphRatio = graph_ratio;
mGraphSize = graph_size;
} }
void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) {
@ -34,6 +36,34 @@ class DisplayMono128X64 : public DisplayMono {
} }
calcLinePositions(); calcLinePositions();
switch(mGraphSize) { // var opts2 = [[0, "Line 1 - 2"], [1, "Line 2 - 3"], [2, "Line 1 - 3"], [3, "Line 2 - 4"], [4, "Line 1 - 4"]];
case 0:
graph_first_line = 1;
graph_last_line = 2;
break;
case 1:
graph_first_line = 2;
graph_last_line = 3;
break;
case 2:
graph_first_line = 1;
graph_last_line = 3;
break;
case 3:
graph_first_line = 2;
graph_last_line = 4;
break;
case 4:
default:
graph_first_line = 1;
graph_last_line = 4;
break;
}
widthShrink = (mScreenSaver == 1) ? pixelShiftRange : 0; // shrink graphwidth for pixelshift screensaver
initPowerGraph(mDispWidth - 22 - widthShrink, mLineYOffsets[graph_last_line] - mLineYOffsets[graph_first_line - 1] - 2);
printText("Ahoy!", l_Ahoy, 0xff); printText("Ahoy!", l_Ahoy, 0xff);
printText("ahoydtu.de", l_Website, 0xff); printText("ahoydtu.de", l_Website, 0xff);
printText(mDisplayData->version, l_Version, 0xff); printText(mDisplayData->version, l_Version, 0xff);
@ -61,23 +91,17 @@ class DisplayMono128X64 : public DisplayMono {
// calculate current pixelshift for pixelshift screensaver // calculate current pixelshift for pixelshift screensaver
calcPixelShift(pixelShiftRange); calcPixelShift(pixelShiftRange);
// print total power // add new power data to power graph
if (mDisplayData->nrProducing > 0) { if (mDisplayData->nrProducing > 0) {
if (mDisplayData->totalPower > 9999.0) addPowerGraphEntry(mDisplayData->totalPower);
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (mDisplayData->totalPower / 1000.0));
else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f W", mDisplayData->totalPower);
printText(mFmtText, l_TotalPower, 0xff);
} else {
printText("offline", l_TotalPower, 0xff);
} }
// print Date and time // print Date and time
if (0 != mDisplayData->utcTs) if (0 != mDisplayData->utcTs)
printText(ah::getDateTimeStrShort(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), l_Time, 0xff); printText(ah::getDateTimeStrShort(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), l_Time, 0xff);
// dynamic status bar, alternatively: if (showLine(l_Status)) {
// alternatively:
// print ip address // print ip address
if (!(mExtra % 5) && (mDisplayData->ipAddress)) { if (!(mExtra % 5) && (mDisplayData->ipAddress)) {
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%s", (mDisplayData->ipAddress).toString().c_str()); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%s", (mDisplayData->ipAddress).toString().c_str());
@ -114,8 +138,24 @@ class DisplayMono128X64 : public DisplayMono {
if (moon_pos!=-1) if (moon_pos!=-1)
mDisplay->drawStr(pos + moon_pos + mPixelshift, mLineYOffsets[l_Status], "H"); // moon symbol mDisplay->drawStr(pos + moon_pos + mPixelshift, mLineYOffsets[l_Status], "H"); // moon symbol
} }
}
if (showLine(l_TotalPower)) {
// print total power
if (mDisplayData->nrProducing > 0) {
if (mDisplayData->totalPower > 9999.0)
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (mDisplayData->totalPower / 1000.0));
else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f W", mDisplayData->totalPower);
printText(mFmtText, l_TotalPower, 0xff);
} else {
printText("offline", l_TotalPower, 0xff);
}
}
// print yields if (showLine(l_YieldDay)) {
// print day yield
mDisplay->setFont(u8g2_font_ncenB10_symbols10_ahoy); mDisplay->setFont(u8g2_font_ncenB10_symbols10_ahoy);
mDisplay->drawStr(16 + mPixelshift, mLineYOffsets[l_YieldDay], "I"); // day symbol mDisplay->drawStr(16 + mPixelshift, mLineYOffsets[l_YieldDay], "I"); // day symbol
mDisplay->drawStr(16 + mPixelshift, mLineYOffsets[l_YieldTotal], "D"); // total symbol mDisplay->drawStr(16 + mPixelshift, mLineYOffsets[l_YieldTotal], "D"); // total symbol
@ -125,42 +165,44 @@ class DisplayMono128X64 : public DisplayMono {
else else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f Wh", mDisplayData->totalYieldDay); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f Wh", mDisplayData->totalYieldDay);
printText(mFmtText, l_YieldDay, 0xff); printText(mFmtText, l_YieldDay, 0xff);
}
if (showLine(l_YieldTotal)) {
// print total yield
if (mDisplayData->totalYieldTotal > 9999.0) if (mDisplayData->totalYieldTotal > 9999.0)
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f MWh", mDisplayData->totalYieldTotal / 1000.0); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f MWh", mDisplayData->totalYieldTotal / 1000.0);
else else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f kWh", mDisplayData->totalYieldTotal); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f kWh", mDisplayData->totalYieldTotal);
printText(mFmtText, l_YieldTotal, 0xff); printText(mFmtText, l_YieldTotal, 0xff);
}
if (mDispSwitchState == d_POWER_GRAPH) {
// plot power graph
plotPowerGraph((mDispWidth - mPgWidth) / 2 + mPixelshift, mLineYOffsets[graph_last_line] - 1);
}
// draw dynamic RSSI bars // draw dynamic RSSI bars
int xoffs;
if (mScreenSaver == 1) // shrink screenwidth for pixelshift screensaver
xoffs = pixelShiftRange/2;
else
xoffs = 0;
int rssi_bar_height = 9; int rssi_bar_height = 9;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int radio_rssi_threshold = -60 - i * 10; int radio_rssi_threshold = -60 - i * 10;
int wifi_rssi_threshold = -60 - i * 10; int wifi_rssi_threshold = -60 - i * 10;
uint8_t barwidth = std::min(4 - i, 3);
if (mDisplayData->RadioRSSI > radio_rssi_threshold) if (mDisplayData->RadioRSSI > radio_rssi_threshold)
mDisplay->drawBox(xoffs + mPixelshift, 8 + (rssi_bar_height + 1) * i, 4 - i, rssi_bar_height); mDisplay->drawBox(widthShrink / 2 + mPixelshift, 8 + (rssi_bar_height + 1) * i, barwidth, rssi_bar_height);
if (mDisplayData->WifiRSSI > wifi_rssi_threshold) if (mDisplayData->WifiRSSI > wifi_rssi_threshold)
mDisplay->drawBox(mDispWidth - 4 - xoffs + mPixelshift + i, 8 + (rssi_bar_height + 1) * i, 4 - i, rssi_bar_height); mDisplay->drawBox(mDispWidth - barwidth - widthShrink / 2 + mPixelshift, 8 + (rssi_bar_height + 1) * i, barwidth, rssi_bar_height);
} }
// draw dynamic antenna and WiFi symbols // draw dynamic antenna and WiFi symbols
mDisplay->setFont(u8g2_font_ncenB10_symbols10_ahoy); mDisplay->setFont(u8g2_font_ncenB10_symbols10_ahoy);
char sym[]=" "; char sym[]=" ";
sym[0] = mDisplayData->RadioSymbol?'A':'E'; // NRF sym[0] = mDisplayData->RadioSymbol?'A':'E'; // NRF
mDisplay->drawStr(xoffs + mPixelshift, mLineYOffsets[l_RSSI], sym); mDisplay->drawStr(widthShrink / 2 + mPixelshift, mLineYOffsets[l_RSSI], sym);
if (mDisplayData->MQTTSymbol) if (mDisplayData->MQTTSymbol)
sym[0] = 'J'; // MQTT sym[0] = 'J'; // MQTT
else else
sym[0] = mDisplayData->WifiSymbol?'B':'F'; // Wifi sym[0] = mDisplayData->WifiSymbol?'B':'F'; // Wifi
mDisplay->drawStr(mDispWidth - mDisplay->getStrWidth(sym) - xoffs + mPixelshift, mLineYOffsets[l_RSSI], sym); mDisplay->drawStr(mDispWidth - mDisplay->getStrWidth(sym) - widthShrink / 2 + mPixelshift, mLineYOffsets[l_RSSI], sym);
mDisplay->sendBuffer();
mDisplay->sendBuffer(); mDisplay->sendBuffer();
mExtra++; mExtra++;
@ -184,7 +226,11 @@ class DisplayMono128X64 : public DisplayMono {
l_MAX_LINES = 5, l_MAX_LINES = 5,
}; };
uint8_t graph_first_line;
uint8_t graph_last_line;
const uint8_t pixelShiftRange = 11; // number of pixels to shift from left to right (centered -> must be odd!) const uint8_t pixelShiftRange = 11; // number of pixels to shift from left to right (centered -> must be odd!)
uint8_t widthShrink;
void calcLinePositions() { void calcLinePositions() {
uint8_t yOff = 0; uint8_t yOff = 0;
@ -199,7 +245,7 @@ class DisplayMono128X64 : public DisplayMono {
dsc = mDisplay->getDescent(); dsc = mDisplay->getDescent();
yOff -= dsc; yOff -= dsc;
if (l_Time == i) // prevent time and status line to touch if (l_Time == i) // prevent time and status line to touch
yOff+=1; // -> one pixels space yOff++; // -> one pixels space
i++; i++;
} while(l_MAX_LINES>i); } while(l_MAX_LINES>i);
} }
@ -226,4 +272,8 @@ class DisplayMono128X64 : public DisplayMono {
dispX += mPixelshift; dispX += mPixelshift;
mDisplay->drawStr(dispX, mLineYOffsets[line], text); mDisplay->drawStr(dispX, mLineYOffsets[line], text);
} }
bool showLine(uint8_t line) {
return ((mDispSwitchState == d_POWER_TEXT) || ((line < graph_first_line) || (line > graph_last_line)));
}
}; };

4
src/plugins/Display/Display_Mono_64X48.h

@ -12,10 +12,12 @@ class DisplayMono64X48 : public DisplayMono {
mExtra = 0; mExtra = 0;
} }
void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum) { void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum, uint8_t graph_ratio, uint8_t graph_size) {
mEnPowerSave = enPowerSave; mEnPowerSave = enPowerSave;
mScreenSaver = screenSaver; mScreenSaver = screenSaver;
mLuminance = lum; mLuminance = lum;
mGraphRatio = graph_ratio;
mGraphSize = graph_size;
} }
void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) {

93
src/plugins/Display/Display_Mono_84X48.h

@ -5,7 +5,6 @@
#pragma once #pragma once
#include "Display_Mono.h" #include "Display_Mono.h"
#include "../../utils/dbg.h"
class DisplayMono84X48 : public DisplayMono { class DisplayMono84X48 : public DisplayMono {
public: public:
@ -13,16 +12,45 @@ class DisplayMono84X48 : public DisplayMono {
mExtra = 0; mExtra = 0;
} }
void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum) { void config(bool enPowerSave, uint8_t screenSaver, uint8_t lum, uint8_t graph_ratio, uint8_t graph_size) {
mEnPowerSave = enPowerSave; mEnPowerSave = enPowerSave;
mScreenSaver = screenSaver; mScreenSaver = screenSaver;
mLuminance = lum; mLuminance = lum;
mGraphRatio = graph_ratio;
mGraphSize = graph_size;
} }
void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) {
u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0);
monoInit(new U8G2_PCD8544_84X48_F_4W_SW_SPI(rot, clock, data, cs, dc, reset), type, displayData); monoInit(new U8G2_PCD8544_84X48_F_4W_SW_SPI(rot, clock, data, cs, dc, reset), type, displayData);
calcLinePositions(); calcLinePositions();
switch(mGraphSize) { // var opts2 = [[0, "Line 1 - 2"], [1, "Line 2 - 3"], [2, "Line 1 - 3"], [3, "Line 2 - 4"], [4, "Line 1 - 4"]];
case 0:
graph_first_line = 1;
graph_last_line = 2;
break;
case 1:
graph_first_line = 2;
graph_last_line = 3;
break;
case 2:
graph_first_line = 1;
graph_last_line = 3;
break;
case 3:
graph_first_line = 2;
graph_last_line = 4;
break;
case 4:
default:
graph_first_line = 1;
graph_last_line = 4;
break;
}
initPowerGraph(mDispWidth - 16, mLineYOffsets[graph_last_line] - mLineYOffsets[graph_first_line - 1] - 2);
printText("Ahoy!", l_Ahoy, 0xff); printText("Ahoy!", l_Ahoy, 0xff);
printText("ahoydtu.de", l_Website, 0xff); printText("ahoydtu.de", l_Website, 0xff);
printText(mDisplayData->version, l_Version, 0xff); printText(mDisplayData->version, l_Version, 0xff);
@ -45,22 +73,16 @@ class DisplayMono84X48 : public DisplayMono {
mDisplay->drawPixel(mDispWidth-1, mDispHeight-1); mDisplay->drawPixel(mDispWidth-1, mDispHeight-1);
*/ */
// print total power // add new power data to power graph
if (mDisplayData->nrProducing > 0) { if (mDisplayData->nrProducing > 0) {
if (mDisplayData->totalPower > 9999.0) addPowerGraphEntry(mDisplayData->totalPower);
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (mDisplayData->totalPower / 1000.0));
else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f W", mDisplayData->totalPower);
printText(mFmtText, l_TotalPower, 0xff);
} else {
printText("offline", l_TotalPower, 0xff);
} }
// print Date and time // print Date and time
if (0 != mDisplayData->utcTs) if (0 != mDisplayData->utcTs)
printText(ah::getDateTimeStrShort(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), l_Time, 0xff); printText(ah::getDateTimeStrShort(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), l_Time, 0xff);
if (showLine(l_Status)) {
// alternatively: // alternatively:
// print ip address // print ip address
if (!(mExtra % 5) && (mDisplayData->ipAddress)) { if (!(mExtra % 5) && (mDisplayData->ipAddress)) {
@ -79,32 +101,57 @@ class DisplayMono84X48 : public DisplayMono {
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%d\x86 %d\x87", mDisplayData->nrProducing, mDisplayData->nrSleeping); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%d\x86 %d\x87", mDisplayData->nrProducing, mDisplayData->nrSleeping);
printText(mFmtText, l_Status, 0xff); printText(mFmtText, l_Status, 0xff);
} }
}
// print yields if (showLine(l_TotalPower)) {
printText("\x88", l_YieldDay, 10); // day symbol // print total power
printText("\x83", l_YieldTotal, 10); // total symbol if (mDisplayData->nrProducing > 0) {
if (mDisplayData->totalPower > 9999.0)
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (mDisplayData->totalPower / 1000.0));
else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f W", mDisplayData->totalPower);
printText(mFmtText, l_TotalPower, 0xff);
} else {
printText("offline", l_TotalPower, 0xff);
}
}
if (showLine(l_YieldDay)) {
// print day yield
printText("\x88", l_YieldDay, 10); // day symbol
if (mDisplayData->totalYieldDay > 9999.0) if (mDisplayData->totalYieldDay > 9999.0)
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kWh", mDisplayData->totalYieldDay / 1000.0); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kWh", mDisplayData->totalYieldDay / 1000.0);
else else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f Wh", mDisplayData->totalYieldDay); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f Wh", mDisplayData->totalYieldDay);
printText(mFmtText, l_YieldDay, 0xff); printText(mFmtText, l_YieldDay, 0xff);
}
if (showLine(l_YieldTotal)) {
// print total yield
printText("\x83", l_YieldTotal, 10); // total symbol
if (mDisplayData->totalYieldTotal > 9999.0) if (mDisplayData->totalYieldTotal > 9999.0)
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f MWh", mDisplayData->totalYieldTotal / 1000.0); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f MWh", mDisplayData->totalYieldTotal / 1000.0);
else else
snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f kWh", mDisplayData->totalYieldTotal); snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f kWh", mDisplayData->totalYieldTotal);
printText(mFmtText, l_YieldTotal, 0xff); printText(mFmtText, l_YieldTotal, 0xff);
}
// draw dynamic Nokia RSSI bars if (mDispSwitchState == d_POWER_GRAPH) {
// plot power graph
plotPowerGraph(8, mLineYOffsets[graph_last_line] - 1);
}
// draw dynamic RSSI bars
int rssi_bar_height = 7; int rssi_bar_height = 7;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int radio_rssi_threshold = -60 - i*10; // radio rssi not yet tested in reality! int radio_rssi_threshold = -60 - i * 10;
int wifi_rssi_threshold = -60 - i * 10; int wifi_rssi_threshold = -60 - i * 10;
uint8_t barwidth = std::min(4 - i, 3);
if (mDisplayData->RadioRSSI > radio_rssi_threshold) if (mDisplayData->RadioRSSI > radio_rssi_threshold)
mDisplay->drawBox(0, 8+(rssi_bar_height+1)*i, 4-i,rssi_bar_height); mDisplay->drawBox(0, 8 + (rssi_bar_height + 1) * i, barwidth, rssi_bar_height);
if (mDisplayData->WifiRSSI > wifi_rssi_threshold) if (mDisplayData->WifiRSSI > wifi_rssi_threshold)
mDisplay->drawBox(mDispWidth-4+i, 8+(rssi_bar_height+1)*i, 4-i,rssi_bar_height); mDisplay->drawBox(mDispWidth - barwidth, 8 + (rssi_bar_height + 1) * i, barwidth, rssi_bar_height);
} }
// draw dynamic antenna and WiFi symbols // draw dynamic antenna and WiFi symbols
@ -139,6 +186,9 @@ class DisplayMono84X48 : public DisplayMono {
l_MAX_LINES = 5, l_MAX_LINES = 5,
}; };
uint8_t graph_first_line;
uint8_t graph_last_line;
void calcLinePositions() { void calcLinePositions() {
uint8_t yOff = 0; uint8_t yOff = 0;
uint8_t i = 0; uint8_t i = 0;
@ -158,7 +208,8 @@ class DisplayMono84X48 : public DisplayMono {
} }
inline void setLineFont(uint8_t line) { inline void setLineFont(uint8_t line) {
if ((line == l_TotalPower) || (line == l_Ahoy)) if ((line == l_TotalPower) ||
(line == l_Ahoy))
mDisplay->setFont(u8g2_font_logisoso16_tr); mDisplay->setFont(u8g2_font_logisoso16_tr);
else else
mDisplay->setFont(u8g2_font_5x8_symbols_ahoy); mDisplay->setFont(u8g2_font_5x8_symbols_ahoy);
@ -174,6 +225,10 @@ class DisplayMono84X48 : public DisplayMono {
dispX = col; dispX = col;
mDisplay->drawStr(dispX, mLineYOffsets[line], text); mDisplay->drawStr(dispX, mLineYOffsets[line], text);
} }
bool showLine(uint8_t line) {
return ((mDispSwitchState == d_POWER_TEXT) || ((line < graph_first_line) || (line > graph_last_line)));
}
}; };

2
src/plugins/Display/Display_data.h

@ -9,6 +9,8 @@ struct DisplayData {
float totalYieldDay=0.0f; // indicate day yield (Wh) float totalYieldDay=0.0f; // indicate day yield (Wh)
float totalYieldTotal=0.0f; // indicate total yield (kWh) float totalYieldTotal=0.0f; // indicate total yield (kWh)
uint32_t utcTs=0; // indicate absolute timestamp (utc unix time). 0 = time is not synchonized uint32_t utcTs=0; // indicate absolute timestamp (utc unix time). 0 = time is not synchonized
uint32_t pGraphStartTime=0; // starttime for power graph (e.g. sunRise)
uint32_t pGraphEndTime=0; // starttime for power graph (e.g. sunSet)
uint8_t nrProducing=0; // indicate number of producing inverters uint8_t nrProducing=0; // indicate number of producing inverters
uint8_t nrSleeping=0; // indicate number of sleeping inverters uint8_t nrSleeping=0; // indicate number of sleeping inverters
bool WifiSymbol = false; // indicate if WiFi is connected bool WifiSymbol = false; // indicate if WiFi is connected

2
src/web/RestApi.h

@ -676,6 +676,8 @@ class RestApi {
obj[F("disp_screensaver")] = (uint8_t)mConfig->plugin.display.screenSaver; obj[F("disp_screensaver")] = (uint8_t)mConfig->plugin.display.screenSaver;
obj[F("disp_rot")] = (uint8_t)mConfig->plugin.display.rot; obj[F("disp_rot")] = (uint8_t)mConfig->plugin.display.rot;
obj[F("disp_cont")] = (uint8_t)mConfig->plugin.display.contrast; obj[F("disp_cont")] = (uint8_t)mConfig->plugin.display.contrast;
obj[F("disp_graph_ratio")] = (uint8_t)mConfig->plugin.display.graph_ratio;
obj[F("disp_graph_size")] = (uint8_t)mConfig->plugin.display.graph_size;
obj[F("disp_clk")] = (mConfig->plugin.display.type == 0) ? DEF_PIN_OFF : mConfig->plugin.display.disp_clk; obj[F("disp_clk")] = (mConfig->plugin.display.type == 0) ? DEF_PIN_OFF : mConfig->plugin.display.disp_clk;
obj[F("disp_data")] = (mConfig->plugin.display.type == 0) ? DEF_PIN_OFF : mConfig->plugin.display.disp_data; obj[F("disp_data")] = (mConfig->plugin.display.type == 0) ? DEF_PIN_OFF : mConfig->plugin.display.disp_data;
obj[F("disp_cs")] = (mConfig->plugin.display.type < 3) ? DEF_PIN_OFF : mConfig->plugin.display.disp_cs; obj[F("disp_cs")] = (mConfig->plugin.display.type < 3) ? DEF_PIN_OFF : mConfig->plugin.display.disp_cs;

18
src/web/html/setup.html

@ -294,6 +294,12 @@
<p class="des">{#DISP_PINOUT}</p> <p class="des">{#DISP_PINOUT}</p>
<div id="dispPins"></div> <div id="dispPins"></div>
<div id="pirPin"></div> <div id="pirPin"></div>
<p class="des">Graph options</p>
<div id="graphSize"></div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Show ratio (0-100 %)</div>
<div class="col-12 col-sm-9"><input type="number" name="disp_graph_ratio" min="0" max="100"></select></div>
</div>
</fieldset> </fieldset>
</div> </div>
@ -1087,6 +1093,18 @@
hideDispPins(pins, parseInt(dtype_sel.value)) hideDispPins(pins, parseInt(dtype_sel.value))
}); });
document.getElementsByName("disp_graph_ratio")[0].value = obj["disp_graph_ratio"];
var opts2 = [[0, "Line 1 - 2"], [1, "Line 2 - 3"], [2, "Line 1 - 3"], [3, "Line 2 - 4"], [4, "Line 1 - 4"]];
var graph_size_sel = sel("disp_graph_size", opts2, obj["disp_graph_size"]);
graph_size_sel.id = 'disp_graph_size';
document.getElementById("graphSize").append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Graph size"),
ml("div", {class: "col-12 col-sm-9"}, graph_size_sel)
])
);
hideDispPins(pins, obj.disp_typ); hideDispPins(pins, obj.disp_typ);
} }

2
src/web/web.h

@ -584,6 +584,8 @@ class Web {
// display // display
mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("disp_pwr") == "on"); mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("disp_pwr") == "on");
mConfig->plugin.display.screenSaver = request->arg("disp_screensaver").toInt(); mConfig->plugin.display.screenSaver = request->arg("disp_screensaver").toInt();
mConfig->plugin.display.graph_ratio = request->arg("disp_graph_ratio").toInt();
mConfig->plugin.display.graph_size = request->arg("disp_graph_size").toInt();
mConfig->plugin.display.rot = request->arg("disp_rot").toInt(); mConfig->plugin.display.rot = request->arg("disp_rot").toInt();
mConfig->plugin.display.type = request->arg("disp_typ").toInt(); mConfig->plugin.display.type = request->arg("disp_typ").toInt();
mConfig->plugin.display.contrast = (mConfig->plugin.display.type == 0) ? 60 : request->arg("disp_cont").toInt(); mConfig->plugin.display.contrast = (mConfig->plugin.display.type == 0) ? 60 : request->arg("disp_cont").toInt();

Loading…
Cancel
Save