Skip to content

WiFi Module

The WiFi module (main/modules/wifi/) provides the low-level WiFi driver lifecycle for the Pibot CNC pendant firmware. It encapsulates both Station (STA) mode — where the pendant joins an existing access point — and Access Point (AP) mode — where the pendant itself becomes an AP for direct client connections. All higher-level network services (HTTP, mDNS, SSDP, WebSocket) are started and stopped by this module as a consequence of connection events.

See also:


ESP_IDF

WiFi Module (current)

Network_Layer

Core_Platform

ESP3DX

(main.cpp)

ESP3DSettings

(NVS-backed config)

ESP3DValues

(observable state)

ESP3DCommands

(message dispatch)

ESP3DNetwork

orchestrator

ESP3DXNetwork

(networkTask / FreeRTOS)

ESP3DNetworkServices

(HTTP, mDNS, SSDP…)

ESP3DWifiClient

(singleton esp3dWifiClient)

STA implementation

esp3d_wifi_sta.cpp

AP implementation

esp3d_wifi_ap.cpp

esp_wifi_*

(radio driver)

esp_netif_*

(TCP/IP stack)

esp_event

(event loop)

FreeRTOS EventGroup


FilePurpose
main/modules/wifi/esp3d_wifi_client.hClass declaration: ESP3DWifiClient, ESP3DIpInfos, ESP3DIpMode
main/modules/wifi/esp3d_wifi_sta.cppSTA event handler, connect(), disconnect(), deinit()
main/modules/wifi/esp3d_wifi_ap.cppAP event handler, startAP(), stopAP()

All three files compile only when ESP3D_WIFI_FEATURE is enabled in the build configuration.


A final singleton class exposed as extern ESP3DWifiClient esp3dWifiClient. It manages both the STA and AP netifs and the underlying ESP-IDF WiFi driver.

returns via getLocalIp()

reads from NVS

ESP3DWifiClient

-bool _useStaticIp

-bool _auto_reconnect_disabled

-bool _wifi_sta_active

-bool _wifi_driver_started

-bool _need_services_restart

-esp_netif_t* _wifiApPtr

-esp_netif_t* _wifiStaPtr

-EventGroupHandle_t _s_wifi_event_group

-string _hostname

+connect() : bool

+disconnect() : bool

+deinit() : bool

+isConnected() : bool

+userDisconnect() : void

+startAP(configMode, limited) : bool

+stopAP() : bool

+hasLostIp() : bool

+getLocalIp(ipInfo) : bool

+getLocalIpString() : const char

+getSignal(RSSI, filter) : int32_t

+getAPMac() : const char

+getSTAMac() : const char

+disableAutoReconnect() : void

+clearAutoReconnectDisabled() : void

+isAutoReconnectDisabled() : bool

+isWifiDriverStarted() : bool

+setNeedServicesRestart(v) : void

+needServicesRestart() : bool

+useStaticIp() : bool

+getEventGroup() : EventGroupHandle_t

+getHostName() : const char

ESP3DIpInfos

+esp_netif_ip_info_t ip_info

+esp_netif_dns_info_t dns_info

«enumeration»

ESP3DIpMode

dhcp = 0

staticIp = 1

FlagMeaning
_wifi_sta_activeSTA is fully connected and network services are running
_wifi_driver_startedesp_wifi_start() was called and not yet followed by esp_wifi_stop()
_auto_reconnect_disabledUser explicitly disconnected; auto-reconnect is suppressed
_need_services_restartIP re-acquired during auto-reconnect; network services must be restarted by the next handle() cycle

No

Yes

No

Yes

No

Yes

Yes

No

WIFI_CONNECTED_BIT

WIFI_FAIL_BIT

Timeout

connect called

SSID configured?

return false

Create FreeRTOS EventGroup

_wifiStaPtr exists?

esp_netif_create_default_wifi_sta

WiFi driver initialized?

esp_wifi_init

+ WIFI_STORAGE_RAM

Reuse existing driver

Register event handlers

WIFI_EVENT + IP_EVENT

Read NVS: SSID, password, IP mode

Static IP mode?

Stop DHCP client

Set static IP / GW / mask / DNS

Start DHCP client

Configure wifi_config_t

SSID, password, scan method

Set hostname + NetBIOS init

esp_wifi_start

xEventGroupWaitBits

max 45 seconds

Bits received?

Read IP from netif

Log failure

Log timeout

set status to '?'

dispatch IP message

set status 'C'

start NetworkServices

return false

return true

