Display: Implement rotation setting and removed icons

This commit is contained in:
Thomas Basler 2023-03-15 20:20:14 +01:00
parent c0b5049a74
commit 2e33f5cd51
12 changed files with 136 additions and 107 deletions

View File

@ -102,7 +102,7 @@ struct CONFIG_T {
bool Display_PowerSafe;
bool Display_ScreenSaver;
bool Display_ShowLogo;
uint8_t Display_Rotation;
uint8_t Display_Contrast;
};

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "defaults.h"
#include <U8g2lib.h>
enum DisplayType_t {
@ -17,25 +18,29 @@ public:
void init(DisplayType_t type, uint8_t data, uint8_t clk, uint8_t cs, uint8_t reset);
void loop();
void setContrast(uint8_t contrast);
void setOrientation(uint8_t rotation = DISPLAY_ROTATION);
void setStartupDisplay();
bool enablePowerSafe = true;
bool enableScreensaver = true;
bool showLogo = true;
uint8_t contrast = 60;
private:
void printText(const char* text, uint8_t line);
void calcLineHeights();
void setFont(uint8_t line);
U8G2* _display;
DisplayType_t _display_type = DisplayType_t::None;
uint8_t _mExtra;
uint16_t _dispY = 0;
uint16_t _period = 1000;
uint16_t _interval = 60000; // interval at which to power save (milliseconds)
uint32_t _lastDisplayUpdate = 0;
uint32_t _previousMillis = 0;
char _fmtText[32];
bool _isLarge = false;
uint8_t _lineOffsets[5];
};
extern DisplayGraphicClass Display;

View File

@ -85,5 +85,5 @@
#define DISPLAY_POWERSAFE true
#define DISPLAY_SCREENSAVER true
#define DISPLAY_SHOWLOGO true
#define DISPLAY_ROTATION 2
#define DISPLAY_CONTRAST 60

View File

@ -88,7 +88,7 @@ bool ConfigurationClass::write()
JsonObject display = device.createNestedObject("display");
display["powersafe"] = config.Display_PowerSafe;
display["screensaver"] = config.Display_ScreenSaver;
display["showlogo"] = config.Display_ShowLogo;
display["rotation"] = config.Display_Rotation;
display["contrast"] = config.Display_Contrast;
JsonArray inverters = doc.createNestedArray("inverters");
@ -225,7 +225,7 @@ bool ConfigurationClass::read()
JsonObject display = device["display"];
config.Display_PowerSafe = display["powersafe"] | DISPLAY_POWERSAFE;
config.Display_ScreenSaver = display["screensaver"] | DISPLAY_SCREENSAVER;
config.Display_ShowLogo = display["showlogo"] | DISPLAY_SHOWLOGO;
config.Display_Rotation = display["rotation"] | DISPLAY_ROTATION;
config.Display_Contrast = display["contrast"] | DISPLAY_CONTRAST;
JsonArray inverters = doc["inverters"];

View File

