Skip to content

Commit

Permalink
Merge branch 'feature/PR_display_graph_0_8_46' of https://github.com/…
Browse files Browse the repository at this point in the history
…You69Man/ahoy into You69Man-feature/PR_display_graph_0_8_46
  • Loading branch information
lumapu committed Jan 7, 2024
2 parents 7e08e6c + c82cc1c commit ac70ada
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 138 deletions.
8 changes: 8 additions & 0 deletions src/config/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ typedef struct {
uint8_t type;
bool pwrSaveAtIvOffline;
uint8_t screenSaver;
uint8_t graph_ratio;
uint8_t graph_size;
uint8_t rot;
//uint16_t wakeUp;
//uint16_t sleepAt;
Expand Down Expand Up @@ -461,6 +463,8 @@ class settings {
mCfg.plugin.display.pwrSaveAtIvOffline = false;
mCfg.plugin.display.contrast = 60;
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.disp_data = DEF_PIN_OFF; // SDA
mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL
Expand Down Expand Up @@ -697,6 +701,8 @@ class settings {
disp[F("type")] = mCfg.plugin.display.type;
disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline;
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("wake")] = mCfg.plugin.display.wakeUp;
//disp[F("sleep")] = mCfg.plugin.display.sleepAt;
Expand All @@ -713,6 +719,8 @@ class settings {
getVal<uint8_t>(disp, F("type"), &mCfg.plugin.display.type);
getVal<bool>(disp, F("pwrSafe"), &mCfg.plugin.display.pwrSaveAtIvOffline);
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);
//mCfg.plugin.display.wakeUp = disp[F("wake")];
//mCfg.plugin.display.sleepAt = disp[F("sleep")];
Expand Down
11 changes: 8 additions & 3 deletions src/plugins/Display/Display.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Display {
default: mMono = NULL; break;
}
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);
}

Expand All @@ -75,10 +75,12 @@ class Display {
}

void tickerSecond() {
bool request_refresh = false;

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();
mNewPayload = false;
mLoopCnt = 0;
Expand Down Expand Up @@ -165,6 +167,9 @@ class Display {
else
mDisplayData.utcTs = 0;

mDisplayData.pGraphStartTime = mApp->getSunrise();
mDisplayData.pGraphEndTime = mApp->getSunset();

if (mMono ) {
mMono->disp();
}
Expand Down
161 changes: 159 additions & 2 deletions src/plugins/Display/Display_Mono.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ class 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 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;

// Common loop function, manages display on/off functions for powersave and screensaver with motionsensor
// 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)) &&
((mScreenSaver != 2) || motion); // screensaver 2 .. motionsensor
Expand Down Expand Up @@ -60,19 +60,32 @@ class DisplayMono {
mLuminance = lum;
mDisplay->setContrast(mLuminance);
}

return(monoMaintainDispSwitchState());
}

protected:
U8G2* mDisplay;
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;
uint16_t mDispWidth;
uint16_t mDispHeight;

bool mEnPowerSave;
uint8_t mScreenSaver = 1; // 0 .. off; 1 .. pixelShift; 2 .. motionsensor
uint8_t mLuminance;
uint8_t mGraphRatio;
uint8_t mGraphSize;

uint8_t mLoopCnt;
uint8_t mLineXOffsets[5] = {};
Expand All @@ -81,9 +94,16 @@ class DisplayMono {
uint8_t mExtra;
int8_t mPixelshift=0;
TimeMonitor mDisplayTime = TimeMonitor(1000 * DISP_DEFAULT_TIMEOUT, true);
TimeMonitor mDispSwitchTime = TimeMonitor();
uint8_t mDispSwitchState;
bool mDisplayActive = true; // always start with display on
char mFmtText[DISP_FMT_TEXT_LEN];

enum _dispSwitchState {
d_POWER_TEXT = 0,
d_POWER_GRAPH = 1,
};

// Common initialization function to be called by subclasses
void monoInit(U8G2* display, uint8_t type, DisplayData *displayData) {
mDisplay = display;
Expand All @@ -95,8 +115,145 @@ class DisplayMono {
mDisplay->clearBuffer();
mDispWidth = mDisplay->getDisplayWidth();
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) {
int8_t mod = (millis() / 10000) % ((range >> 1) << 2);
mPixelshift = mScreenSaver == 1 ? ((mod < range) ? mod - (range >> 1) : -(mod - range - (range >> 1) + 1)) : 0;
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/Display/Display_Mono_128X32.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class DisplayMono128X32 : public DisplayMono {
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;
mScreenSaver = screenSaver;
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) {
Expand Down
Loading

0 comments on commit ac70ada

Please sign in to comment.