The STA event handler (wifi_sta_event_handler) implements a layered retry strategy coordinated through a FreeRTOS EventGroupHandle_t.

WIFI_EVENT_STA_START\nclearAutoReconnectDisabled()\nstatus = T

esp_wifi_connect() OK

IP_EVENT_STA_GOT_IP\nWIFI_CONNECTED_BIT set

connect() unblocked\nstatus = C\nNetworkServices.begin()

esp_wifi_connect() error\nWIFI_FAIL_BIT set

IP_EVENT_STA_LOST_IP\nstatus = ?

IP_EVENT_STA_GOT_IP\nstatus = C\nneedServicesRestart = true

NetworkServices restarted\nby networkTask handle()

WIFI_EVENT_STA_DISCONNECTED

reason == AUTH_FAIL\nor 4WAY_HANDSHAKE_TIMEOUT\nstatus = A\nauto_reconnect_disabled = true\nWIFI_FAIL_BIT set

retry_num < 10\nstatus = ?\nesp_wifi_connect()

connect OK

retry_num >= 10\nWIFI_FAIL_BIT set

isAutoReconnectDisabled()\ndo not retry

Connecting

Associated

GotIP

Active

ConnectFailed

LostIP

Reconnected

Disconnected

AuthFailed

Retrying

RetryFailed

Stopped

Disconnect reason codes handled explicitly:

ReasonCodeBehaviour
WIFI_REASON_AUTH_FAIL202Stop retrying immediately; status → "A"
WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT204Same as AUTH_FAIL (typical wrong-password indicator)
Any other reason—Retry up to ESP3D_STA_MAXIMUM_RETRY (10) times

Timeout: connect() waits at most ESP3D_WIFI_CONNECT_TIMEOUT_MS (45 000 ms) for WIFI_CONNECTED_BIT or WIFI_FAIL_BIT. This prevents the network task from blocking forever when an AP associates but never answers DHCP.


  • SSID / password copy: wifi_config is zeroed, then the SSID and password are copied with memcpy(…, strnlen(…, sizeof(field))) instead of strcpy(): the fields are fixed-size arrays, not C strings, so a 32-byte SSID or 64-byte password can no longer overflow them (STA and AP).
  • AP: the password buffer is 65 bytes (was 32, too small for a 64-character WPA2 passphrase), and ap.ssid_len is computed from the SSID (it was computed from the password).
  • getLocalIp(): the netif is chosen from the active radio mode (wifi_sta → STA netif; wifi_ap, wifi_ap_config, wifi_ap_limited → AP netif). The STA netif is kept alive by disconnect() (for example a failed STA connection falling back to the setup AP), so its mere presence did not mean STA was the active role, and in setup AP mode the STA netif (with no address) was read instead of the AP one.

Yes

No

No

Yes

Yes

No

Yes

No

startAP called

_wifiApPtr set?

stopAP first

WiFi driver initialized?

esp_wifi_init

+ WIFI_STORAGE_RAM

Reuse existing driver

Register wifi_ap_event_handler

Read NVS: SSID, password,

channel, static IP

esp_netif_create_default_wifi_ap

Stop DHCP server

Set IP info

Restart DHCP server

esp_wifi_set_mode WIFI_MODE_AP

esp_wifi_set_config

esp_wifi_start

Set hostname

esp_netif_get_ip_info

IP info OK?

Build success message

Build failure message

dispatch message to all clients

success AND NOT limited?

NetworkServices.begin

return success flag

configMode vs normal AP:

FlagGateway in netifUse case
configMode = trueSame as AP IPConfiguration portal — AP acts as default gateway
configMode = false0 (none)Standard AP — clients get IP but no routing

limited = true: AP starts but NetworkServices.begin() is suppressed. Used when only the WiFi PHY is required without starting the full HTTP/mDNS/SSDP stack.

Open vs secured AP:

PasswordAuth mode
Empty stringWIFI_AUTH_OPEN
Non-emptyWIFI_AUTH_WPA_WPA2_PSK

The WiFi driver (esp_wifi_init / esp_wifi_deinit) is not called on every disconnect() or stopAP(). Reusing the initialized driver avoids repeated heap fragmentation and startup latency across reconnect cycles.

