Reliable Wi-Fi and configurable NTP

Updated 2026-06-11
CoreOne MK4 MK4S XL MINI

The stock firmware has three networking problems:

  1. ESP inactivity watchdog drops idle Wi-Fi linksINACTIVE_PACKET_SECONDS = 5 in the ESP NIC firmware treats a quiet network as a dead link, triggering a reconnect cycle every ~75 seconds
  2. WPA2/WPA3 transition-mode authentication fails — the ESP32 defaults to WPA3_SAE_PWE_UNSPECIFIED, which doesn't support Hash-to-Element (H2E); the ESP8266 backported WPA3 implementation is unreliable in mixed mode
  3. NTP is hard-coded to prusa3d.pool.ntp.org — no mechanism to use DHCP option 42 or a local server on isolated LANs

This affects all Prusa printers with the ESP Wi-Fi module (Core One, Core One L, MK4, MK4S, XL, MINI+). The Wi-Fi fixes apply only to Wi-Fi-capable models; the NTP fix applies to all networked printers.

How the Wi-Fi stack works

Prusa printers with Wi-Fi use an external ESP module (ESP32 on newer boards, ESP8266 on older ones) connected to the main MCU over UART. The ESP runs its own firmware (uart_wifi.bin) that handles the 802.11 stack and communicates with the main MCU via a message protocol. The main MCU runs lwIP for TCP/IP and delegates Wi-Fi management to the ESP.

ESP NIC firmware

The ESP NIC firmware is built from lib/esp32-nic/ or lib/esp8266-nic/ using ESP-IDF or the ESP8266 RTOS SDK. It is flashed onto the ESP module alongside a bootloader and partition table. The main MCU verifies the ESP firmware version on every boot and reflashes it automatically if the version does not match.

The firmware handles Wi-Fi association, authentication, inbound packet monitoring, and AP scanning.

NTP client

The printer syncs its clock using lwIP's SNTP client, initialized in sntp_client_init() and configured in sntp_opts.h:

#define SNTP_SERVER_DNS     1
#define SNTP_SERVER_ADDRESS "prusa3d.pool.ntp.org"

The client runs periodically via sntp_client_step() from the network event loop in wui.cpp. It has no fallback servers, no DHCP-provided NTP option, and no user configuration.

The ESP NIC firmware monitors inbound Wi-Fi traffic with a 5-second timeout:

static const uint32_t INACTIVE_PACKET_SECONDS = 5;

Every 5 seconds, check_online_status() checks whether any inbound packet has arrived. If none has, it launches a probe scan — taking the radio off-channel for 120–300ms per channel. After 3 failed probes, it reports link-down to the main MCU, which triggers a full Wi-Fi restart. On a quiet network, this cycle repeats every ~75 seconds.

