diff --git a/displayprodLinux/Network.share.xml b/displayprodLinux/Network.share.xml
new file mode 100644
index 0000000..6198b6a
--- /dev/null
+++ b/displayprodLinux/Network.share.xml
@@ -0,0 +1,324 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/displayprodLinux/SmartHomeDisplay.xml b/displayprodLinux/SmartHomeDisplay.xml
index 94b9e72..569d3e0 100644
--- a/displayprodLinux/SmartHomeDisplay.xml
+++ b/displayprodLinux/SmartHomeDisplay.xml
@@ -25,6 +25,11 @@
+
+
+
#include
#include "esp_event.h"
+#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
+struct StaticIpConfig {
+ uint8_t ip[4];
+ uint8_t subnet[4];
+ uint8_t gateway[4];
+ uint8_t dns[4];
+};
+
class Eth {
public:
static Eth& instance();
void init();
+ void applyStaticIp(const StaticIpConfig& cfg);
+ std::string getIPAddress();
private:
Eth() = default;
@@ -25,4 +35,5 @@ private:
bool initialized_ = false;
EventGroupHandle_t ethEventGroup_ = nullptr;
+ esp_netif_t* netif_ = nullptr;
};
\ No newline at end of file
diff --git a/main/NetworkConfig.cpp b/main/NetworkConfig.cpp
new file mode 100644
index 0000000..e2d258e
--- /dev/null
+++ b/main/NetworkConfig.cpp
@@ -0,0 +1,96 @@
+#include "NetworkConfig.hpp"
+#include "Hardware/Eth.hpp"
+#include "Wifi.hpp"
+#include "knx_facade.h"
+#include "esp_log.h"
+
+static const char* TAG = "NetworkConfig";
+
+// Parameter offsets within the NW module's parameter block.
+// These must match the offsets generated by OpenKNXproducer for the NW module.
+// The producer assigns a base offset; these are relative offsets within Network.share.xml.
+// After running the producer, update NW_PARAM_BASE to the assigned base offset.
+
+static constexpr uint16_t NW_PARAM_BASE = 0; // Will be set by producer
+
+// Byte 0: Flags
+static constexpr uint16_t NW_FLAGS = NW_PARAM_BASE + 0;
+static constexpr uint8_t NW_LAN_ENABLE_MASK = 0x80; // Bit 7
+static constexpr uint8_t NW_LAN_STATIC_MASK = 0x40; // Bit 6
+static constexpr uint8_t NW_WLAN_ENABLE_MASK = 0x20; // Bit 5
+static constexpr uint8_t NW_WLAN_STATIC_MASK = 0x10; // Bit 4
+
+// LAN IP config: bytes 1-16
+static constexpr uint16_t NW_LAN_IP = NW_PARAM_BASE + 1;
+static constexpr uint16_t NW_LAN_SUBNET = NW_PARAM_BASE + 5;
+static constexpr uint16_t NW_LAN_GATEWAY = NW_PARAM_BASE + 9;
+static constexpr uint16_t NW_LAN_DNS = NW_PARAM_BASE + 13;
+
+// WLAN IP config: bytes 17-32
+static constexpr uint16_t NW_WLAN_IP = NW_PARAM_BASE + 17;
+static constexpr uint16_t NW_WLAN_SUBNET = NW_PARAM_BASE + 21;
+static constexpr uint16_t NW_WLAN_GATEWAY = NW_PARAM_BASE + 25;
+static constexpr uint16_t NW_WLAN_DNS = NW_PARAM_BASE + 29;
+
+extern KnxFacade knx;
+
+static void readIpOctets(uint16_t offset, uint8_t out[4]) {
+ for (int i = 0; i < 4; i++) {
+ out[i] = knx.paramByte(offset + i);
+ }
+}
+
+void NetworkConfig::applyFromKnx() {
+ if (!knx.configured()) {
+ ESP_LOGW(TAG, "KNX not configured, using default network settings");
+ return;
+ }
+
+ uint8_t flags = knx.paramByte(NW_FLAGS);
+ bool lanEnabled = (flags & NW_LAN_ENABLE_MASK) != 0;
+ bool lanStatic = (flags & NW_LAN_STATIC_MASK) != 0;
+ bool wlanEnabled = (flags & NW_WLAN_ENABLE_MASK) != 0;
+ bool wlanStatic = (flags & NW_WLAN_STATIC_MASK) != 0;
+
+ ESP_LOGI(TAG, "Network config: LAN=%s(%s) WLAN=%s(%s)",
+ lanEnabled ? "on" : "off", lanStatic ? "static" : "dhcp",
+ wlanEnabled ? "on" : "off", wlanStatic ? "static" : "dhcp");
+
+ // Apply LAN static IP if configured
+ if (lanEnabled && lanStatic) {
+ StaticIpConfig cfg = {};
+ readIpOctets(NW_LAN_IP, cfg.ip);
+ readIpOctets(NW_LAN_SUBNET, cfg.subnet);
+ readIpOctets(NW_LAN_GATEWAY, cfg.gateway);
+ readIpOctets(NW_LAN_DNS, cfg.dns);
+
+ ESP_LOGI(TAG, "LAN static: %d.%d.%d.%d/%d.%d.%d.%d gw %d.%d.%d.%d dns %d.%d.%d.%d",
+ cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3],
+ cfg.subnet[0], cfg.subnet[1], cfg.subnet[2], cfg.subnet[3],
+ cfg.gateway[0], cfg.gateway[1], cfg.gateway[2], cfg.gateway[3],
+ cfg.dns[0], cfg.dns[1], cfg.dns[2], cfg.dns[3]);
+
+ Eth::instance().applyStaticIp(cfg);
+ }
+
+ // Initialize WLAN if enabled
+ if (wlanEnabled) {
+ Wifi::instance().init();
+
+ if (wlanStatic) {
+ StaticIpConfig cfg = {};
+ readIpOctets(NW_WLAN_IP, cfg.ip);
+ readIpOctets(NW_WLAN_SUBNET, cfg.subnet);
+ readIpOctets(NW_WLAN_GATEWAY, cfg.gateway);
+ readIpOctets(NW_WLAN_DNS, cfg.dns);
+
+ ESP_LOGI(TAG, "WLAN static: %d.%d.%d.%d/%d.%d.%d.%d gw %d.%d.%d.%d dns %d.%d.%d.%d",
+ cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3],
+ cfg.subnet[0], cfg.subnet[1], cfg.subnet[2], cfg.subnet[3],
+ cfg.gateway[0], cfg.gateway[1], cfg.gateway[2], cfg.gateway[3],
+ cfg.dns[0], cfg.dns[1], cfg.dns[2], cfg.dns[3]);
+
+ Wifi::instance().applyStaticIp(cfg);
+ }
+ }
+}
diff --git a/main/NetworkConfig.hpp b/main/NetworkConfig.hpp
new file mode 100644
index 0000000..c9c03ac
--- /dev/null
+++ b/main/NetworkConfig.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include
+
+class NetworkConfig {
+public:
+ static void applyFromKnx();
+};
diff --git a/main/Wifi.cpp b/main/Wifi.cpp
index 0be16e1..7894505 100644
--- a/main/Wifi.cpp
+++ b/main/Wifi.cpp
@@ -1,9 +1,11 @@
#include "Wifi.hpp"
+#include "Hardware/Eth.hpp"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "nvs_flash.h"
#include "nvs.h"
+#include "lwip/ip4_addr.h"
#include
#include
@@ -61,9 +63,8 @@ void Wifi::init() {
wifiEventGroup_ = xEventGroupCreate();
- ESP_ERROR_CHECK(esp_netif_init());
- ESP_ERROR_CHECK(esp_event_loop_create_default());
- esp_netif_create_default_wifi_sta();
+ // esp_netif_init() and esp_event_loop_create_default() are already called by Eth::init()
+ netif_ = esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
@@ -323,3 +324,42 @@ bool Wifi::getSavedPassword(const char* ssid, std::string& password) {
nvs_close(handle);
return false;
}
+
+void Wifi::applyStaticIp(const StaticIpConfig& cfg) {
+ if (!netif_) {
+ ESP_LOGW(TAG, "applyStaticIp: no netif");
+ return;
+ }
+
+ esp_netif_dhcpc_stop(netif_);
+
+ esp_netif_ip_info_t ip_info = {};
+ IP4_ADDR(&ip_info.ip, cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3]);
+ IP4_ADDR(&ip_info.netmask, cfg.subnet[0], cfg.subnet[1], cfg.subnet[2], cfg.subnet[3]);
+ IP4_ADDR(&ip_info.gw, cfg.gateway[0], cfg.gateway[1], cfg.gateway[2], cfg.gateway[3]);
+
+ esp_err_t err = esp_netif_set_ip_info(netif_, &ip_info);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set static IP: %s", esp_err_to_name(err));
+ esp_netif_dhcpc_start(netif_);
+ return;
+ }
+
+ esp_netif_dns_info_t dns_info = {};
+ IP4_ADDR(&dns_info.ip.u_addr.ip4, cfg.dns[0], cfg.dns[1], cfg.dns[2], cfg.dns[3]);
+ dns_info.ip.type = ESP_IPADDR_TYPE_V4;
+ esp_netif_set_dns_info(netif_, ESP_NETIF_DNS_MAIN, &dns_info);
+
+ ESP_LOGI(TAG, "WiFi static IP set: %d.%d.%d.%d", cfg.ip[0], cfg.ip[1], cfg.ip[2], cfg.ip[3]);
+}
+
+std::string Wifi::getIPAddress() {
+ if (!netif_) return "";
+ esp_netif_ip_info_t ip_info;
+ if (esp_netif_get_ip_info(netif_, &ip_info) == ESP_OK) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), IPSTR, IP2STR(&ip_info.ip));
+ return buf;
+ }
+ return "";
+}
diff --git a/main/Wifi.hpp b/main/Wifi.hpp
index 10d9590..926050f 100644
--- a/main/Wifi.hpp
+++ b/main/Wifi.hpp
@@ -5,14 +5,18 @@
#include
#include "esp_wifi.h"
#include "esp_event.h"
+#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
+struct StaticIpConfig;
+
class Wifi {
public:
static Wifi& instance();
void init();
+ void applyStaticIp(const StaticIpConfig& cfg);
void scan(std::function&)> callback);
void connect(const char* ssid, const char* password);
void disconnect();
@@ -52,4 +56,6 @@ private:
static constexpr const char* NVS_NAMESPACE = "wifi_creds";
static constexpr int MAX_SAVED_NETWORKS = 10;
+
+ esp_netif_t* netif_ = nullptr;
};
diff --git a/main/main.cpp b/main/main.cpp
index 9a00599..28b0a5b 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -13,6 +13,7 @@
#include "KnxWorker.hpp"
#include "Wifi.hpp"
#include "Hardware/Eth.hpp"
+#include "NetworkConfig.hpp"
#include "WebServer.hpp"
#include "SdCard.hpp"
#include "Fonts.hpp"
@@ -22,6 +23,10 @@
static void knx_task(void* arg) {
KnxWorker* worker = static_cast(arg);
worker->init();
+
+ // Apply network config from KNX parameters after KNX is initialized
+ NetworkConfig::applyFromKnx();
+
while (true) {
worker->loop();
vTaskDelay(pdMS_TO_TICKS(10));