esp_netif_*esp_wifi_*ESP3DWifiClientESP3DNetworkesp_netif_*esp_wifi_*ESP3DWifiClientESP3DNetworkSoft disconnect (STA) - driver stays alive_wifiStaPtr kept\nDriver not deinited\n_wifi_driver_started = falseSoft AP stop - driver stays alive_wifiApPtr = nullptr\nDriver not deinitedFull deinit - when leaving WiFi entirelyAll netifs freed\nDriver fully releaseddisconnect()esp_wifi_disconnect()esp_wifi_stop()esp_netif_dhcpc_stop()stopAP()esp_wifi_stop()esp_wifi_clear_default_wifi_driver_and_handlersesp_netif_destroy_default_wifi(_wifiApPtr)deinit()stopAP() if AP runningdisconnect() if event group setesp_wifi_clear_default_wifi_driver_and_handlers(_wifiStaPtr)esp_netif_destroy_default_wifi(_wifiStaPtr)esp_wifi_deinit()
MethodDriver afterSTA netif afterAP netif after
disconnect()Stopped (not deinited)KeptUnchanged
stopAP()Not deinitedUnchangedDestroyed
deinit()Fully deinitedDestroyedDestroyed

The module reads the following settings from ESP3DSettings (NVS-backed).

Setting IndexTypeDescription
esp3d_sta_ssidstring (32)Target AP SSID
esp3d_sta_passwordstring (64)WPA2 passphrase
esp3d_sta_ip_modebyte0 = DHCP, 1 = static
esp3d_sta_ip_staticuint32Static IP (packed IPv4)
esp3d_sta_gw_staticuint32Static gateway
esp3d_sta_mask_staticuint32Static netmask
esp3d_sta_dns_staticuint32Static primary DNS
Setting IndexTypeDescription
esp3d_ap_ssidstring (32)AP SSID to broadcast
esp3d_ap_passwordstring (64)WPA2 passphrase (empty = open network)
esp3d_ap_channelbyteWiFi channel (1–13)
esp3d_ap_ip_staticuint32AP IP address (DHCP pool base)
Setting IndexTypeDescription
esp3d_hostnamestring (32)mDNS / NetBIOS hostname (NetBIOS truncated to 15 chars)

The WiFi module writes to ESP3DValuesIndex::connection_status (and conditionally server_status). These drive the UI via the ESP3DValues observable/subscription system.

ValueMeaningWritten when
"U"UninitializedSystem startup
"T"Trying to connectWIFI_EVENT_STA_START fires
"C"Connected (IP obtained)IP_EVENT_STA_GOT_IP / connect() success path
"?"Disconnected / IP lostSTA_DISCONNECTED, STA_LOST_IP, connect timeout
"A"Authentication failureAUTH_FAIL / 4WAY_HANDSHAKE_TIMEOUT reason

Radio CNC link guard (a14): both connection_status and server_status are written only when ESP3DNetwork::radioIsCncLink() is true (the CNC output client is the WiFi socket or WebSocket client, or BT). The STA event handler goes through wifi_sta_set_connection_status(). For serial, UART Ext or USB CNC configurations, WiFi is the remote / debug access only; writing these values here would overwrite the CNC transport status on every WiFi event (before a14 only server_status was guarded, and only for socket_client). See network.md.


ESP3DCommandsESP3DNetworkServicesESP3DValueswifi_sta_event_handlerESP3DWifiClientESP3DNetworkESP3DCommandsESP3DNetworkServicesESP3DValueswifi_sta_event_handlerESP3DWifiClientESP3DNetworkWIFI_EVENT_STA_START firesIP_EVENT_STA_GOT_IP firesStarts HTTP, mDNS, SSDP,\nsocket client/server as configuredconnect()connection_status = "?"create EventGroup\nregister WIFI + IP handlersesp_wifi_set_mode(STA)\nesp_wifi_set_config\nesp_wifi_start()connection_status = "T"esp_wifi_connect()xEventGroupSetBits(WIFI_CONNECTED_BIT)xEventGroupWaitBits unblocksdispatch("Connected to SSID\n")connection_status = "C"begin() ESP3DCommandsESP3DNetworkServicesESP3DWifiClientESP3DNetworkESP3DCommandsESP3DNetworkServicesESP3DWifiClientESP3DNetworkstartAP()esp_wifi_init if not already doneregister wifi_ap_event_handleresp_netif_create_default_wifi_ap()configure IP + DHCP serveresp_wifi_set_mode(AP)\nesp_wifi_set_config\nesp_wifi_start()set hostnamedispatch("Access Point SSID started…\n")begin()

ESP3DWifiClient is not called directly by application screens or CNC logic. All mode transitions are requested through ESP3DNetwork::setMode() or ESP3DNetwork::setModeAsync(). The networkTask (Core 0, ESP3DXNetwork) polls the module every 10 ms via ESP3DXNetwork::handle().

