@ -22,256 +22,255 @@
# include "../../utils/timemonitor.h"
class DisplayMono {
public :
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 , 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 bool loop ( uint8_t lum , bool motion ) {
bool dispConditions = ( ! mEnPowerSave | | ( mDisplayData - > nrProducing > 0 ) ) & &
( ( mScreenSaver ! = 2 ) | | motion ) ; // screensaver 2 .. motionsensor
if ( mDisplayActive ) {
if ( ! dispConditions ) {
if ( mDisplayTime . isTimeout ( ) ) { // switch display off after timeout
mDisplayActive = false ;
mDisplay - > setPowerSave ( true ) ;
DBGPRINTLN ( " **** Display off **** " ) ;
public :
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 , 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 bool loop ( uint8_t lum , bool motion ) {
bool dispConditions = ( ! mEnPowerSave | | ( mDisplayData - > nrProducing > 0 ) ) & &
( ( mScreenSaver ! = 2 ) | | motion ) ; // screensaver 2 .. motionsensor
if ( mDisplayActive ) {
if ( ! dispConditions ) {
if ( mDisplayTime . isTimeout ( ) ) { // switch display off after timeout
mDisplayActive = false ;
mDisplay - > setPowerSave ( true ) ;
DBGPRINTLN ( " **** Display off **** " ) ;
}
}
else
mDisplayTime . reStartTimeMonitor ( ) ; // keep display on
}
else
mDisplayTime . reStartTimeMonitor ( ) ; // keep display on
}
else {
if ( dispConditions ) {
mDisplayActive = true ;
mDisplayTime . reStartTimeMonitor ( ) ; // switch display on
mDisplay - > setPowerSave ( false ) ;
DBGPRINTLN ( " **** Display on **** " ) ;
else {
if ( dispConditions ) {
mDisplayActive = true ;
mDisplayTime . reStartTimeMonitor ( ) ; // switch display on
mDisplay - > setPowerSave ( false ) ;
DBGPRINTLN ( " **** Display on **** " ) ;
}
}
if ( mLuminance ! = lum ) {
mLuminance = lum ;
mDisplay - > setContrast ( mLuminance ) ;
}
}
if ( mLuminance ! = lum ) {
mLuminance = lum ;
return ( monoMaintainDispSwitchState ( ) ) ;
}
protected :
enum class DispSwitchState {
TEXT ,
GRAPH
} ;
protected :
U8G2 * mDisplay ;
DisplayData * mDisplayData ;
float * mPgData = nullptr ;
uint8_t mPgWidth = 0 ;
uint8_t mPgHeight = 0 ;
float mPgMaxPwr = 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 ] = { } ;
uint8_t mLineYOffsets [ 5 ] = { } ;
uint8_t mExtra ;
int8_t mPixelshift = 0 ;
TimeMonitor mDisplayTime = TimeMonitor ( 1000 * DISP_DEFAULT_TIMEOUT , true ) ;
TimeMonitor mDispSwitchTime = TimeMonitor ( ) ;
DispSwitchState mDispSwitchState = DispSwitchState : : TEXT ;
bool mDisplayActive = true ; // always start with display on
char mFmtText [ DISP_FMT_TEXT_LEN ] ;
// Common initialization function to be called by subclasses
void monoInit ( U8G2 * display , uint8_t type , DisplayData * displayData ) {
mDisplay = display ;
mType = type ;
mDisplayData = displayData ;
mDisplay - > begin ( ) ;
mDisplay - > setPowerSave ( false ) ; // always start with display on
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 ] = { } ;
uint8_t mLineYOffsets [ 5 ] = { } ;
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 ;
mType = type ;
mDisplayData = displayData ;
mDisplay - > begin ( ) ;
mDisplay - > setPowerSave ( false ) ; // always start with display on
mDisplay - > setContrast ( mLuminance ) ;
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 ( ) ;
mDisplay - > clearBuffer ( ) ;
mDispWidth = mDisplay - > getDisplayWidth ( ) ;
mDispHeight = mDisplay - > getDisplayHeight ( ) ;
mDispSwitchTime . stopTimeMonitor ( ) ;
if ( mGraphRatio = = 100 ) // if graph ratio is 100% start in graph mode
mDispSwitchState = DispSwitchState : : 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 DispSwitchState : : TEXT :
if ( mDispSwitchTime . isTimeout ( ) ) {
mDispSwitchState = DispSwitchState : : GRAPH ;
mDispSwitchTime . startTimeMonitor ( 150 * mGraphRatio ) ; // mGraphRatio: 0-100 Gesamtperiode 15000 ms
change = true ;
}
break ;
case DispSwitchState : : GRAPH :
if ( mDispSwitchTime . isTimeout ( ) ) {
mDispSwitchState = DispSwitchState : : 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 ] ;
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 ;
DBGPRINTLN ( " max. Power = " + String ( mPgMaxAvailPower ) ) ; */
}
}
uint8_t sss2pgpos ( uint seconds_since_start ) {
return ( seconds_since_start * ( mPgWidth - 1 ) / ( mDisplayData - > pGraphEndTime - mDisplayData - > pGraphStartTime ) ) ;
}
void resetPowerGraph ( ) {
if ( mPgData ! = nullptr ) {
mPgMaxPwr = 0.0 ;
mPgLastPos = 0 ;
for ( uint8_t i = 0 ; i < mPgWidth ; i + + )
mPgData [ i ] = 0.0 ;
}
}
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 ) ;
uint8_t sss2pgpos ( uint seconds_since_start ) {
return ( seconds_since_start * ( mPgWidth - 1 ) / ( mDisplayData - > pGraphEndTime - mDisplayData - > pGraphStartTime ) ) ;
}
// 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 ) ;
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
}
}
// 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 ) ) ;
uint8_t getPowerGraphXpos ( uint8_t p ) { //
if ( ( p < = mPgLastPos ) & & ( mPgLastPos > 0 ) )
return ( ( p * ( mPgWidth - 1 ) ) / mPgLastPos ) ; // scaling of x-axis
else
return ( 0 ) ;
}
// 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 ;
}
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 ;
}
} ;
/* adapted 5x8 Font for low-res displays with symbols
Symbols :
\ x80 . . . antenna
\ x81 . . . WiFi
\ x82 . . . suncurve
\ x83 . . . sum / sigma
\ x84 . . . antenna crossed
\ x85 . . . WiFi crossed
\ x86 . . . sun
\ x87 . . . moon
\ x88 . . . calendar / day
\ x89 . . . MQTT */
\ x80 . . . antenna
\ x81 . . . WiFi
\ x82 . . . suncurve
\ x83 . . . sum / sigma
\ x84 . . . antenna crossed
\ x85 . . . WiFi crossed
\ x86 . . . sun
\ x87 . . . moon
\ x88 . . . calendar / day
\ x89 . . . MQTT */
const uint8_t u8g2_font_5x8_symbols_ahoy [ 1049 ] U8G2_FONT_SECTION ( " u8g2_font_5x8_symbols_ahoy " ) =
" j \0 \3 \2 \4 \4 \3 \4 \5 \10 \10 \0 \377 \6 \377 \6 \0 \1 \61 \2 b \4 \0 \5 \0 \304 \11 ! \7 a \306 "
" \212 ! \11 \42 \7 \63 \335 \212 \304 \22 # \16 u \304 \232 R \222 \14 JePJI \2 $ \14 u \304 \252 l \251 m "