Compare commits
3 Commits
1fbc28a628
...
f7cedf24e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
f7cedf24e8
|
|||
|
1c52f7d679
|
|||
|
7a73fc4b7b
|
@@ -1,4 +1,4 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
ARG DOCKER_TAG=release-v5.4
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
@@ -29,6 +29,31 @@ static EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
static const char *TAG = "wifi_manager";
|
||||
|
||||
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||
{
|
||||
ESP_LOGI(TAG, "WIFI_EVENT_STA_START: Connecting to AP...");
|
||||
esp_wifi_connect();
|
||||
}
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
|
||||
{
|
||||
ESP_LOGW(TAG, "WIFI_EVENT_STA_DISCONNECTED: Verbindung verloren, versuche erneut...");
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||
{
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP: IP-Adresse erhalten: " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP)
|
||||
{
|
||||
ESP_LOGW(TAG, "IP_EVENT_STA_LOST_IP: IP-Adresse verloren!");
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_create_ap()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
@@ -58,6 +83,13 @@ void wifi_manager_init()
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
// Default WiFi Station
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
|
||||
// Event Handler registrieren
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
|
||||
|
||||
// Try to load stored WiFi configuration
|
||||
persistence_manager_t pm;
|
||||
char ssid[33] = {0};
|
||||
@@ -97,9 +129,9 @@ void wifi_manager_init()
|
||||
EventBits_t bits;
|
||||
do
|
||||
{
|
||||
esp_wifi_connect();
|
||||
ESP_LOGI(TAG, "Warte auf IP-Adresse (DHCP)...");
|
||||
bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE,
|
||||
5000 / portTICK_PERIOD_MS);
|
||||
10000 / portTICK_PERIOD_MS);
|
||||
if (bits & WIFI_CONNECTED_BIT)
|
||||
{
|
||||
led_behavior_t led_behavior = {
|
||||
@@ -108,7 +140,7 @@ void wifi_manager_init()
|
||||
.mode = LED_MODE_SOLID,
|
||||
};
|
||||
led_status_set_behavior(led_behavior);
|
||||
ESP_LOGI(TAG, "WiFi connection established successfully");
|
||||
ESP_LOGI(TAG, "WiFi connection established successfully (mit IP)");
|
||||
break;
|
||||
}
|
||||
retries++;
|
||||
@@ -116,7 +148,9 @@ void wifi_manager_init()
|
||||
|
||||
if (!(bits & WIFI_CONNECTED_BIT))
|
||||
{
|
||||
ESP_LOGW(TAG, "WiFi connection failed, switching to Access Point mode");
|
||||
ESP_LOGW(TAG, "WiFi connection failed (keine IP?), switching to Access Point mode");
|
||||
// AP-Netzwerkschnittstelle initialisieren, falls noch nicht geschehen
|
||||
esp_netif_create_default_wifi_ap();
|
||||
wifi_create_ap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,15 @@ extern "C"
|
||||
bool initialized;
|
||||
} persistence_manager_t;
|
||||
|
||||
/**
|
||||
* @brief Erases the entire NVS flash (factory reset).
|
||||
*
|
||||
* Warning: This will remove all stored data and namespaces!
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, otherwise error code.
|
||||
*/
|
||||
esp_err_t persistence_manager_factory_reset(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize the persistence manager with a given NVS namespace.
|
||||
*
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
|
||||
#include "persistence_manager.h"
|
||||
#include <esp_log.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "persistence_manager"
|
||||
|
||||
esp_err_t persistence_manager_factory_reset(void)
|
||||
{
|
||||
// Erase the entire NVS flash (factory reset)
|
||||
esp_err_t err = nvs_flash_erase();
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Factory reset failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t persistence_manager_init(persistence_manager_t *pm, const char *nvs_namespace)
|
||||
{
|
||||
if (!pm)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "analytics.h"
|
||||
#include "button_handling.h"
|
||||
#include "common.h"
|
||||
#include "common/InactivityTracker.h"
|
||||
#include "hal/u8g2_esp32_hal.h"
|
||||
#include "i2c_checker.h"
|
||||
@@ -182,7 +183,55 @@ void app_task(void *args)
|
||||
return;
|
||||
}
|
||||
|
||||
// Display initialisieren, damit Info angezeigt werden kann
|
||||
setup_screen();
|
||||
|
||||
// BACK-Button prüfen und ggf. Einstellungen löschen (mit Countdown)
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << BUTTON_BACK);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
if (gpio_get_level(BUTTON_BACK) == 0)
|
||||
{
|
||||
u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tr);
|
||||
for (int i = 5; i > 0; --i)
|
||||
{
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
u8g2_DrawStr(&u8g2, 5, 20, "BACK gedrueckt!");
|
||||
u8g2_DrawStr(&u8g2, 5, 35, "Halte fuer Reset...");
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "Loesche in %d s", i);
|
||||
u8g2_DrawStr(&u8g2, 5, 55, buf);
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (gpio_get_level(BUTTON_BACK) != 0)
|
||||
{
|
||||
// Button losgelassen, abbrechen
|
||||
break;
|
||||
}
|
||||
if (i == 1)
|
||||
{
|
||||
// After 5 seconds still pressed: perform factory reset
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
u8g2_DrawStr(&u8g2, 5, 30, "Alle Einstellungen ");
|
||||
u8g2_DrawStr(&u8g2, 5, 45, "werden geloescht...");
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
persistence_manager_factory_reset();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
u8g2_DrawStr(&u8g2, 5, 35, "Fertig. Neustart...");
|
||||
u8g2_SendBuffer(&u8g2);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
esp_restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup_buttons();
|
||||
init_ui();
|
||||
|
||||
|
||||
@@ -114,18 +114,6 @@
|
||||
updateConnectBtn();
|
||||
});
|
||||
|
||||
// Toggle password visibility
|
||||
function togglePassword() {
|
||||
const input = document.getElementById('password');
|
||||
const btn = document.getElementById('password-btn');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
btn.textContent = '🙈';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
btn.textContent = '👁️';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,29 +1,57 @@
|
||||
@media (max-width: 600px) {
|
||||
.header {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
flex: 1 1 100%;
|
||||
text-align: center;
|
||||
order: 2;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
order: 1;
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-start;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Captive Portal CSS - WiFi setup specific styles */
|
||||
/* Base styles are in shared.css */
|
||||
|
||||
|
||||
body {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 8px;
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.header p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
@@ -163,27 +191,6 @@ select {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Password Toggle */
|
||||
.password-toggle {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.password-toggle input {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.password-toggle button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Info Box */
|
||||
.info-box {
|
||||
|
||||
@@ -20,10 +20,6 @@ body {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 12px;
|
||||
|
||||
@@ -1,3 +1,60 @@
|
||||
/* Passwortfeld Toggle (zentral für alle Seiten) */
|
||||
.password-toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.password-toggle input {
|
||||
padding-right: 50px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.password-toggle button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.password-toggle button:active {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Passwortfeld Toggle */
|
||||
.password-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.password-toggle input[type="password"],
|
||||
.password-toggle input[type="text"] {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.password-toggle button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
padding: 0 6px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.password-toggle button:active {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Shared CSS - Base styles for all pages */
|
||||
|
||||
/* CSS Variables - Dark Mode (default) */
|
||||
|
||||
@@ -162,8 +162,11 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" data-i18n="wifi.password">WLAN Passwort</label>
|
||||
<div class="password-toggle">
|
||||
<input type="password" id="password" data-i18n-placeholder="wifi.password.placeholder"
|
||||
placeholder="Passwort eingeben" autocomplete="off">
|
||||
<button type="button" onclick="togglePassword()" id="password-btn">👁️</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/**
|
||||
* Passwortfeld sichtbar/unsichtbar schalten (shared)
|
||||
*/
|
||||
function togglePassword() {
|
||||
const input = document.getElementById('password');
|
||||
const btn = document.getElementById('password-btn');
|
||||
if (!input || !btn) return;
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
btn.textContent = '🙈';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
btn.textContent = '👁️';
|
||||
}
|
||||
}
|
||||
// Shared WiFi configuration functions
|
||||
// Used by both captive.html and index.html
|
||||
|
||||
|
||||
Reference in New Issue
Block a user