diff --git a/README.md b/README.md
index dc27cfc..3a662fb 100644
--- a/README.md
+++ b/README.md
@@ -52,20 +52,16 @@ See the "[Connecting via Ethernet](#connecting-via-ethernet-optional)" section f
## Recommended ESP32 devices
-- If WIFI6 is absolutely required: ESP32-C6
-- If PoE is required: Any of the above mentioned devices with PoE or any other ESP device in combination with a SPI Ethernet module ([W5500](https://www.aliexpress.com/w/wholesale-w5500.html)) and [PoE to Ethernet and USB type B/C splitter](https://aliexpress.com/w/wholesale-poe-splitter-usb-c.html)
-- If you want maximum performance and intend to run any or multiple of the following:
- - a Nuki Lock and Nuki Opener and/or
- - MQTT SSL and/or
- - HTTP SSL and/or
- - large amounts of keypad codes, timecontrol or authorization entries
- - Developing/debugging Nuki devices and/or Nuki Hub
+We don't recommend using single-core ESP32 devices (ESP32-C3, ESP32-C6, ESP32-H2, ESP32-Solo1).
+Although NukiHub supports single-core devices, NukiHub uses both CPU cores (if available) to process tasks (e.g. HTTP server/MQTT client/BLE scanner/BLE client) and thus runs much better on dual-core devices.
- An ESP32-S3 with 2MB of PSRAM or more (look for an ESP32-S3 with the designation N>=4 and R>=2 such as an ESP32-S3 N16R8)
-
-- In general when buying a new device when size and a couple of dollars more or less are not an issue: An ESP32-S3 with 2MB of PSRAM or more.
+When buying a new device in 2025 we can only recommend the ESP32-S3 with PSRAM (look for an ESP32-S3 with the designation N>=4 and R>=2 such as an ESP32-S3 N16R8).
+The ESP32-S3 is a dual-core CPU with many GPIO's, ability to enlarge RAM using PSRAM, ability to connect Ethernet modules over SPI and optionally power the device with a PoE splitter.
+The only functions missing from the ESP32-S3 as compared to other ESP devices is the ability to use some Ethernet modules only supported by the original ESP32 (and ESP32-P4) and the ability to connect over WIFI6 (C6 or ESP32-P4 with C6 module)
-The ESP32-S3 is a dual-core CPU with many GPIO's, ability to enlarge RAM using PSRAM, ability to connect Ethernet modules over SPI and optionally power the device with a PoE splitter. The only functions missing from the ESP32-S3 as compared to other ESP devices is the ability to use some Ethernet modules only supported by the original ESP32 and the ability to connect over WIFI6 (C6)
+Other considerations:
+- If PoE is required: An ESP32-S3 with PSRAM in combination with a SPI Ethernet module ([W5500](https://www.aliexpress.com/w/wholesale-w5500.html)) and [PoE to Ethernet and USB type B/C splitter](https://aliexpress.com/w/wholesale-poe-splitter-usb-c.html) or the M5stack Atom S3R with the M5stack AtomPoe W5500 module
+- If WIFI6 is absolutely required (it probably isn't): ESP32-C6
## Feature comparison
@@ -194,6 +190,8 @@ In a browser navigate to the IP address assigned to the ESP32.
- RSSI Publish interval: Set to a positive integer to set the amount of seconds between updates to the maintenance/wifiRssi MQTT topic with the current Wi-Fi RSSI, set to -1 to disable, default 60.
- Restart on disconnect: Enable to restart the Nuki Hub when disconnected from the network.
- Check for Firmware Updates every 24h: Enable to allow the Nuki Hub to check the latest release of the Nuki Hub firmware on boot and every 24 hours. Requires the Nuki Hub to be able to connect to github.com. The latest version will be published to MQTT and will be visible on the main page of the Web Configurator.
+- HTTP SSL Certificate (PSRAM enabled devices only): Optionally set to the SSL certificate of the HTTPS server, see the "[HTTPS Server](#https-server-optional-psram-enabled-devices-only)" section of this README.
+- HTTP SSL Key (PSRAM enabled devices only): Optionally set to the SSL key of the HTTPS server, see the "[HTTPS Server](#https-server-optional-psram-enabled-devices-only)" section of this README.
#### IP Address assignment
@@ -575,6 +573,23 @@ openssl req -new -key server.key -out server.csr -subj "/C=US/ST=YourState/L=You
# sign it
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650
```
+
+## HTTPS Server (optional, PSRAM enabled devices only)
+
+The Webconfigurator can use/force HTTPS on PSRAM enabled devices.
+To enable SSL encryption, supply the certificate and key in the Network configuration page and reboot NukiHub.
+
+Example self-signed certificate creation for your HTTPS server:
+```console
+
+# make a Certificate and key pair, MAKE SURE THE CN MATCHES THE DOMAIN AT WHICH NUKIHUB IS AVAILABLE
+openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout nukihub.key -out nukihub.crt -subj "/C=US/ST=YourState/L=YourCity/O=YourOrganization/OU=YourUnit/CN=YourCAName"
+
+```
+
+Although you can use the HTTPS server in this way your client device will not trust the certificate by default.
+An option would be to configure a proxy SSL server (such as Caddy, Traefik, nginx) with a non-self signed (e.g. let's encrypt) SSL certificate and have this proxy server connect to NukiHub over SSL and trust the self-signed NukiHub certificate for this connection.
+
## Home Assistant Discovery (optional)
This software supports [MQTT Discovery](https://www.home-assistant.io/docs/mqtt/discovery/) for integrating Nuki Hub with Home Assistant.
diff --git a/platformio.ini b/platformio.ini
index 37ddf68..08f6a64 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -33,8 +33,8 @@ build_unflags =
build_flags =
-fexceptions
-DTLS_CA_MAX_SIZE=2200
- -DTLS_CERT_MAX_SIZE=1500
- -DTLS_KEY_MAX_SIZE=1800
+ -DTLS_CERT_MAX_SIZE=2200
+ -DTLS_KEY_MAX_SIZE=2200
-DESP_PLATFORM
-DESP32
-DARDUINO_ARCH_ESP32
diff --git a/sdkconfig.defaults b/sdkconfig.defaults
index 3465515..7a29d2b 100644
--- a/sdkconfig.defaults
+++ b/sdkconfig.defaults
@@ -74,7 +74,6 @@ CONFIG_ARDUINO_SELECTIVE_ESP_SR=n
CONFIG_ARDUINO_SELECTIVE_Zigbee=n
CONFIG_ARDUINO_SELECTIVE_SD=n
CONFIG_ARDUINO_SELECTIVE_SD_MMC=n
-CONFIG_ARDUINO_SELECTIVE_SPIFFS=n
CONFIG_ARDUINO_SELECTIVE_FFat=n
CONFIG_ARDUINO_SELECTIVE_LittleFS=n
CONFIG_ARDUINO_SELECTIVE_PPP=n
@@ -98,11 +97,15 @@ CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="resources/github_root_ca.pem"
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y
-CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
+CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
CONFIG_HTTPD_WS_SUPPORT=y
-CONFIG_ESP_HTTPS_SERVER_ENABLE=n
+CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE=y
-CONFIG_BOOTLOADER_WDT_TIME_MS=120000
\ No newline at end of file
+CONFIG_BOOTLOADER_WDT_TIME_MS=120000
+CONFIG_LWIP_MAX_SOCKETS=24
+CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
+CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=1024
+CONFIG_ARDUINO_LOOP_STACK_SIZE=12288
\ No newline at end of file
diff --git a/src/Config.h b/src/Config.h
index aaa471f..0814eab 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.07"
#define NUKI_HUB_VERSION_INT (uint32_t)907
#define NUKI_HUB_BUILD "unknownbuildnr"
-#define NUKI_HUB_DATE "2024-12-30"
+#define NUKI_HUB_DATE "2024-12-31"
#define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest"
#define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"
diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp
index ef6ad57..945c9f6 100644
--- a/src/WebCfgServer.cpp
+++ b/src/WebCfgServer.cpp
@@ -5,7 +5,9 @@
#include "RestartReason.h"
#include
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED
-#include
+#include "esp_psram.h"
+#include "FS.h"
+#include "SPIFFS.h"
#endif
#ifndef CONFIG_IDF_TARGET_ESP32H2
#include
@@ -77,13 +79,13 @@ void WebCfgServer::initialize()
{
_psychicServer->onOpen([&](PsychicClient* client) { Log->printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str()); });
_psychicServer->onClose([&](PsychicClient* client) { Log->printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); });
-
+
HTTPAuthMethod auth_type = BASIC_AUTH;
if (_preferences->getBool(preference_http_auth_type, false))
{
auth_type = DIGEST_AUTH;
}
-
+
_psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword))
@@ -297,6 +299,14 @@ void WebCfgServer::initialize()
{
return buildMqttSSLConfigHtml(request, resp, 2);
}
+ else if (value == "httpcrtconfig")
+ {
+ return buildHttpSSLConfigHtml(request, resp, 1);
+ }
+ else if (value == "httpkeyconfig")
+ {
+ return buildHttpSSLConfigHtml(request, resp, 2);
+ }
else if (value == "nukicfg")
{
return buildNukiConfigHtml(request, resp);
@@ -428,6 +438,10 @@ void WebCfgServer::initialize()
{
return buildConfirmHtml(request, resp, message, 3, true, "/get?page=mqttconfig");
}
+ else if(request->hasParam("httpssl"))
+ {
+ return buildConfirmHtml(request, resp, message, 3, true, "/get?page=ntwconfig");
+ }
else
{
return buildConfirmHtml(request, resp, message, 3, true);
@@ -491,7 +505,7 @@ void WebCfgServer::initialize()
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
-
+
return handleOtaUpload(request, filename, index, data, len, last);
});
@@ -501,7 +515,7 @@ void WebCfgServer::initialize()
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
-
+
String result;
if (!Update.hasError())
{
@@ -1588,6 +1602,74 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
configChanged = true;
}
}
+ #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
+ else if(key == "HTTPCRT")
+ {
+ if (!SPIFFS.begin(true)) {
+ Log->println("SPIFFS Mount Failed");
+ }
+ else
+ {
+ if(value != "")
+ {
+ File file = SPIFFS.open("/http_ssl.crt", FILE_WRITE);
+ if (!file) {
+ Log->println("Failed to open /http_ssl.crt for writing");
+ }
+ else
+ {
+ if (!file.print(value))
+ {
+ Log->println("Failed to write /http_ssl.crt");
+ }
+ file.close();
+ }
+ }
+ else
+ {
+ if (!SPIFFS.remove("/http_ssl.crt")) {
+ Serial.println("Failed to delete /http_ssl.crt");
+ }
+ }
+ Log->print(F("Setting changed: "));
+ Log->println(key);
+ configChanged = true;
+ }
+ }
+ else if(key == "HTTPKEY")
+ {
+ if (!SPIFFS.begin(true)) {
+ Log->println("SPIFFS Mount Failed");
+ }
+ else
+ {
+ if(value != "")
+ {
+ File file = SPIFFS.open("/http_ssl.key", FILE_WRITE);
+ if (!file) {
+ Log->println("Failed to open /http_ssl.key for writing");
+ }
+ else
+ {
+ if (!file.print(value))
+ {
+ Log->println("Failed to write /http_ssl.key");
+ }
+ file.close();
+ }
+ }
+ else
+ {
+ if (!SPIFFS.remove("/http_ssl.key")) {
+ Serial.println("Failed to delete /http_ssl.key");
+ }
+ }
+ Log->print(F("Setting changed: "));
+ Log->println(key);
+ configChanged = true;
+ }
+ }
+ #endif
else if(key == "NWHW")
{
if(_preferences->getInt(preference_network_hardware, 0) != value.toInt())
@@ -3681,6 +3763,13 @@ esp_err_t WebCfgServer::buildNetworkConfigHtml(PsychicRequest *request, PsychicR
printCheckBox(&response, "RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), "");
printCheckBox(&response, "CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), "");
printCheckBox(&response, "FINDBESTRSSI", "Find WiFi AP with strongest signal", _preferences->getBool(preference_find_best_rssi, false), "");
+ #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
+ if(esp_psram_get_size() > 0)
+ {
+ response.print("