You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
6.8 KiB

#ifndef __DISPLAY__
#define __DISPLAY__
#include <Timezone.h>
#include <U8g2lib.h>
#include "../../hm/hmSystem.h"
#include "../../hm/hmRadio.h"
#include "../../utils/helper.h"
#include "Display_Mono.h"
#include "Display_Mono_128X32.h"
#include "Display_Mono_128X64.h"
#include "Display_Mono_84X48.h"
#include "Display_Mono_64X48.h"
#include "Display_ePaper.h"
#include "Display_data.h"
template <class HMSYSTEM, class HMRADIO>
class Display {
public:
Display() {
mMono = NULL;
}
void setup(IApp *app, display_t *cfg, HMSYSTEM *sys, HMRADIO *radio, uint32_t *utcTs) {
mApp = app;
mHmRadio = radio;
mCfg = cfg;
mSys = sys;
mUtcTs = utcTs;
mNewPayload = false;
mLoopCnt = 0;
mDisplayData.version = app->getVersion(); // version never changes, so only set once
switch (mCfg->type) {
case 0: mMono = NULL; break;
case 1: // fall-through
case 2: mMono = new DisplayMono128X64(); break;
case 3: mMono = new DisplayMono84X48(); break;
case 4: mMono = new DisplayMono128X32(); break;
case 5: mMono = new DisplayMono64X48(); break;
case 6: mMono = new DisplayMono128X64(); break;
#if defined(ESP32)
case 10:
mMono = NULL; // ePaper does not use this
mRefreshCycle = 0;
mEpaper.config(mCfg->rot, mCfg->pwrSaveAtIvOffline);
mEpaper.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mDisplayData.version);
break;
#endif
default: mMono = NULL; break;
}
if(mMono) {
mMono->config(mCfg->pwrSaveAtIvOffline, mCfg->screenSaver, mCfg->contrast);
mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, &mDisplayData);
}
// setup PIR pin for motion sensor
#ifdef ESP32
if ((mCfg->screenSaver == 2) && (mCfg->pirPin != DEF_PIN_OFF))
pinMode(mCfg->pirPin, INPUT);
#endif
#ifdef ESP8266
if ((mCfg->screenSaver == 2) && (mCfg->pirPin != DEF_PIN_OFF) && (mCfg->pirPin != A0))
pinMode(mCfg->pirPin, INPUT);
#endif
}
void payloadEventListener(uint8_t cmd) {
mNewPayload = true;
}
void tickerSecond() {
if (mMono != NULL)
mMono->loop(mCfg->contrast, motionSensorActive());
if (mNewPayload || (((++mLoopCnt) % 5) == 0)) {
DataScreen();
mNewPayload = false;
mLoopCnt = 0;
}
#if defined(ESP32)
mEpaper.tickerSecond();
#endif
}
private:
void DataScreen() {
if (mCfg->type == 0)
return;
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
uint8_t nrprod = 0;
uint8_t nrsleep = 0;
int8_t minQAllInv = 4;
Inverter<> *iv;
record_t<> *rec;
bool allOff = true;
uint8_t nInv = mSys->getNumInverters();
for (uint8_t i = 0; i < nInv; i++) {
iv = mSys->getInverterByPos(i);
if (iv == NULL)
continue;
int8_t maxQInv = -6;
for(uint8_t ch = 0; ch < RF_MAX_CHANNEL_ID; ch++) {
int8_t q = iv->txRfQuality[ch];
if (q > maxQInv)
maxQInv = q;
}
if (maxQInv < minQAllInv)
minQAllInv = maxQInv;
rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv->isProducing())
nrprod++;
else
nrsleep++;
totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec);
totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec);
totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec);
if(allOff) {
if(iv->status != InverterStatus::OFF)
allOff = false;
}
}
// prepare display data
mDisplayData.nrProducing = nrprod;
mDisplayData.nrSleeping = nrsleep;
mDisplayData.totalPower = (allOff) ? 0.0 : totalPower; // if all inverters are off, total power can't be greater than 0
mDisplayData.totalYieldDay = totalYieldDay;
mDisplayData.totalYieldTotal = totalYieldTotal;
mDisplayData.RadioSymbol = mHmRadio->isChipConnected();
mDisplayData.WifiSymbol = (WiFi.status() == WL_CONNECTED);
mDisplayData.MQTTSymbol = mApp->getMqttIsConnected();
mDisplayData.RadioRSSI = (0 < mDisplayData.nrProducing) ? ivQuality2RadioRSSI(minQAllInv) : SCHAR_MIN; // Workaround as NRF24 has no RSSI. Approximation by quality levels from heuristic function
mDisplayData.WifiRSSI = (WiFi.status() == WL_CONNECTED) ? WiFi.RSSI() : SCHAR_MIN;
mDisplayData.ipAddress = WiFi.localIP();
time_t utc= mApp->getTimestamp();
if (year(utc) > 2020)
mDisplayData.utcTs = utc;
else
mDisplayData.utcTs = 0;
if (mMono ) {
mMono->disp();
}
#if defined(ESP32)
else if (mCfg->type == 10) {
mEpaper.loop(((allOff) ? 0.0 : totalPower), totalYieldDay, totalYieldTotal, nrprod);
mRefreshCycle++;
}
if (mRefreshCycle > 480) {
mEpaper.fullRefresh();
mRefreshCycle = 0;
}
#endif
}
bool motionSensorActive() {
if ((mCfg->screenSaver == 2) && (mCfg->pirPin != DEF_PIN_OFF)) {
#if defined(ESP8266)
if (mCfg->pirPin == A0)
return((analogRead(A0) >= 512));
else
return(digitalRead(mCfg->pirPin));
#elif defined(ESP32)
return(digitalRead(mCfg->pirPin));
#endif
}
else
return(false);
}
// approximate RSSI in dB by invQuality levels from heuristic function (very unscientific but better than nothing :-) )
int8_t ivQuality2RadioRSSI(int8_t invQuality) {
int8_t pseudoRSSIdB;
switch(invQuality) {
case 4: pseudoRSSIdB = -55; break;
case 3:
case 2:
case 1: pseudoRSSIdB = -65; break;
case 0:
case -1:
case -2: pseudoRSSIdB = -75; break;
case -3:
case -4:
case -5: pseudoRSSIdB = -85; break;
case -6:
default: pseudoRSSIdB = -95; break;
}
return (pseudoRSSIdB);
}
// private member variables
IApp *mApp;
DisplayData mDisplayData;
bool mNewPayload;
uint8_t mLoopCnt;
uint32_t *mUtcTs;
display_t *mCfg;
HMSYSTEM *mSys;
HMRADIO *mHmRadio;
uint16_t mRefreshCycle;
#if defined(ESP32)
DisplayEPaper mEpaper;
#endif
DisplayMono *mMono;
};
#endif /*__DISPLAY__*/