@ -5,31 +5,6 @@
#include <map>
#include <time.h>
static uint8_t bmp_logo[] PROGMEM = {
B00000000, B00000000, // ................
B11101100, B00110111, // ..##.######.##..
B11101100, B00110111, // ..##.######.##..
B11100000, B00000111, // .....######.....
B11010000, B00001011, // ....#.####.#....
B10011000, B00011001, // ...##..##..##...
B10000000, B00000001, // .......##.......
B00000000, B00000000, // ................
B01111000, B00011110, // ...####..####...
B11111100, B00111111, // ..############..
B01111100, B00111110, // ..#####..#####..
B00000000, B00000000, // ................
B11111100, B00111111, // ..############..
B11111110, B01111111, // .##############.
B01111110, B01111110, // .######..######.
B00000000, B00000000 // ................
};
static uint8_t bmp_arrow[] PROGMEM = {
B00000000, B00011100, B00011100, B00001110, B00001110, B11111110, B01111111,
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000
};
std::map<DisplayType_t, std::function<U8G2*(uint8_t, uint8_t, uint8_t, uint8_t)>> display_types = {
{ DisplayType_t::PCD8544, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs) { return new U8G2_PCD8544_84X48_F_4W_HW_SPI(U8G2_R0, cs, data, reset); } },
{ DisplayType_t::SSD1306, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs) { return new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(U8G2_R0, reset, clock, data); } },
@ -52,56 +27,83 @@ void DisplayGraphicClass::init(DisplayType_t type, uint8_t data, uint8_t clk, ui
auto constructor = display_types[_display_type];
_display = constructor(reset, clk, data, cs);
_display->begin();
setContrast(DISPLAY_CONTRAST);
}
}
void DisplayGraphicClass::calcLineHeights()
{
uint8_t yOff = 0;
for (uint8_t i = 0; i < 4; i++) {
setFont(i);
yOff += (_display->getMaxCharHeight());
_lineOffsets[i] = yOff;
}
}
void DisplayGraphicClass::setFont(uint8_t line)
{
switch (line) {
case 0:
_display->setFont((_isLarge) ? u8g2_font_ncenB14_tr : u8g2_font_logisoso16_tr);
break;
case 3:
_display->setFont(u8g2_font_5x8_tr);
break;
default:
_display->setFont((_isLarge) ? u8g2_font_ncenB10_tr : u8g2_font_5x8_tr);
break;
}
}
void DisplayGraphicClass::printText(const char* text, uint8_t line)
{
// get the width and height of the display
uint16_t maxWidth = _display->getWidth();
uint16_t maxHeight = _display->getHeight();
uint8_t dispX;
if (!_isLarge) {
dispX = (line == 0) ? 5 : 0;
} else {
dispX = (line == 0) ? 20 : 5;
}
setFont(line);
// pxMovement +x (0 - 6 px)
uint8_t ex = enableScreensaver ? (_mExtra % 7) : 0;
dispX += enableScreensaver ? (_mExtra % 7) : 0;
_display->drawStr(dispX, _lineOffsets[line], text);
}
// set the font size based on the display size
switch (line) {
void DisplayGraphicClass::setOrientation(uint8_t rotation)
{
if (_display_type == DisplayType_t::None) {
return;
}
switch (rotation) {
case 0:
_display->setDisplayRotation(U8G2_R0);
break;
case 1:
if (maxWidth > 120 && maxHeight > 60) {
_display->setFont(u8g2_font_ncenB14_tr); // large display
} else {
_display->setFont(u8g2_font_logisoso16_tr); // small display
}
_display->setDisplayRotation(U8G2_R1);
break;
case 4:
if (maxWidth > 120 && maxHeight > 60) {
_display->setFont(u8g2_font_5x8_tr); // large display
} else {
_display->setFont(u8g2_font_5x8_tr); // small display
}
case 2:
_display->setDisplayRotation(U8G2_R2);
break;
default:
if (maxWidth > 120 && maxHeight > 60) {
_display->setFont(u8g2_font_ncenB10_tr); // large display
} else {
_display->setFont(u8g2_font_5x8_tr); // small display
}
case 3:
_display->setDisplayRotation(U8G2_R3);
break;
}
// get the font height, to calculate the textheight
_dispY += (_display->getMaxCharHeight()) + 1;
_isLarge = (_display->getWidth() > 100);
calcLineHeights();
}
// calculate the starting position of the text
uint16_t dispX;
if (line == 1) {
dispX = 20 + ex;
} else {
dispX = 5 + ex;
void DisplayGraphicClass::setStartupDisplay()
{
if (_display_type == DisplayType_t::None) {
return;
}
// draw the Text, on the calculated pos
_display->drawStr(dispX, _dispY, text);
_display->clearBuffer();
printText("OpenDTU!", 0);
_display->sendBuffer();
}
void DisplayGraphicClass::loop()
@ -136,20 +138,6 @@ void DisplayGraphicClass::loop()
_display->clearBuffer();
// set Contrast of the Display to raise the lifetime
_display->setContrast(contrast);
//=====> Logo and Lighting ==========
// pxMovement +x (0 - 6 px)
uint8_t ex = enableScreensaver ? (_mExtra % 7) : 0;
if (isprod > 0) {
_display->drawXBMP(5 + ex, 1, 8, 17, bmp_arrow);
if (showLogo) {
_display->drawXBMP(_display->getWidth() - 24 + ex, 2, 16, 16, bmp_logo);
}
}
//<=======================
//=====> Actual Production ==========
if ((totalPower > 0) && (isprod > 0)) {
_display->setPowerSave(false);
@ -158,14 +146,14 @@ void DisplayGraphicClass::loop()
} else {
snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", totalPower);
}
printText(_fmtText, 1);
printText(_fmtText, 0);
_previousMillis = millis();
}
//<=======================
//=====> Offline ===========
else {
printText("offline", 1);
printText("offline", 0);
// check if it's time to enter power saving mode
if (millis() - _previousMillis >= (_interval * 2)) {
_display->setPowerSave(enablePowerSafe);
@ -175,27 +163,34 @@ void DisplayGraphicClass::loop()
//=====> Today & Total Production =======
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
printText(_fmtText, 2);
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
printText(_fmtText, 3);
printText(_fmtText, 2);
//<=======================
//=====> IP or Date-Time ========
if (!(_mExtra % 10) && NetworkSettings.localIP()) {
printText(NetworkSettings.localIP().toString().c_str(), 4);
printText(NetworkSettings.localIP().toString().c_str(), 3);
} else {
// Get current time
time_t now = time(nullptr);
strftime(_fmtText, sizeof(_fmtText), "%a %d.%m.%Y %H:%M", localtime(&now));
printText(_fmtText, 4);
printText(_fmtText, 3);
}
_display->sendBuffer();
_dispY = 0;
_mExtra++;
_lastDisplayUpdate = millis();
}
}
void DisplayGraphicClass::setContrast(uint8_t contrast)
{
if (_display_type == DisplayType_t::None) {
return;
}
_display->setContrast(contrast * 2.55f);
}
DisplayGraphicClass Display;

View File

@ -64,7 +64,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
displayPinObj[F("reset")] = pin.display_reset;
JsonObject display = root.createNestedObject("display");
display[F("show_logo")] = config.Display_ShowLogo;
display[F("rotation")] = config.Display_Rotation;
display[F("power_safe")] = config.Display_PowerSafe;
display[F("screensaver")] = config.Display_ScreenSaver;
display[F("contrast")] = config.Display_Contrast;
@ -133,15 +133,15 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
bool performRestart = root[F("curPin")][F("name")].as<String>() != config.Dev_PinMapping;
strlcpy(config.Dev_PinMapping, root[F("curPin")][F("name")].as<String>().c_str(), sizeof(config.Dev_PinMapping));
config.Display_ShowLogo = root[F("display")][F("show_logo")].as<bool>();
config.Display_Rotation = root[F("display")][F("rotation")].as<uint8_t>();
config.Display_PowerSafe = root[F("display")][F("power_safe")].as<bool>();
config.Display_ScreenSaver = root[F("display")][F("screensaver")].as<bool>();
config.Display_Contrast = root[F("display")][F("contrast")].as<uint8_t>();
Display.showLogo = config.Display_ShowLogo;
Display.setOrientation(config.Display_Rotation);
Display.enablePowerSafe = config.Display_PowerSafe;
Display.enableScreensaver = config.Display_ScreenSaver;
Display.contrast = config.Display_Contrast;
Display.setContrast(config.Display_Contrast);
Configuration.write();

View File

@ -107,10 +107,11 @@ void setup()
pin.display_clk,
pin.display_cs,
pin.display_reset);
Display.showLogo = config.Display_ShowLogo;
Display.setOrientation(config.Display_Rotation);
Display.enablePowerSafe = config.Display_PowerSafe;
Display.enableScreensaver = config.Display_ScreenSaver;
Display.contrast = config.Display_Contrast;
Display.setContrast(config.Display_Contrast);
Display.setStartupDisplay();
MessageOutput.println(F("done"));
// Check for default DTU serial

