Feature: Added fullscreen diagram to display
This commit is contained in:
parent
637d4f06a3
commit
f013698471
@ -6,6 +6,14 @@
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#define CHART_HEIGHT 20 // chart area hight in pixels
|
||||
#define CHART_WIDTH 47 // chart area width in pixels
|
||||
|
||||
// Left-Upper position of diagram is drawn
|
||||
// (text of Y-axis is display left of that pos)
|
||||
#define CHART_POSX 80
|
||||
#define CHART_POSY 0
|
||||
|
||||
enum DisplayType_t {
|
||||
None,
|
||||
PCD8544,
|
||||
@ -18,6 +26,7 @@ enum DisplayType_t {
|
||||
enum DiagramMode_t {
|
||||
Off,
|
||||
Small,
|
||||
Fullscreen,
|
||||
DisplayMode_Max,
|
||||
};
|
||||
|
||||
@ -57,8 +66,8 @@ private:
|
||||
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
||||
uint8_t _display_language = DISPLAY_LANGUAGE;
|
||||
uint8_t _mExtra;
|
||||
uint16_t _period = 1000;
|
||||
uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||
const uint16_t _period = 1000;
|
||||
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||
uint32_t _previousMillis = 0;
|
||||
char _fmtText[32];
|
||||
bool _isLarge = false;
|
||||
|
||||
@ -5,20 +5,14 @@
|
||||
#include <U8g2lib.h>
|
||||
#include <array>
|
||||
|
||||
#define CHART_HEIGHT 20 // chart area hight in pixels
|
||||
#define CHART_WIDTH 47 // chart area width in pixels
|
||||
|
||||
// Left-Upper position of diagram is drawn
|
||||
// (text of Y-axis is display left of that pos)
|
||||
#define DIAG_POSX 80
|
||||
#define DIAG_POSY 0
|
||||
#define MAX_DATAPOINTS 128
|
||||
|
||||
class DisplayGraphicDiagramClass {
|
||||
public:
|
||||
DisplayGraphicDiagramClass();
|
||||
|
||||
void init(Scheduler& scheduler, U8G2* display);
|
||||
void redraw(uint8_t screenSaverOffsetX);
|
||||
void redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen);
|
||||
|
||||
void updatePeriod();
|
||||
|
||||
@ -26,15 +20,17 @@ private:
|
||||
void averageLoop();
|
||||
void dataPointLoop();
|
||||
|
||||
static uint32_t getSecondsPerDot();
|
||||
uint32_t getSecondsPerDot();
|
||||
|
||||
Task _averageTask;
|
||||
Task _dataPointTask;
|
||||
|
||||
U8G2* _display = nullptr;
|
||||
std::array<float, CHART_WIDTH> _graphValues = {};
|
||||
std::array<float, MAX_DATAPOINTS> _graphValues = {};
|
||||
uint8_t _graphValuesCount = 0;
|
||||
|
||||
uint8_t _chartWidth = MAX_DATAPOINTS;
|
||||
|
||||
float _iRunningAverage = 0;
|
||||
uint16_t _iRunningAverageCnt = 0;
|
||||
};
|
||||
|
||||
@ -28,8 +28,8 @@ const uint8_t languages[] = {
|
||||
};
|
||||
|
||||
static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" };
|
||||
static const char* const i18n_current_power_w[] = { "%3.0f W", "%3.0f W", "%3.0f W" };
|
||||
static const char* const i18n_current_power_kw[] = { "%2.1f kW", "%2.1f kW", "%2.1f kW" };
|
||||
static const char* const i18n_current_power_w[] = { "%.0f W", "%.0f W", "%.0f W" };
|
||||
static const char* const i18n_current_power_kw[] = { "%.1f kW", "%.1f kW", "%.1f kW" };
|
||||
static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" };
|
||||
static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" };
|
||||
static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" };
|
||||
@ -94,18 +94,31 @@ bool DisplayGraphicClass::isValidDisplay()
|
||||
|
||||
void DisplayGraphicClass::printText(const char* text, const uint8_t line)
|
||||
{
|
||||
setFont(line);
|
||||
|
||||
uint8_t dispX;
|
||||
if (!_isLarge) {
|
||||
dispX = (line == 0) ? 5 : 0;
|
||||
} else {
|
||||
switch (line) {
|
||||
case 0:
|
||||
if (_diagram_mode == DiagramMode_t::Small) {
|
||||
dispX = (line == 0) ? 10 : 5;
|
||||
// Center between left border and diagram
|
||||
dispX = (CHART_POSX - _display->getStrWidth(text)) / 2;
|
||||
} else {
|
||||
dispX = (line == 0) ? 20 : 5;
|
||||
// Center on screen
|
||||
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// Center on screen
|
||||
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
|
||||
break;
|
||||
default:
|
||||
dispX = 5;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
setFont(line);
|
||||
|
||||
dispX += enableScreensaver ? (_mExtra % 7) : 0;
|
||||
_display->drawStr(dispX, _lineOffsets[line], text);
|
||||
@ -170,14 +183,29 @@ void DisplayGraphicClass::loop()
|
||||
|
||||
_display->clearBuffer();
|
||||
bool displayPowerSave = false;
|
||||
bool showText = true;
|
||||
|
||||
//=====> Actual Production ==========
|
||||
if (Datastore.getIsAtLeastOneReachable()) {
|
||||
displayPowerSave = false;
|
||||
if (_isLarge && _diagram_mode == DiagramMode_t::Small) {
|
||||
if (_isLarge) {
|
||||
uint8_t screenSaverOffsetX = enableScreensaver ? (_mExtra % 7) : 0;
|
||||
_diagram.redraw(screenSaverOffsetX);
|
||||
switch (_diagram_mode) {
|
||||
case DiagramMode_t::Small:
|
||||
_diagram.redraw(screenSaverOffsetX, CHART_POSX, CHART_POSY, CHART_WIDTH, CHART_HEIGHT, false);
|
||||
break;
|
||||
case DiagramMode_t::Fullscreen:
|
||||
// Every 10 seconds
|
||||
if (_mExtra % (10 * 2) < 10) {
|
||||
_diagram.redraw(screenSaverOffsetX, 10, 0, _display->getDisplayWidth() - 12, _display->getDisplayHeight() - 3, true);
|
||||
showText = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (showText) {
|
||||
const float watts = Datastore.getTotalAcPowerEnabled();
|
||||
if (watts > 999) {
|
||||
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], watts / 1000);
|
||||
@ -185,6 +213,7 @@ void DisplayGraphicClass::loop()
|
||||
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], watts);
|
||||
}
|
||||
printText(_fmtText, 0);
|
||||
}
|
||||
_previousMillis = millis();
|
||||
}
|
||||
//<=======================
|
||||
@ -199,6 +228,7 @@ void DisplayGraphicClass::loop()
|
||||
}
|
||||
//<=======================
|
||||
|
||||
if (showText) {
|
||||
//=====> Today & Total Production =======
|
||||
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
|
||||
printText(_fmtText, 1);
|
||||
@ -208,7 +238,8 @@ void DisplayGraphicClass::loop()
|
||||
//<=======================
|
||||
|
||||
//=====> IP or Date-Time ========
|
||||
if (!(_mExtra % 10) && NetworkSettings.localIP()) {
|
||||
// Change every 3 seconds
|
||||
if (!(_mExtra % (3 * 2) < 3) && NetworkSettings.localIP()) {
|
||||
printText(NetworkSettings.localIP().toString().c_str(), 3);
|
||||
} else {
|
||||
// Get current time
|
||||
@ -216,6 +247,8 @@ void DisplayGraphicClass::loop()
|
||||
strftime(_fmtText, sizeof(_fmtText), i18n_date_format[_display_language], localtime(&now));
|
||||
printText(_fmtText, 3);
|
||||
}
|
||||
}
|
||||
|
||||
_display->sendBuffer();
|
||||
|
||||
_mExtra++;
|
||||
|
||||
@ -52,38 +52,39 @@ void DisplayGraphicDiagramClass::dataPointLoop()
|
||||
|
||||
uint32_t DisplayGraphicDiagramClass::getSecondsPerDot()
|
||||
{
|
||||
return Configuration.get().Display.Diagram.Duration / CHART_WIDTH;
|
||||
return Configuration.get().Display.Diagram.Duration / _chartWidth;
|
||||
}
|
||||
|
||||
void DisplayGraphicDiagramClass::updatePeriod()
|
||||
{
|
||||
_dataPointTask.setInterval(getSecondsPerDot() * TASK_SECOND);
|
||||
// Calculate seconds per datapoint
|
||||
_dataPointTask.setInterval(Configuration.get().Display.Diagram.Duration * TASK_SECOND / MAX_DATAPOINTS );
|
||||
}
|
||||
|
||||
void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX)
|
||||
void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen)
|
||||
{
|
||||
// screenSaverOffsetX expected to be in range 0..6
|
||||
const uint8_t graphPosX = DIAG_POSX + ((screenSaverOffsetX > 3) ? 1 : 0);
|
||||
const uint8_t graphPosY = DIAG_POSY + ((screenSaverOffsetX > 3) ? 1 : 0);
|
||||
_chartWidth = width;
|
||||
|
||||
const uint8_t horizontal_line_y = graphPosY + CHART_HEIGHT - 1;
|
||||
// screenSaverOffsetX expected to be in range 0..6
|
||||
const uint8_t graphPosX = xPos + ((screenSaverOffsetX > 3) ? 1 : 0);
|
||||
const uint8_t graphPosY = yPos + ((screenSaverOffsetX > 3) ? 1 : 0);
|
||||
|
||||
const uint8_t horizontal_line_y = graphPosY + height - 1;
|
||||
const uint8_t arrow_size = 2;
|
||||
|
||||
// draw diagram axis
|
||||
_display->drawVLine(graphPosX, graphPosY, CHART_HEIGHT);
|
||||
_display->drawHLine(graphPosX, horizontal_line_y, CHART_WIDTH);
|
||||
_display->drawVLine(graphPosX, graphPosY, height);
|
||||
_display->drawHLine(graphPosX, horizontal_line_y, width);
|
||||
|
||||
// UP-arrow
|
||||
_display->drawLine(graphPosX, graphPosY, graphPosX + arrow_size, graphPosY + arrow_size);
|
||||
_display->drawLine(graphPosX, graphPosY, graphPosX - arrow_size, graphPosY + arrow_size);
|
||||
|
||||
// LEFT-arrow
|
||||
_display->drawLine(graphPosX + CHART_WIDTH - 1, horizontal_line_y, graphPosX + CHART_WIDTH - 1 - arrow_size, horizontal_line_y - arrow_size);
|
||||
_display->drawLine(graphPosX + CHART_WIDTH - 1, horizontal_line_y, graphPosX + CHART_WIDTH - 1 - arrow_size, horizontal_line_y + arrow_size);
|
||||
_display->drawLine(graphPosX + width - 1, horizontal_line_y, graphPosX + width - 1 - arrow_size, horizontal_line_y - arrow_size);
|
||||
_display->drawLine(graphPosX + width - 1, horizontal_line_y, graphPosX + width - 1 - arrow_size, horizontal_line_y + arrow_size);
|
||||
|
||||
// draw AC value
|
||||
// 4 pixels per char
|
||||
_display->setFont(u8g2_font_tom_thumb_4x6_mr);
|
||||
char fmtText[7];
|
||||
const float maxWatts = *std::max_element(_graphValues.begin(), _graphValues.end());
|
||||
if (maxWatts > 999) {
|
||||
@ -91,25 +92,46 @@ void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX)
|
||||
} else {
|
||||
snprintf(fmtText, sizeof(fmtText), "%dW", static_cast<uint16_t>(maxWatts));
|
||||
}
|
||||
const uint8_t textLength = strlen(fmtText);
|
||||
_display->drawStr(graphPosX - arrow_size - textLength * 4, graphPosY + 5, fmtText);
|
||||
|
||||
// draw chart
|
||||
const float scaleFactor = maxWatts / CHART_HEIGHT;
|
||||
uint8_t axisTick = 1;
|
||||
for (uint8_t i = 1; i < _graphValuesCount; i++) {
|
||||
// draw one tick per hour to the x-axis
|
||||
if (i * getSecondsPerDot() > (3600u * axisTick)) {
|
||||
_display->drawPixel(graphPosX + 1 + i, graphPosY + CHART_HEIGHT);
|
||||
axisTick++;
|
||||
if (isFullscreen) {
|
||||
_display->setFont(u8g2_font_5x8_tr);
|
||||
_display->setFontDirection(3);
|
||||
_display->drawStr(graphPosX - arrow_size, graphPosY + _display->getStrWidth(fmtText), fmtText);
|
||||
_display->setFontDirection(0);
|
||||
} else {
|
||||
// 4 pixels per char
|
||||
_display->setFont(u8g2_font_tom_thumb_4x6_mr);
|
||||
_display->drawStr(graphPosX - arrow_size - _display->getStrWidth(fmtText), graphPosY + 5, fmtText);
|
||||
}
|
||||
|
||||
if (scaleFactor == 0) {
|
||||
// draw chart
|
||||
const float scaleFactorY = maxWatts / static_cast<float>(height);
|
||||
const float scaleFactorX = static_cast<float>(MAX_DATAPOINTS) / static_cast<float>(_chartWidth);
|
||||
|
||||
if (maxWatts > 0 && isFullscreen) {
|
||||
// draw y axis ticks
|
||||
const uint16_t yAxisWattPerTick = maxWatts <= 100 ? 10 : maxWatts <= 1000 ? 100 : maxWatts < 5000 ? 500 : 1000;
|
||||
const uint8_t yAxisTickSizePixel = height / (maxWatts / yAxisWattPerTick);
|
||||
|
||||
for (int16_t tickYPos = graphPosY + height; tickYPos > graphPosY - arrow_size; tickYPos -= yAxisTickSizePixel) {
|
||||
_display->drawPixel(graphPosX - 1, tickYPos);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t xAxisTicks = 1;
|
||||
for (uint8_t i = 1; i < _graphValuesCount; i++) {
|
||||
// draw one tick per hour to the x-axis
|
||||
if (i * getSecondsPerDot() > (3600u * xAxisTicks)) {
|
||||
_display->drawPixel((graphPosX + 1 + i) * scaleFactorX, graphPosY + height);
|
||||
xAxisTicks++;
|
||||
}
|
||||
|
||||
if (scaleFactorY == 0 || scaleFactorX == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_display->drawLine(
|
||||
graphPosX + i - 1, horizontal_line_y - std::max<int16_t>(0, _graphValues[i - 1] / scaleFactor - 0.5),
|
||||
graphPosX + i, horizontal_line_y - std::max<int16_t>(0, _graphValues[i] / scaleFactor - 0.5));
|
||||
graphPosX + (i - 1) / scaleFactorX, horizontal_line_y - std::max<int16_t>(0, _graphValues[i - 1] / scaleFactorY - 0.5),
|
||||
graphPosX + i / scaleFactorX, horizontal_line_y - std::max<int16_t>(0, _graphValues[i] / scaleFactorY - 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,9 +582,10 @@
|
||||
"PowerSafeHint": "Schaltet das Display aus, wenn kein Wechselrichter Strom erzeugt",
|
||||
"Screensaver": "Bildschirmschoner aktivieren:",
|
||||
"ScreensaverHint": "Bewegt die Ausgabe bei jeder Aktualisierung um ein Einbrennen zu verhindern (v. a. für OLED-Displays nützlich)",
|
||||
"DiagramMode": "Diagram Modus:",
|
||||
"DiagramMode": "Diagramm Modus:",
|
||||
"off": "Deaktiviert",
|
||||
"small": "Klein",
|
||||
"fullscreen": "Vollbild",
|
||||
"DiagramDuration": "Diagramm Periode:",
|
||||
"DiagramDurationHint": "Die Zeitperiode welche im Diagramm dargestellt wird.",
|
||||
"Seconds": "Sekunden",
|
||||
|
||||
@ -585,6 +585,7 @@
|
||||
"DiagramMode": "Diagram mode:",
|
||||
"off": "Off",
|
||||
"small": "Small",
|
||||
"fullscreen": "Fullscreen",
|
||||
"DiagramDuration": "Diagram duration:",
|
||||
"DiagramDurationHint": "The time period which is shown in the diagram.",
|
||||
"Seconds": "Seconds",
|
||||
|
||||
@ -585,6 +585,7 @@
|
||||
"DiagramMode": "Diagram mode:",
|
||||
"off": "Off",
|
||||
"small": "Small",
|
||||
"fullscreen": "Fullscreen",
|
||||
"DiagramDuration": "Diagram duration:",
|
||||
"DiagramDurationHint": "The time period which is shown in the diagram.",
|
||||
"Seconds": "Seconds",
|
||||
|
||||
@ -199,6 +199,7 @@ export default defineComponent({
|
||||
diagramModeList: [
|
||||
{ key: 0, value: "off" },
|
||||
{ key: 1, value: "small" },
|
||||
{ key: 2, value: "fullscreen" },
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user