# 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 RADIO >
class Display {
public :
Display ( ) {
mMono = NULL ;
}
void setup ( IApp * app , display_t * cfg , HMSYSTEM * sys , RADIO * hmradio , RADIO * hmsradio , uint32_t * utcTs ) {
mApp = app ;
mHmRadio = hmradio ;
mHmsRadio = hmsradio ;
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 ; // None
case 1 : mMono = new DisplayMono128X64 ( ) ; break ; // SSD1306_128X64 (0.96", 1.54")
case 2 : mMono = new DisplayMono128X64 ( ) ; break ; // SH1106_128X64 (1.3")
case 3 : mMono = new DisplayMono84X48 ( ) ; break ; // PCD8544_84X48 (1.6" - Nokia 5110)
case 4 : mMono = new DisplayMono128X32 ( ) ; break ; // SSD1306_128X32 (0.91")
case 5 : mMono = new DisplayMono64X48 ( ) ; break ; // SSD1306_64X48 (0.66" - Wemos OLED Shield)
case 6 : mMono = new DisplayMono128X64 ( ) ; break ; // SSD1309_128X64 (2.42")
# 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 ;
bool nrf_en = mApp - > getNrfEnabled ( ) ;
bool nrf_ok = nrf_en & & mHmRadio - > isChipConnected ( ) ;
# if defined(ESP32)
bool cmt_en = mApp - > getCmtEnabled ( ) ;
bool cmt_ok = cmt_en & & mHmsRadio - > isChipConnected ( ) ;
# else
bool cmt_en = false ;
bool cmt_ok = false ;
# endif
mDisplayData . RadioSymbol = ( nrf_ok & & ! cmt_en ) | | ( cmt_ok & & ! nrf_en ) | | ( nrf_ok & & cmt_ok ) ;
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 ;
RADIO * mHmRadio ;
RADIO * mHmsRadio ;
uint16_t mRefreshCycle ;
# if defined(ESP32)
DisplayEPaper mEpaper ;
# endif
DisplayMono * mMono ;
} ;
# endif /*__DISPLAY__*/