2026-09-23 10:23:58 +02:00
|
|
|
#include "display_ui.h"
|
|
|
|
|
#include <Wire.h>
|
|
|
|
|
#include <U8g2lib.h>
|
2026-09-26 01:10:32 +02:00
|
|
|
#include "fire_frames.h"
|
|
|
|
|
#include "clock_frames.h"
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
static uint8_t fire_flames_frame = 0;
|
|
|
|
|
static uint8_t clock_frame = 0;
|
2026-09-23 23:03:07 +02:00
|
|
|
|
|
|
|
|
uint32_t lastDispTime = 0;
|
|
|
|
|
|
|
|
|
|
// Statische Bitmap aus Lopaka
|
|
|
|
|
static const unsigned char image_paint_27_bits[] U8X8_PROGMEM = {
|
|
|
|
|
0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
|
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
|
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
|
|
|
|
|
0x00,0x00,0x01,0x00
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-25 01:19:16 +02:00
|
|
|
static const unsigned char image_checked_bits[] U8X8_PROGMEM = {0x00,0x00,0x40,0x21,0x13,0x0e,0x04,0x00};
|
|
|
|
|
static const unsigned char image_crossed_bits[] U8X8_PROGMEM = {0x00,0x21,0x12,0x0c,0x1e,0x33,0x00,0x00};
|
|
|
|
|
|
2026-09-23 23:03:07 +02:00
|
|
|
// U8g2-Display-Objekt
|
2026-09-23 10:23:58 +02:00
|
|
|
static U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
|
|
|
|
|
|
|
|
|
|
void initDisplay() {
|
|
|
|
|
Wire.begin(I2C_SDA, I2C_SCL); // SDA, SCL für XIAO ESP32-C6
|
|
|
|
|
Wire.setClock(400000); // 400 kHz Fast Mode
|
|
|
|
|
u8g2.begin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #############################################################
|
2026-09-23 23:03:07 +02:00
|
|
|
// ANIMATIONEN BERECHNEN & ZEICHNEN
|
2026-09-23 10:23:58 +02:00
|
|
|
// #############################################################
|
2026-09-23 23:03:07 +02:00
|
|
|
static void updateAnimations() {
|
2026-09-26 01:10:32 +02:00
|
|
|
// 8 Frames: Wechsel alle 77 ms
|
|
|
|
|
fire_flames_frame = (millis() / 77) % FIRE_FLAMES_FRAME_COUNT;
|
|
|
|
|
|
|
|
|
|
// 143 Frames: Wechsel alle 50 ms (ca. 7 Sekunden Gesamt-Umlaufzeit)
|
|
|
|
|
clock_frame = (millis() / 50) % CLOCK_FRAME_COUNT;
|
2026-09-23 23:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void drawAnimation_fire_flames() {
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
|
u8g2.drawXBMP(80, 30, FIRE_FLAMES_WIDTH, FIRE_FLAMES_HEIGHT, fire_flames_frames[fire_flames_frame]);
|
2026-09-23 23:03:07 +02:00
|
|
|
}
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-23 23:03:07 +02:00
|
|
|
static void drawAnimation_Clock() {
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
|
u8g2.drawXBMP(104, 40, CLOCK_WIDTH, CLOCK_HEIGHT, clock_frames[clock_frame]);
|
2026-09-23 10:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #############################################################
|
2026-09-23 23:03:07 +02:00
|
|
|
// HAUPTANZEIGE
|
2026-09-23 10:23:58 +02:00
|
|
|
// #############################################################
|
2026-09-23 23:03:07 +02:00
|
|
|
static void drawHomeScreen(int currentSpeed, int curH, int curM, bool timeValid, bool timerActive, SystemSettings settings, int curTemp) {
|
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
|
u8g2.setBitmapMode(1);
|
|
|
|
|
u8g2.setDrawColor(1); // 1 = Weiß (an)
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
// Aktuelle Geschwindigkeit
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.setFont(u8g2_font_helvB08_tr);
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawStr(2, 10, "Geschw:");
|
|
|
|
|
char speedBuf[16];
|
|
|
|
|
snprintf(speedBuf, sizeof(speedBuf), "%d U/min", currentSpeed);
|
|
|
|
|
u8g2.drawStr(52, 10, speedBuf);
|
2026-09-23 23:03:07 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
// Zeitschaltuhr
|
|
|
|
|
u8g2.drawStr(2, 21, "Zeituhr");
|
2026-09-23 23:03:07 +02:00
|
|
|
// 3. Dynamische Zeitschaltuhr: Start- und Endzeit
|
2026-09-26 01:10:32 +02:00
|
|
|
char timerBuf[16];
|
|
|
|
|
if (settings.timerEnabled) {
|
|
|
|
|
snprintf(timerBuf, sizeof(timerBuf), "%02d:%02d-%02d:%02d",
|
|
|
|
|
settings.startHour, settings.startMin, settings.endHour, settings.endMin);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(timerBuf, sizeof(timerBuf), "Deaktiviert");
|
|
|
|
|
}
|
|
|
|
|
u8g2.drawStr(52, 21, timerBuf);
|
2026-09-23 23:03:07 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
// Aktuelle Temperatur
|
|
|
|
|
u8g2.drawStr(2, 32, "Temp");
|
|
|
|
|
char tempBuf[16];
|
|
|
|
|
snprintf(tempBuf, sizeof(tempBuf), "%d / %d \xb0""C", curTemp, settings.maxTemp); // \xb0 = Grad-Zeichen
|
|
|
|
|
u8g2.drawStr(52, 32, tempBuf);
|
|
|
|
|
|
|
|
|
|
// =========================================================
|
|
|
|
|
// 2. HORIZONTALE TRENNLINIE
|
|
|
|
|
// =========================================================
|
|
|
|
|
u8g2.drawHLine(0, 36, 128);
|
2026-09-23 23:03:07 +02:00
|
|
|
|
|
|
|
|
// 5. Große aktuelle Uhrzeit unten links
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.setFont(u8g2_font_helvB14_tr);
|
2026-09-23 23:03:07 +02:00
|
|
|
char timeBuf[10];
|
|
|
|
|
if (timeValid) {
|
|
|
|
|
snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", curH, curM);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(timeBuf, sizeof(timeBuf), "--:--");
|
|
|
|
|
}
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawStr(2, 58, timeBuf);
|
2026-09-23 23:03:07 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
// Icon 1: Zeitschaltuhr aktiv (z. B. bei X = 90)
|
|
|
|
|
if (timerActive) {
|
|
|
|
|
drawAnimation_Clock();
|
|
|
|
|
}
|
2026-09-23 23:03:07 +02:00
|
|
|
|
2026-09-26 01:10:32 +02:00
|
|
|
// Icon 2: Überhitzungs-Warnung (z. B. bei X = 110)
|
|
|
|
|
if (curTemp >= settings.maxTemp) {
|
|
|
|
|
drawAnimation_fire_flames();
|
|
|
|
|
}
|
2026-09-23 10:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #############################################################
|
|
|
|
|
// Zeitschaltuhr
|
|
|
|
|
// #############################################################
|
2026-09-23 23:03:07 +02:00
|
|
|
static void drawTimerScreen(TimeEditStep timeEditStep, uint8_t settingIdx, const SystemSettings& settings){
|
2026-09-26 01:10:32 +02:00
|
|
|
const uint8_t* Font = u8g2_font_helvB10_tr;
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setFontMode(1);
|
|
|
|
|
u8g2.setBitmapMode(1);
|
|
|
|
|
// Von
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.setFont(Font);
|
|
|
|
|
u8g2.drawStr(30, 18, "Von:");
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Start Time
|
|
|
|
|
char startBuf[10];
|
|
|
|
|
snprintf(startBuf, sizeof(startBuf), "%02d:%02d", settings.startHour, settings.startMin);
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawStr(66, 18, startBuf);
|
2026-09-23 23:03:07 +02:00
|
|
|
|
2026-09-25 01:19:16 +02:00
|
|
|
// string 23
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawStr(30, 36, "Bis:");
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// End Time
|
|
|
|
|
char endBuf[10];
|
|
|
|
|
snprintf(endBuf, sizeof(endBuf), "%02d:%02d", settings.endHour, settings.endMin);
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawStr(66, 36, endBuf);
|
|
|
|
|
|
|
|
|
|
// AutoSpeed
|
|
|
|
|
u8g2.drawStr(18, 54, "Speed:");
|
|
|
|
|
char speedBuf[10];
|
|
|
|
|
snprintf(speedBuf, sizeof(speedBuf), "%d", settings.autoSpeed);
|
|
|
|
|
u8g2.drawStr(66, 54, speedBuf);
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Auswahlbox Zeichnen
|
|
|
|
|
switch (timeEditStep) {
|
|
|
|
|
case EDIT_START_HOUR:
|
|
|
|
|
u8g2.setDrawColor(2); // Invertierungs mode
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawBox(65, 7, 16, 13);
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setDrawColor(1); // Wieder zurück auf normal
|
|
|
|
|
break;
|
|
|
|
|
case EDIT_START_MIN:
|
|
|
|
|
u8g2.setDrawColor(2); // Invertierungs mode
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawBox(65 + 19, 7, 16, 13);
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setDrawColor(1); // Wieder zurück auf normal
|
|
|
|
|
break;
|
|
|
|
|
case EDIT_END_HOUR:
|
|
|
|
|
u8g2.setDrawColor(2); // Invertierungs mode
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawBox(65, 25, 16, 13);
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setDrawColor(1); // Wieder zurück auf normal
|
|
|
|
|
break;
|
|
|
|
|
case EDIT_END_MIN:
|
|
|
|
|
u8g2.setDrawColor(2); // Invertierungs mode
|
2026-09-26 01:10:32 +02:00
|
|
|
u8g2.drawBox(65 + 19, 25, 16, 13);
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setDrawColor(1); // Wieder zurück auf normal
|
|
|
|
|
break;
|
2026-09-26 01:10:32 +02:00
|
|
|
case EDIT_SPEED:{
|
|
|
|
|
int widthSpeedBuf = u8g2.getStrWidth(speedBuf);
|
|
|
|
|
u8g2.setDrawColor(2);
|
|
|
|
|
u8g2.drawBox(64, 43, widthSpeedBuf + 4, 13); // Umschließt den gesamten Wert
|
|
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-09-25 01:19:16 +02:00
|
|
|
default: break;
|
|
|
|
|
}
|
2026-09-23 23:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #############################################################
|
|
|
|
|
// EINSTELLUNGSMENÜ
|
|
|
|
|
// #############################################################
|
|
|
|
|
static void drawSettingsScreen(uint8_t settingIdx, TimeEditStep timeEditStep, const SystemSettings& s) {
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-25 01:19:16 +02:00
|
|
|
const uint8_t* textFont = u8g2_font_helvB08_tr;
|
|
|
|
|
const uint8_t* valFont = u8g2_font_helvB08_tr;
|
|
|
|
|
|
|
|
|
|
if(timeEditStep != EDIT_NONE){
|
|
|
|
|
drawTimerScreen(timeEditStep, settingIdx, s);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8g2.setFontMode(1);
|
|
|
|
|
u8g2.setBitmapMode(1);
|
|
|
|
|
u8g2.setDrawColor(1);
|
|
|
|
|
|
|
|
|
|
//######################
|
|
|
|
|
// Zeitschaltuhr Einstellungen
|
|
|
|
|
//######################
|
|
|
|
|
u8g2.setFont(textFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(10, 10, "Zeituhr Einstellung");
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
//######################
|
|
|
|
|
// Zeitschaltuhr Status
|
|
|
|
|
//######################
|
|
|
|
|
u8g2.setFont(textFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(10, 22, "Zeituhr:");
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Wenn Zeitschaltuhr Aktiviert
|
|
|
|
|
if(s.timerEnabled){
|
|
|
|
|
// Draw Check
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawXBMP(78, 15, 7, 8, image_checked_bits);
|
2026-09-25 01:19:16 +02:00
|
|
|
}else{
|
|
|
|
|
// Draw X
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawXBMP(78, 15, 6, 8, image_crossed_bits);
|
2026-09-25 01:19:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################
|
|
|
|
|
// Max Temp
|
|
|
|
|
//######################
|
|
|
|
|
u8g2.setFont(textFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(10, 34, "Max Temp:");
|
2026-09-25 01:19:16 +02:00
|
|
|
char maxTempBuf[10];
|
|
|
|
|
snprintf(maxTempBuf, sizeof(maxTempBuf), "%d", s.maxTemp);
|
|
|
|
|
u8g2.setFont(valFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(78, 34, maxTempBuf);
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Wenn Ausgewählt
|
|
|
|
|
if(s.editMaxTemp){
|
|
|
|
|
// Edit Fenster makieren
|
|
|
|
|
int w = u8g2.getStrWidth(maxTempBuf);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawFrame(75, 25, w + 6, 12);
|
2026-09-25 01:19:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################
|
|
|
|
|
// Beschleunigung
|
|
|
|
|
//######################
|
|
|
|
|
u8g2.setFont(textFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(10, 46, "Beschleu:");
|
2026-09-25 01:19:16 +02:00
|
|
|
u8g2.setFont(valFont);
|
|
|
|
|
char accBuf[10];
|
|
|
|
|
snprintf(accBuf, sizeof(accBuf), "%d", s.accelRate);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawStr(78, 46, accBuf);
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Wenn Ausgewählt
|
|
|
|
|
if(s.editAcc){
|
|
|
|
|
// Edit Fenster makieren
|
|
|
|
|
int w = u8g2.getStrWidth(accBuf);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawFrame(75, 37, w + 6, 12);
|
2026-09-25 01:19:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################
|
|
|
|
|
// Save
|
|
|
|
|
//######################
|
|
|
|
|
u8g2.setFont(textFont);
|
2026-09-25 01:56:44 +02:00
|
|
|
u8g2.drawUTF8(10, 58, "Bestätigung");
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Wenn nichts bearbeitet wird...
|
|
|
|
|
if(!s.editAcc || !s.editMaxTemp){
|
|
|
|
|
// ... Auswahl rahmen anzeigen
|
|
|
|
|
int rowY = 0;
|
|
|
|
|
int rowH = 12;
|
|
|
|
|
|
|
|
|
|
switch (settingIdx) {
|
|
|
|
|
case 0: rowY = 1; break; // Zeituhr Einstellung
|
|
|
|
|
case 1: rowY = 13; break; // Zeituhr An/Aus
|
|
|
|
|
case 2: rowY = 25; break; // Max Temp
|
|
|
|
|
case 3: rowY = 37; break; // Beschleunigung
|
|
|
|
|
case 4: rowY = 49; break; // Bestätigung
|
|
|
|
|
default: break;
|
2026-09-23 23:03:07 +02:00
|
|
|
}
|
2026-09-25 01:19:16 +02:00
|
|
|
|
|
|
|
|
// Rahmen um die aktive Zeile (von X=2 bis fast zum rechten Rand)
|
|
|
|
|
u8g2.drawFrame(2, rowY, 124, rowH);
|
|
|
|
|
}
|
2026-09-23 10:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #############################################################
|
|
|
|
|
// Main Funktion
|
|
|
|
|
// #############################################################
|
|
|
|
|
void updateDisplay(bool inSettings,
|
|
|
|
|
uint8_t settingIdx,
|
|
|
|
|
TimeEditStep editStep,
|
|
|
|
|
const SystemSettings& settings,
|
|
|
|
|
int currentSpeed,
|
2026-09-23 23:03:07 +02:00
|
|
|
int curH, int curM, bool timeValid, int curTemp) {
|
|
|
|
|
if (millis() - lastDispTime >= 50) {
|
|
|
|
|
lastDispTime = millis();
|
2026-09-26 01:10:32 +02:00
|
|
|
|
2026-09-23 23:03:07 +02:00
|
|
|
u8g2.clearBuffer();
|
2026-09-26 01:10:32 +02:00
|
|
|
updateAnimations();
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-23 23:03:07 +02:00
|
|
|
if (inSettings) {
|
|
|
|
|
drawSettingsScreen(settingIdx, editStep, settings);
|
|
|
|
|
}else{
|
|
|
|
|
drawHomeScreen(currentSpeed, curH, curM, timeValid, settings.timerEnabled, settings, curTemp);
|
|
|
|
|
}
|
2026-09-23 10:23:58 +02:00
|
|
|
|
2026-09-23 23:03:07 +02:00
|
|
|
u8g2.sendBuffer();
|
|
|
|
|
}
|
2026-09-23 10:23:58 +02:00
|
|
|
}
|