View File

@ -508,8 +508,12 @@
"PowerSafeHint": "Schaltet das Display aus, wenn kein Wechselrichter Strom erzeugt",
"Screensaver": "Screensaver aktivieren:",
"ScreensaverHint": "Bewegt die Ausgabe bei jeder Aktualisierung um ein Einbrennen zu verhindern (v. a. für OLED-Displays nützlich)",
"ShowLogo": "Logo anzeigen:",
"Contrast": "Kontrast ({contrast}):",
"Rotation": "Rotation:",
"rot0": "Keine Rotation",
"rot90": "90 Grad Drehung",
"rot180": "180 Grad Drehung",
"rot270": "270 Grad Drehung",
"Save": "@:dtuadmin.Save"
},
"pininfo": {

View File

@ -508,8 +508,12 @@
"PowerSafeHint": "Turn off the display if no inverter is producing.",
"Screensaver": "Enable Screensaver:",
"ScreensaverHint": "Move the display a little bit on each update to prevent burn-in. (Useful especially for OLED displays)",
"ShowLogo": "Show Logo:",
"Contrast": "Contrast ({contrast}):",
"Rotation": "Rotation:",
"rot0": "No rotation",
"rot90": "90 degree rotation",
"rot180": "180 degree rotation",
"rot270": "270 degree rotation",
"Save": "@:dtuadmin.Save"
},
"pininfo": {

View File

@ -508,8 +508,12 @@
"PowerSafeHint": "Eteindre l'écran si aucun onduleur n'est en production.",
"Screensaver": "Activer l'écran de veille",
"ScreensaverHint": "Déplacez un peu l'écran à chaque mise à jour pour éviter le phénomène de brûlure. (Utile surtout pour les écrans OLED)",
"ShowLogo": "Afficher le logo",
"Contrast": "Contraste ({contrast}):",
"Rotation": "Rotation:",
"rot0": "No rotation",
"rot90": "90 degree rotation",
"rot180": "180 degree rotation",
"rot270": "270 degree rotation",
"Save": "@:dtuadmin.Save"
},
"pininfo": {

View File

@ -1,7 +1,7 @@
import type { Device } from "./PinMapping";
export interface Display {
show_logo: boolean;
rotation: number;
power_safe: boolean;
screensaver: boolean;
contrast: number;

View File

@ -57,8 +57,18 @@
v-model="deviceConfigList.display.screensaver" type="checkbox"
:tooltip="$t('deviceadmin.ScreensaverHint')" />
<InputElement :label="$t('deviceadmin.ShowLogo')"
v-model="deviceConfigList.display.show_logo" type="checkbox" />
<div class="row mb-3">
<label class="col-sm-2 col-form-label">
{{ $t('deviceadmin.Rotation') }}
</label>
<div class="col-sm-10">
<select class="form-select" v-model="deviceConfigList.display.rotation">
<option v-for="rotation in displayRotationList" :key="rotation.key" :value="rotation.key">
{{ rotation.value }}
</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="inputDisplayContrast" class="col-sm-2 col-form-label">{{
@ -108,6 +118,12 @@ export default defineComponent({
alertMessage: "",
alertType: "info",
showAlert: false,
displayRotationList: [
{ key: 0, value: this.$t('deviceadmin.rot0') },
{ key: 1, value: this.$t('deviceadmin.rot90') },
{ key: 2, value: this.$t('deviceadmin.rot180') },
{ key: 3, value: this.$t('deviceadmin.rot270') },
],
}
},
created() {