on boot

WIFI_STA

WIFI_AP

NO_RADIO

ESP3DSettings

esp3d_radio_mode

ESP3DNetwork

.setMode()

ESP3DWifiClient

STA mode

.connect()

AP mode

.startAP()

No Radio

.startNoRadioMode()

ESP3DXNetwork::handle() also monitors:

  • ESP3DWifiClient::hasLostIp() — triggers AP fallback when configured
  • ESP3DWifiClient::needServicesRestart() — restarts network services after IP re-acquired during auto-reconnect

WiFi settings are exposed through the [ESP...] command system dispatched via ESP3DCommands.

CommandAction
ESP100Get/set STA SSID
ESP101Get/set STA password
ESP102Get/set STA IP mode (DHCP / static)
ESP103Get/set STA static IP
ESP104Get/set STA static gateway
ESP105Get/set STA static netmask
ESP106Get/set STA static DNS
ESP107Get/set AP SSID
ESP108Get/set AP password
ESP111Get current local IP address
ESP410Scan available WiFi networks (used by WiFi scan screen)

WiFi Transport Role

Mutually Exclusive - hardware limit, no PSRAM

Cannot coexist

sanity_check.cmake forbids

ESP3D_WIFI_FEATURE

(WiFi radio)

ESP3D_BT_SERIAL_FEATURE

or ESP3D_BT_BLE_FEATURE

(Bluetooth radio)

Default build

SERIAL or USB CNC + WiFi remote

WebUI + SSDP ON

SOCKET_CLIENT_SERVICE ON

WiFi CNC via TCP

WebUI + SSDP OFF

SSDP_SERVICE

WEBUI_SERVER

WS_SERVER_SERVICE

WS_CLIENT_SERVICE

Key rules (enforced by cmake/sanity_check.cmake):

  • ESP3D_WIFI_FEATURE and any Bluetooth feature are mutually exclusive — no PSRAM means both radio stacks cannot coexist.
  • When SOCKET_CLIENT_SERVICE is ON (WiFi as CNC transport), SSDP_SERVICE, WEBUI_SERVER, WS_SERVER_SERVICE, and WS_CLIENT_SERVICE are forbidden.
  • The default CMakeLists.txt has SOCKET_CLIENT_SERVICE OFF, WebUI and SSDP ON — WiFi is the remote access transport while CNC runs over serial or USB serial.

See docs/features/feature_resource_matrix.md §2–§4 for the full compatibility matrix.


The WiFi stack is the largest consumer of DRAM on this platform.

ConfigurationAvailable heap (approx.)
WiFi active~75 KB
Bluetooth mode~10 KB (WiFi must be OFF)
WiFi + heavy services under loadCan drop below 1 KB contiguous block

Design decisions made to minimise allocation pressure:

  • esp_wifi_init() is called once on first use and the driver is reused across soft-disconnect cycles. esp_wifi_deinit() is only invoked from deinit() when fully leaving WiFi mode.
  • AP and STA esp_netif_t pointers are created once and reused; only _wifiApPtr is destroyed on stopAP(). _wifiStaPtr persists until deinit().
  • WiFi settings are read into stack-allocated fixed-size char buffers (max 65 bytes), never into heap-allocated std::string in hot paths.
  • WIFI_STORAGE_RAM is set immediately after esp_wifi_init() to prevent the WiFi driver from persisting configuration to flash (reduces write wear and init latency).

See docs/guides/esp32_memory_constraints.md — subsection Serial CNC + WiFi remote — fragmentation playbook — for fragmentation analysis under mixed workloads.


WiFi Module

STA Mode

connect

FreeRTOS EventGroup

45s bounded timeout

10 retry max

Auth fail stops retries

Static IP or DHCP

disconnect

Soft stop driver alive

deinit

Full driver teardown

userDisconnect

Disables auto-reconnect

AP Mode

startAP

configMode gateway variant

limited mode skips services

DHCP server managed

stopAP

Netif destroyed driver kept

Status Reporting

connection_status observable

Values U T C ? A

server_status guarded by output client

Integration

ESP3DNetwork orchestrates mode

ESP3DNetworkServices on connect

ESP3DValues UI observables

ESP3DCommands ESP1xx

ESP3DSettings NVS reads

Build Constraints

WiFi XOR Bluetooth

SOCKET_CLIENT XOR WebUI

Driver reuse across reconnects

WIFI_STORAGE_RAM always set