wlan-config-seite

This commit is contained in:
Bernd Reuther
2026-08-04 15:09:11 +02:00
parent 922db01b7e
commit e5897a4002
8 changed files with 453 additions and 55 deletions
+7
View File
@@ -16,11 +16,18 @@ framework = arduino
monitor_speed = 115200 monitor_speed = 115200
monitor_port = /dev/ttyACM0 monitor_port = /dev/ttyACM0
board_build.arduino.memory_type = qio_opi
build_flags = build_flags =
-DBOARD_HAS_PSRAM -DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1 -DARDUINO_USB_MODE=1
; Konfiguration für Arduino_GFX
-DCONFIG_ARDUINO_GFX_DEFAULT_BUS_TYPE=0
-D LV_CONF_SKIP
-D LV_COLOR_16_SWAP=0
lib_deps = lib_deps =
moononournation/GFX Library for Arduino moononournation/GFX Library for Arduino
knolleary/PubSubClient knolleary/PubSubClient
lvgl/lvgl @ ^8.3.11
+1 -4
View File
@@ -17,10 +17,7 @@ void Application::begin()
display.begin(); display.begin();
display.showStatus( // display.showStatus();
wifi.ip(),
mqtt.connected() ? "verbunden" : "offline"
);
} }
+2 -2
View File
@@ -3,8 +3,8 @@
namespace Config namespace Config
{ {
// WLAN // WLAN
constexpr const char* WIFI_SSID = "RS20W"; constexpr const char* WIFI_SSID = "";
constexpr const char* WIFI_PASSWORD = "***"; constexpr const char* WIFI_PASSWORD = "";
// MQTT // MQTT
constexpr const char* MQTT_HOST = "mqtt.diereuthers.home"; constexpr const char* MQTT_HOST = "mqtt.diereuthers.home";
+307 -15
View File
@@ -1,31 +1,323 @@
#include <Arduino.h>
#include "DisplayManager.h" #include "DisplayManager.h"
#include "WifiManager.h"
// Externer Zeiger auf deinen WifiManager (in main.cpp instanziiert)
extern WifiManager wifi;
DisplayManager* DisplayManager::instance = nullptr;
// Statische Pointer für die LVGL-Elemente auf der Config-Seite
static lv_obj_t *ta_ssid;
static lv_obj_t *ta_pass;
static lv_obj_t *kb;
DisplayManager::DisplayManager()
{
instance = this;
bus = new Arduino_ESP32QSPI(
TFT_CS, TFT_SCK, TFT_D0, TFT_D1, TFT_D2, TFT_D3
);
// Physisches Display im Nativ-Modus (320x480)
output_display = new Arduino_AXS15231B(
bus, TFT_RST, 0 /* Rotation 0 */, false, 320, 480
);
// Canvas mit physischem Puffer
gfx = new Arduino_Canvas(320, 480, output_display, 0, 0, 0);
}
DisplayManager::~DisplayManager()
{
delete gfx;
delete output_display;
delete bus;
}
// ---------------------------------------------------------------------
// LVGL CALLBACKS
// ---------------------------------------------------------------------
void DisplayManager::my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
instance->gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
instance->gfx->flush();
lv_disp_flush_ready(disp);
}
void DisplayManager::my_touch_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
TouchPoint tp = instance->readTouch();
if (tp.touched) {
data->state = LV_INDEV_STATE_PR;
data->point.x = tp.x;
data->point.y = tp.y;
} else {
data->state = LV_INDEV_STATE_REL;
}
}
// ---------------------------------------------------------------------
// INITIALISIERUNG
// ---------------------------------------------------------------------
void DisplayManager::begin() void DisplayManager::begin()
{ {
Serial.println("DisplayManager gestartet"); pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
if (!gfx->begin()) {
Serial.println("[Display] FEHLER: Canvas konnte nicht gestartet werden!");
return;
} }
// Canvas logisch auf Querformat (480x320) drehen
gfx->setRotation(1);
_initialized = true;
void DisplayManager::showStatus( initTouch();
const String& wifi, initLVGL();
const String& mqtt }
)
void DisplayManager::initTouch()
{ {
Serial.println("----------------"); pinMode(TOUCH_RST, OUTPUT);
Serial.println("DISPLAY"); digitalWrite(TOUCH_RST, HIGH);
Serial.print("WLAN: "); delay(10);
Serial.println(wifi); digitalWrite(TOUCH_RST, LOW);
delay(50);
digitalWrite(TOUCH_RST, HIGH);
delay(100);
Serial.print("MQTT: "); pinMode(TOUCH_INT, INPUT_PULLUP);
Serial.println(mqtt); Wire.begin(TOUCH_SDA, TOUCH_SCL, 400000);
Wire.setTimeOut(100);
Serial.println("----------------");
} }
void DisplayManager::initLVGL()
{
lv_init();
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf = (lv_color_t *)heap_caps_malloc(480 * 40 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
lv_disp_draw_buf_init(&draw_buf, buf, NULL, 480 * 40);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = 480;
disp_drv.ver_res = 320;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touch_read;
lv_indev_drv_register(&indev_drv);
// Beide Oberflächen initial aufbauen
buildMainUI();
buildConfigUI();
}
// ---------------------------------------------------------------------
// UI BUILDER
// ---------------------------------------------------------------------
void DisplayManager::buildMainUI()
{
main_screen = lv_obj_create(NULL);
lv_obj_set_style_bg_color(main_screen, lv_color_hex(0x121212), LV_PART_MAIN);
lv_obj_t *title = lv_label_create(main_screen);
lv_label_set_text(title, "MQTTPanel32 Querformat");
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 15);
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
// Test Button
lv_obj_t *btn = lv_btn_create(main_screen);
lv_obj_set_size(btn, 200, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, -60, 0);
lv_obj_add_event_cb(btn, [](lv_event_t *e) {
lv_obj_t *btn_obj = lv_event_get_target(e);
lv_obj_t *label = lv_obj_get_child(btn_obj, 0);
static bool state = false;
state = !state;
if (state) {
lv_label_set_text(label, "Status: AN");
lv_obj_set_style_bg_color(btn_obj, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN);
} else {
lv_label_set_text(label, "Status: AUS");
lv_obj_set_style_bg_color(btn_obj, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN);
}
}, LV_EVENT_CLICKED, NULL);
lv_obj_t *btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "Drueck mich!");
lv_obj_center(btn_label);
// Button zu den WLAN Einstellungen
lv_obj_t *btn_cfg = lv_btn_create(main_screen);
lv_obj_set_size(btn_cfg, 100, 50);
lv_obj_align(btn_cfg, LV_ALIGN_CENTER, 120, 0);
lv_obj_set_style_bg_color(btn_cfg, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN);
lv_obj_add_event_cb(btn_cfg, [](lv_event_t *e) {
instance->showConfigScreen();
}, LV_EVENT_CLICKED, NULL);
lv_obj_t *lbl_cfg = lv_label_create(btn_cfg);
lv_label_set_text(lbl_cfg, "WLAN Setup");
lv_obj_center(lbl_cfg);
}
void DisplayManager::buildConfigUI()
{
config_screen = lv_obj_create(NULL);
lv_obj_set_style_bg_color(config_screen, lv_color_hex(0x1E1E1E), LV_PART_MAIN);
String saved_ssid = wifi.getSavedSSID();
String saved_pass = wifi.getSavedPassword();
lv_obj_t *title = lv_label_create(config_screen);
lv_label_set_text(title, "WLAN Konfiguration");
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
// Textfeld SSID
ta_ssid = lv_textarea_create(config_screen);
lv_textarea_set_one_line(ta_ssid, true);
lv_textarea_set_placeholder_text(ta_ssid, "SSID");
lv_textarea_set_text(ta_ssid, saved_ssid.c_str());
lv_obj_set_size(ta_ssid, 110, 40);
lv_obj_align(ta_ssid, LV_ALIGN_TOP_LEFT, 10, 40);
// Textfeld Passwort
ta_pass = lv_textarea_create(config_screen);
lv_textarea_set_one_line(ta_pass, true);
lv_textarea_set_password_mode(ta_pass, true);
lv_textarea_set_placeholder_text(ta_pass, "Passwort");
lv_textarea_set_text(ta_pass, saved_pass.c_str());
lv_obj_set_size(ta_pass, 180, 40);
lv_obj_align(ta_pass, LV_ALIGN_TOP_LEFT, 130, 40);
// On-Screen Tastatur
kb = lv_keyboard_create(config_screen);
lv_obj_set_size(kb, 480, 150);
lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, 0);
auto ta_event_cb = [](lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *ta = lv_event_get_target(e);
if (code == LV_EVENT_FOCUSED) {
lv_keyboard_set_textarea(kb, ta);
}
};
lv_obj_add_event_cb(ta_ssid, ta_event_cb, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ta_pass, ta_event_cb, LV_EVENT_ALL, NULL);
// Speichern-Button
lv_obj_t *btn_save = lv_btn_create(config_screen);
lv_obj_set_size(btn_save, 70, 40);
lv_obj_align(btn_save, LV_ALIGN_TOP_RIGHT, -90, 40);
lv_obj_set_style_bg_color(btn_save, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN);
lv_obj_t *lbl_save = lv_label_create(btn_save);
lv_label_set_text(lbl_save, "Save");
lv_obj_center(lbl_save);
lv_obj_add_event_cb(btn_save, [](lv_event_t *e) {
const char *s = lv_textarea_get_text(ta_ssid);
const char *p = lv_textarea_get_text(ta_pass);
// Übergabe der Daten an den WifiManager
wifi.saveCredentials(s, p);
}, LV_EVENT_CLICKED, NULL);
// Cancel Button
lv_obj_t *btn_cancel = lv_btn_create(config_screen);
lv_obj_set_size(btn_cancel, 70, 40);
lv_obj_align(btn_cancel, LV_ALIGN_TOP_RIGHT, -10, 40);
lv_obj_set_style_bg_color(btn_cancel, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN);
lv_obj_t *lbl_cancel = lv_label_create(btn_cancel);
lv_label_set_text(lbl_cancel, "Cancel");
lv_obj_center(lbl_cancel);
lv_obj_add_event_cb(btn_cancel, [](lv_event_t *e) {
// Zurück zur Hauptseite wechseln, ohne Änderungen zu speichern
instance->showMainScreen();
}, LV_EVENT_CLICKED, NULL);
}
// ---------------------------------------------------------------------
// HARDWARE TOUCH-TREIBER & MAPPING
// ---------------------------------------------------------------------
TouchPoint DisplayManager::readTouch()
{
TouchPoint tp;
uint8_t cmd[8] = {0xB5, 0xAB, 0xA5, 0x5A, 0x00, 0x00, 0x00, 0x08};
Wire.beginTransmission(TOUCH_I2C_ADDR);
Wire.write(cmd, 8);
if (Wire.endTransmission() != 0) return tp;
uint8_t buf[8] = {0};
if (Wire.requestFrom((uint8_t)TOUCH_I2C_ADDR, (uint8_t)8) == 8) {
for (int i = 0; i < 8; i++) buf[i] = Wire.read();
uint8_t touch_count = buf[1] & 0x0F;
uint16_t x_raw = ((uint16_t)(buf[2] & 0x0F) << 8) | buf[3];
uint16_t y_raw = ((uint16_t)(buf[4] & 0x0F) << 8) | buf[5];
if (touch_count >= 1 && touch_count <= 5) {
// Umrechnung Hardware-Portrait -> Screen-Landscape (480x320)
uint16_t x_mapped = y_raw;
uint16_t y_mapped = 320 - x_raw;
if (x_mapped < 480 && y_mapped < 320) {
tp.touched = true;
tp.x = x_mapped;
tp.y = y_mapped;
}
}
}
return tp;
}
// ---------------------------------------------------------------------
// SCREEN SWITCHING & MAIN LOOP
// ---------------------------------------------------------------------
void DisplayManager::showMainScreen() {
if (main_screen) lv_scr_load(main_screen);
}
void DisplayManager::showConfigScreen() {
if (config_screen) lv_scr_load(config_screen);
}
void DisplayManager::loop() void DisplayManager::loop()
{ {
if (!_initialized) return;
// LVGL Zeitgeber aktualisieren
static uint32_t last_tick = 0;
uint32_t now = millis();
lv_tick_inc(now - last_tick);
last_tick = now;
// LVGL Aufgaben verarbeiten
lv_timer_handler();
delay(5);
}
void DisplayManager::showStatus(const String& wifi, const String& mqtt)
{
// Hier kannst du später z. B. ein LVGL Label aktualisieren
// Vorerst lassen wir die Methode leer, damit der Compiler zufrieden ist.
} }
+55 -6
View File
@@ -1,18 +1,67 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#include <lvgl.h>
struct TouchPoint {
bool touched = false;
uint16_t x = 0;
uint16_t y = 0;
};
class DisplayManager class DisplayManager
{ {
public: public:
DisplayManager();
~DisplayManager();
void begin(); void begin();
void showStatus(
const String& wifi,
const String& mqtt
);
void loop(); void loop();
void showStatus(const String& wifi, const String& mqtt);
// Öffentlich aufrufbare Methoden zum Umschalten der Screens
void showMainScreen();
void showConfigScreen();
private:
static constexpr int8_t TFT_BL = 1;
static constexpr int8_t TFT_CS = 45;
static constexpr int8_t TFT_SCK = 47;
static constexpr int8_t TFT_D0 = 21;
static constexpr int8_t TFT_D1 = 48;
static constexpr int8_t TFT_D2 = 40;
static constexpr int8_t TFT_D3 = 39;
static constexpr int8_t TFT_RST = GFX_NOT_DEFINED;
static constexpr int8_t TOUCH_SDA = 4;
static constexpr int8_t TOUCH_SCL = 8;
static constexpr int8_t TOUCH_INT = 3;
static constexpr int8_t TOUCH_RST = 12;
static constexpr uint8_t TOUCH_I2C_ADDR = 0x3B;
Arduino_DataBus *bus = nullptr;
Arduino_GFX *output_display = nullptr;
Arduino_Canvas *gfx = nullptr;
bool _initialized = false;
// LVGL Screen-Pointer
lv_obj_t *main_screen = nullptr;
lv_obj_t *config_screen = nullptr;
// Interne Methoden
void initTouch();
TouchPoint readTouch();
void initLVGL();
void buildMainUI();
void buildConfigUI();
static void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
static void my_touch_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data);
static DisplayManager* instance;
}; };
+53 -18
View File
@@ -2,7 +2,7 @@
#include <WiFi.h> #include <WiFi.h>
#include "WifiManager.h" #include "WifiManager.h"
#include "Config.h" #include "Config.h" // Kann für HOSTNAME oder Fallback-SSID bleiben
void WifiManager::begin() void WifiManager::begin()
{ {
@@ -10,38 +10,50 @@ void WifiManager::begin()
WiFi.setHostname(Config::HOSTNAME); WiFi.setHostname(Config::HOSTNAME);
WiFi.begin( // Credentials aus NVS lesen
Config::WIFI_SSID, String ssid = getSavedSSID();
Config::WIFI_PASSWORD String pass = getSavedPassword();
);
// Fallback: Wenn NVS leer ist, versuche Werte aus Config.h (falls definiert)
if (ssid.length() == 0) {
#ifdef WIFI_SSID
ssid = Config::WIFI_SSID;
pass = Config::WIFI_PASSWORD;
#endif
}
if (ssid.length() == 0) {
Serial.println("Keine WLAN-Zugangsdaten im NVS gefunden!");
_connected = false;
return;
}
Serial.printf("Verbinde mit SSID: %s\n", ssid.c_str());
WiFi.begin(ssid.c_str(), pass.c_str());
uint8_t counter = 0; uint8_t counter = 0;
while (WiFi.status() != WL_CONNECTED) while (WiFi.status() != WL_CONNECTED)
{ {
delay(500); delay(500);
Serial.print("."); Serial.print(".");
counter++; counter++;
if (counter > 40) if (counter > 30) // ~15 Sekunden Timeout
{ {
Serial.println(); Serial.println();
Serial.println("WLAN Timeout"); Serial.println("WLAN Timeout");
_connected = false;
return; return;
} }
} }
_connected = true; _connected = true;
Serial.println(); Serial.println();
Serial.println("WLAN verbunden"); Serial.println("WLAN verbunden");
Serial.print("IP: "); Serial.print("IP: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
} }
void WifiManager::loop() void WifiManager::loop()
{ {
bool state = (WiFi.status() == WL_CONNECTED); bool state = (WiFi.status() == WL_CONNECTED);
@@ -49,25 +61,18 @@ void WifiManager::loop()
if (state != _connected) if (state != _connected)
{ {
_connected = state; _connected = state;
if (_connected) if (_connected)
{
Serial.println("WLAN wieder verbunden"); Serial.println("WLAN wieder verbunden");
}
else else
{
Serial.println("WLAN getrennt"); Serial.println("WLAN getrennt");
} }
} }
}
bool WifiManager::connected() bool WifiManager::connected()
{ {
return _connected; return _connected;
} }
String WifiManager::ip() String WifiManager::ip()
{ {
if (_connected) if (_connected)
@@ -75,3 +80,33 @@ String WifiManager::ip()
return "0.0.0.0"; return "0.0.0.0";
} }
// --- NVS Speicher-Methoden ---
void WifiManager::saveCredentials(const String& ssid, const String& password)
{
_prefs.begin("wifi-config", false);
_prefs.putString("ssid", ssid);
_prefs.putString("pass", password);
_prefs.end();
Serial.println("[WifiManager] Neue Zugangsdaten gespeichert. Starte neu...");
delay(500);
ESP.restart(); // Reboot um sauber neu zu verbinden
}
String WifiManager::getSavedSSID()
{
_prefs.begin("wifi-config", true);
String ssid = _prefs.getString("ssid", "");
_prefs.end();
return ssid;
}
String WifiManager::getSavedPassword()
{
_prefs.begin("wifi-config", true);
String pass = _prefs.getString("pass", "");
_prefs.end();
return pass;
}
+9
View File
@@ -1,5 +1,8 @@
#pragma once #pragma once
#include <Arduino.h>
#include <Preferences.h>
class WifiManager class WifiManager
{ {
public: public:
@@ -9,6 +12,12 @@ public:
bool connected(); bool connected();
String ip(); String ip();
// Neue Methoden für NVS-Speicherung
void saveCredentials(const String& ssid, const String& password);
String getSavedSSID();
String getSavedPassword();
private: private:
bool _connected = false; bool _connected = false;
Preferences _prefs;
}; };
+17 -8
View File
@@ -1,17 +1,26 @@
#include <Arduino.h> #include <Arduino.h>
#include "Application.h" #include "WifiManager.h"
#include "Display/DisplayManager.h"
Application app; WifiManager wifi;
DisplayManager display;
void setup() void setup() {
{
Serial.begin(115200); Serial.begin(115200);
delay(1000); delay(1000);
app.begin(); display.begin();
wifi.begin();
// Prüfen ob WLAN geklappt hat
if (wifi.connected()) {
display.showMainScreen();
} else {
display.showConfigScreen();
}
} }
void loop() void loop() {
{ wifi.loop();
app.loop(); display.loop();
} }