This was traced in detail on an MK4S fleet (issue #5152): over 4 days, three MK4S units accounted for 83% of all disconnects across a 10-printer fleet. The workaround is to ping each printer every 3 seconds, keeping last_inbound_seen fresh and preventing the timeout. The firmware source has a TODO comment noting this:

// TODO: Shall we generate something to provoke getting some packets? Like ARP pings to the AP?

The fix in upstream PR #5188 by bkerler, pulled into the community edition via PR #18: if the ESP is associated with the AP and the link is up, a lack of inbound traffic is not evidence of a dead link. The ESP stays associated and keeps the link up regardless of inbound traffic.

WPA2/WPA3 transition-mode authentication fails

Modern Wi-Fi routers run WPA2/WPA3 transition mode — accepting both WPA2-PSK and WPA3-SAE clients on the same SSID. Both ESP32 and ESP8266 fail in this configuration, for different reasons.

ESP32: Wrong SAE PWE method

The ESP-IDF defaults to WPA3_SAE_PWE_UNSPECIFIED (method 0), which does not support the Hash-to-Element (H2E) PWE method required by many Wi-Fi 6 routers in transition mode. IDF v5.0 introduced WPA3_SAE_PWE_BOTH (method 3) to support both H2E and Hunting-and-Pecking.

Additionally, CONFIG_ESP32_WIFI_NVS_ENABLED=y causes WPA3 PMKSA entries to persist in NVS across reboots. The esp_wifi_restore() call at startup does not reliably clear these entries, leading to stale-PMK authentication failures on subsequent boots.

The fix in PR #18:

// ESP32: Set SAE PWE to BOTH to support H2E and Hunting-and-Pecking
wifi_config.sta.pmf_cfg.capable = 1;
+   wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;          // Scan all channels
+   wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL;      // Pick strongest AP
+   wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;      // Start from WPA2
+   #ifdef CONFIG_ESP_WIFI_SAE_SUPPORT
+       wifi_config.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;          // Support both H2E and H&P
+   #endif

And disable NVS to prevent stale PMKSA entries:

-CONFIG_ESP32_WIFI_NVS_ENABLED=y
+# CONFIG_ESP32_WIFI_NVS_ENABLED is not set

ESP8266: WPA3 SAE disabled entirely

The ESP8266 RTOS SDK's WPA3 SAE implementation is based on IDF v3.4 with backported patches — it does not support H2E at all. WPA3 authentication is unreliable in transition mode. The fix is to disable WPA3 SAE entirely for the ESP8266, forcing WPA2-PSK. WPA2/WPA3 transition-mode routers must accept WPA2-PSK by definition:

-CONFIG_ESP8266_WIFI_ENABLE_WPA3_SAE=y
+# CONFIG_ESP8266_WIFI_ENABLE_WPA3_SAE is not set

NTP is hard-coded to Prusa's server

The stock firmware syncs time only from prusa3d.pool.ntp.org, which requires internet access and DNS resolution. On an isolated LAN — no internet, local DNS only — the clock never sets. SSL certificates fail to validate, PrusaConnect cannot authenticate, and log timestamps are incorrect.

PR #9 adds a three-way NTP source mode, stored in the config store and configurable from the network settings menu:

Mode NTP source Requirements
Prusa (default) prusa3d.pool.ntp.org via DNS Internet access (stock behavior)
DHCP NTP server from DHCP option 42 DHCP server that provides option 42
Custom User-configured hostname or IP A reachable NTP server

The implementation adds an ntp_mode_t enum and ntp_server string field to the config store, an MI_NTP_MODE switch and MI_NTP_SERVER text field in the network menu, and rewrites sntp_client_step() to apply the mode at runtime:

typedef enum {
    NTP_MODE_PRUSA = 0,   // prusa3d.pool.ntp.org (needs internet)
    NTP_MODE_DHCP = 1,    // DHCP option 42
    NTP_MODE_CUSTOM = 2,  // user-configured hostname or IP
} ntp_mode_t;

The DHCP mode requires LWIP_DHCP_GET_NTP_SRV, which was not enabled in the stock firmware. When set to DHCP, the client renews the DHCP lease immediately to get a fresh ACK with the NTP server, rather than waiting for the next renewal:

static void renew_dhcp_leases(void) {
    struct netif *netif;
    NETIF_FOREACH(netif) {
        if (dhcp_supplied_address(netif)) {
            dhcp_renew(netif);
        }
    }
}

For Custom mode, an IP address literal works without DNS. The custom server name is stored in a static buffer because lwIP's sntp_setservername() stores only the pointer, not the string content.

AP selection by signal strength

When multiple access points share the same SSID (UniFi, mesh, enterprise), the ESP NIC firmware connects to whichever AP it finds first in the scan results — which may be a distant AP — and does not roam to a stronger one.

The fix in PR #18 sets the scan method to WIFI_ALL_CHANNEL_SCAN and the sort method to WIFI_CONNECT_AP_BY_SIGNAL on the ESP32, with equivalent configuration on the ESP8266:

wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;    // All channels, not per-channel
wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Strongest first

ESP NIC firmware built from source

PR #19 builds the ESP NIC firmware from source during the main firmware build, rather than using pre-built binaries in src/resources/<chip>/. This is a prerequisite for the fixes in PR #18.

The build uses lib/AddEspNicFw.cmake with Docker to set up the ESP-IDF or ESP8266 RTOS SDK toolchain and compile from lib/esp32-nic/ and lib/esp8266-nic/. The Docker image rebuilds only when the Dockerfile changes; the firmware binary rebuilds only when the ESP NIC source files change. ESP_FW_USE_PREBUILT=ON falls back to pre-built binaries if Docker is unavailable.

The ESP firmware version was bumped from 13 to 14 to signal incompatibility — the main MCU's espif.cpp checks the version on every boot and reflashes the ESP if it doesn't match:

-static constexpr uint8_t SUPPORTED_FW_VERSION = 13;
+static constexpr uint8_t SUPPORTED_FW_VERSION = 14;

History and status

Impact by network type

Network type Stock firmware Community firmware
Internet-connected, single AP Works Works (no regression)
Isolated LAN (no internet) Wi-Fi drops every ~75s; clock never syncs Wi-Fi stable; NTP via DHCP or local server
Multiple APs, same SSID (UniFi/mesh) Connects to random AP; poor roaming Selects strongest AP
WPA2/WPA3 transition mode Auth failures on ESP32 and ESP8266 ESP32: H2E supported; ESP8266: falls back to WPA2-PSK
Enterprise LAN with DHCP option 42 Ignores DHCP NTP; uses prusa3d.pool.ntp.org (fails) NTP from DHCP option 42

The NTP configuration is available on all printers (ethernet and Wi-Fi). The Wi-Fi fixes only apply to printers with the ESP module.

References

Research Notes

The three PRs were authored by FilipDolnik and merged on June 7, 2026. PR #19 (ESP build from source) is a prerequisite for PR #18 (ESP firmware v14 with the fixes). PR #9 (NTP configuration) is independent but targets the same use case: printers on isolated LANs.

The upstream PR #5188 by bkerler (March 2026) contains the same ESP fixes but has not been merged into the official firmware as of June 2026. The community edition pulled it in via the mh/esp branch. Prusa Research's last attempt at ESP firmware changes caused a Wi-Fi regression in 6.2.0 alpha 1 (issue #4199, September 2024), which they reverted in 6.2.0 alpha 2. They have not publicly addressed the inactivity watchdog or WPA3 transition-mode issues since.

The INACTIVE_PACKET_SECONDS = 5 value is aggressive. Normal Wi-Fi behavior includes gaps of 5–30 seconds between inbound packets on an idle but connected device. The ESP8266-based printers (MINI+) don't have the WPA3 issue because the older ESP8266 NIC firmware didn't have WPA3 SAE enabled — the regression was introduced when CONFIG_ESP8266_WIFI_ENABLE_WPA3_SAE=y was turned on in a later firmware version.

The LWIP_DHCP_GET_NTP_SRV option (enabled in PR #9) is a standard lwIP feature that was not enabled in the stock firmware. DHCP option 42 is the standard mechanism for providing NTP server addresses and is supported by most DHCP servers (dnsmasq, Kea, ISC DHCP, Windows DHCP Server, Mikrotik RouterOS, Ubiquiti UniFi).