Skip to content

[lightning-ln882h] Add static IP support#375

Merged
kuba2k2 merged 2 commits into
libretiny-eu:masterfrom
Bl00d-B0b:ln882h-static-ip-fix
Jun 29, 2026
Merged

[lightning-ln882h] Add static IP support#375
kuba2k2 merged 2 commits into
libretiny-eu:masterfrom
Bl00d-B0b:ln882h-static-ip-fix

Conversation

@Bl00d-B0b

@Bl00d-B0b Bl00d-B0b commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Adds static IP support for LN882H. The LN882H SDK always starts DHCP internally in \wifi_sta_connect()\ with no option to disable it, so static IP is not supported at the SDK level. This PR implements static IP by stopping DHCP and applying the configured address after each connect, reconnect and roam event.

Changes

  1. Apply static IP after connect — stop DHCP and apply static IP in \wifiEventStaConnected()\ before the SDK hands over to the application. \wifi_sta_connect()\ always starts DHCP internally with no way to disable it.

  2. *Fix forward declaration of \wifiEventIpReceived* — required for the above.

  3. Ensure static IP survives reconnect and roam — two additional cases:

    • Wrong order:
      etif_set_addr()\ was called before
      etifapi_dhcp_release_and_stop(). When DHCP is already BOUND, this triggers the netif status callback with \dhcp_supplied_address==true\ before DHCP is stopped. Fixed: stop DHCP first.
    • Missing fallback: \wifiEventIpReceived()\ is called from inside \dhcp_recv()\ in the LwIP tcpip_thread. If DHCP completes despite the early stop (roaming, SDK-internal reconnect), the lease stays active. Cannot call
      etifapi_dhcp_release_and_stop()\ (deadlock) or \dhcp_release_and_stop()\ directly (use-after-free). Fixed: schedule \wifiApplyStaticIpCallback\ via \ cpip_callback()\ to run after \dhcp_recv()\ returns.

Tested on

LN882H-based 2-gang light switch (DS-101JL) running ESPHome — static IP stable across power cycles, reconnects and roaming for several months.

Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request Apr 2, 2026
…y-eu#375)

wifi_sta_connect() always starts DHCP internally; there is no way to
pass a dhcp_mode parameter to it like BK72XX's bk_wlan_start_sta_adv_fix.

Apply static IP and stop DHCP in wifiEventStaConnected(), immediately
after the L2 association event fires, before the SDK-initiated DHCP
exchange completes. Emit GOT_IP manually since the netdev callback
won't fire when DHCP is stopped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch from 648ae3b to ea5142d Compare April 2, 2026 19:15
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request Apr 2, 2026
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request Apr 2, 2026
…am (libretiny-eu#375)

Two bugs in the previous static-IP fix (e2d501b + f49e18f):

1. Wrong order in wifiEventStaConnected(): netif_set_addr() was called
   before netifapi_dhcp_release_and_stop(). When DHCP is already BOUND
   at the time the connected event fires, netif_set_addr() triggers the
   LwIP netif status callback with dhcp_supplied_address==true, emitting
   a spurious GOT_IP before DHCP was stopped.
   Fix: stop DHCP first, then set the address.

2. Missing fallback in wifiEventIpReceived(): this callback is invoked by
   the SDK's sta_netif_status_changed_cb() from inside dhcp_recv() in the
   LwIP tcpip_thread. If DHCP completes before or after the connected
   event (e.g. roaming, SDK-internal reconnect), the DHCP lease stays
   active and the client sends periodic renewal requests.
   Cannot call netifapi_dhcp_release_and_stop() here (deadlock: we ARE
   the tcpip_thread waiting on itself) and cannot call dhcp_release_and_stop()
   directly (use-after-free: dhcp_recv() hasn't returned yet).
   Fix: schedule wifiApplyStaticIpCallback via tcpip_callback(). It runs
   after dhcp_recv() unwinds, where it is safe to stop DHCP, apply the
   static address, and emit GOT_IP.
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch 2 times, most recently from 5760e15 to 6686568 Compare April 2, 2026 19:54
Bl00d-B0b pushed a commit to Bl00d-B0b/libretiny that referenced this pull request Apr 5, 2026
…ct (libretiny-eu#375)

When the reconnect/roam fallback path fires (wifiApplyStaticIpCallback),
LwIP's dhcp_recv() has already updated the DNS table via DHCP option 6
before we could stop DHCP. Restore info.dns1/dns2 after netif_set_addr()
to ensure the user-configured DNS servers survive reconnects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Bl00d-B0b

Bl00d-B0b commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

Added one more fix: on the reconnect/roam fallback path (wifiApplyStaticIpCallback), LwIP's dhcp_recv() processes DHCP option 6 and overwrites the DNS table before we get to stop DHCP. The static IP and gateway were already being restored, but DNS was not. Added DNS restoration (dns_setserver) after netif_set_addr() using the stored info.dns1/info.dns2 values.

Currently testing on a DS-101JL / BSEED WiFi Touch Light Switch 2 Gang with an LN-CB3S V1.0 module (WL2H-U-2 / LN882H).

Bl00d-B0b pushed a commit to Bl00d-B0b/libretiny that referenced this pull request Apr 7, 2026
…ct (libretiny-eu#375)

When the reconnect/roam fallback path fires (wifiApplyStaticIpCallback),
LwIP's dhcp_recv() has already updated the DNS table via DHCP option 6
before we could stop DHCP. Restore info.dns1/dns2 after netif_set_addr()
to ensure the user-configured DNS servers survive reconnects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch from 3e876e5 to fc8c881 Compare April 7, 2026 05:34
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…y-eu#375)

wifi_sta_connect() always starts DHCP internally; there is no way to
pass a dhcp_mode parameter to it like BK72XX's bk_wlan_start_sta_adv_fix.

Apply static IP and stop DHCP in wifiEventStaConnected(), immediately
after the L2 association event fires, before the SDK-initiated DHCP
exchange completes. Emit GOT_IP manually since the netdev callback
won't fire when DHCP is stopped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch from fc8c881 to 6bf1b38 Compare May 21, 2026 03:46
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…am (libretiny-eu#375)

Two bugs in the previous static-IP fix (e2d501b + f49e18f):

1. Wrong order in wifiEventStaConnected(): netif_set_addr() was called
   before netifapi_dhcp_release_and_stop(). When DHCP is already BOUND
   at the time the connected event fires, netif_set_addr() triggers the
   LwIP netif status callback with dhcp_supplied_address==true, emitting
   a spurious GOT_IP before DHCP was stopped.
   Fix: stop DHCP first, then set the address.

2. Missing fallback in wifiEventIpReceived(): this callback is invoked by
   the SDK's sta_netif_status_changed_cb() from inside dhcp_recv() in the
   LwIP tcpip_thread. If DHCP completes before or after the connected
   event (e.g. roaming, SDK-internal reconnect), the DHCP lease stays
   active and the client sends periodic renewal requests.
   Cannot call netifapi_dhcp_release_and_stop() here (deadlock: we ARE
   the tcpip_thread waiting on itself) and cannot call dhcp_release_and_stop()
   directly (use-after-free: dhcp_recv() hasn't returned yet).
   Fix: schedule wifiApplyStaticIpCallback via tcpip_callback(). It runs
   after dhcp_recv() unwinds, where it is safe to stop DHCP, apply the
   static address, and emit GOT_IP.
Bl00d-B0b pushed a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…ct (libretiny-eu#375)

When the reconnect/roam fallback path fires (wifiApplyStaticIpCallback),
LwIP's dhcp_recv() has already updated the DNS table via DHCP option 6
before we could stop DHCP. Restore info.dns1/dns2 after netif_set_addr()
to ensure the user-configured DNS servers survive reconnects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…retiny-eu#375)

- Replace double-space with tab before trailing comment on lwip/dhcp.h include
  (AlignTrailingComments + UseTab:Always requires tab alignment to col 24)
- Replace double-space with tab+space in ipaddr.addr assignment
  (AlignConsecutiveAssignments + UseTab:Always requires tab padding)
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch from 00524d8 to 07fac3b Compare May 21, 2026 11:01
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…y-eu#375)


wifi_sta_connect() always starts DHCP internally; there is no way to
pass a dhcp_mode parameter to it like BK72XX's bk_wlan_start_sta_adv_fix.

Apply static IP and stop DHCP in wifiEventStaConnected(), immediately
after the L2 association event fires, before the SDK-initiated DHCP
exchange completes. Emit GOT_IP manually since the netdev callback
won't fire when DHCP is stopped.
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…am (libretiny-eu#375)


Two bugs in the previous static-IP fix (e2d501b + f49e18f):

1. Wrong order in wifiEventStaConnected(): netif_set_addr() was called
   before netifapi_dhcp_release_and_stop(). When DHCP is already BOUND
   at the time the connected event fires, netif_set_addr() triggers the
   LwIP netif status callback with dhcp_supplied_address==true, emitting
   a spurious GOT_IP before DHCP was stopped.
   Fix: stop DHCP first, then set the address.

2. Missing fallback in wifiEventIpReceived(): this callback is invoked by
   the SDK's sta_netif_status_changed_cb() from inside dhcp_recv() in the
   LwIP tcpip_thread. If DHCP completes before or after the connected
   event (e.g. roaming, SDK-internal reconnect), the DHCP lease stays
   active and the client sends periodic renewal requests.
   Cannot call netifapi_dhcp_release_and_stop() here (deadlock: we ARE
   the tcpip_thread waiting on itself) and cannot call dhcp_release_and_stop()
   directly (use-after-free: dhcp_recv() hasn't returned yet).
   Fix: schedule wifiApplyStaticIpCallback via tcpip_callback(). It runs
   after dhcp_recv() unwinds, where it is safe to stop DHCP, apply the
   static address, and emit GOT_IP.
Bl00d-B0b pushed a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…ct (libretiny-eu#375)


When the reconnect/roam fallback path fires (wifiApplyStaticIpCallback),
LwIP's dhcp_recv() has already updated the DNS table via DHCP option 6
before we could stop DHCP. Restore info.dns1/dns2 after netif_set_addr()
to ensure the user-configured DNS servers survive reconnects.
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request May 21, 2026
…retiny-eu#375)


- Replace double-space with tab before trailing comment on lwip/dhcp.h include
  (AlignTrailingComments + UseTab:Always requires tab alignment to col 24)
- Replace double-space with tab+space in ipaddr.addr assignment
  (AlignConsecutiveAssignments + UseTab:Always requires tab padding)
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch 4 times, most recently from 2b6828b to cdf3b81 Compare May 24, 2026 07:40
@Bl00d-B0b Bl00d-B0b changed the title [lightning-ln882h] Fix static IP ignored after WiFi connect [lightning-ln882h] Add static IP support May 25, 2026
@Bl00d-B0b Bl00d-B0b force-pushed the ln882h-static-ip-fix branch from cdf3b81 to d5f9469 Compare June 2, 2026 14:42
@kuba2k2

kuba2k2 commented Jun 28, 2026

Copy link
Copy Markdown
Member

There is only one pathway to starting DHCP internally by the SDK:

  1. sta_connected() in wifi_manager.c
  2. netdev_set_state() in ethernetif.c
  3. Finally, netifapi_dhcp_start() in netifapi.h

The wifi_manager will always call event_cb[WIFI_MGR_EVENT_STA_CONNECTED]() when the station is connected. Because of wifi_manager_reg_event_callback(WIFI_MGR_EVENT_STA_CONNECTED, &wifiEventStaConnected);, the WiFi library function will be called, and only after DHCP has been started internally. Note: it is not started by wifi_sta_connect(), in contrary to the PR's source code comments.

So only the part that sets a static IP address in STA_CONNECTED should be needed.

Tested on ESPHome, works just fine; forcefully kicking the device off the network causes it to reconnect and use the static IP address again.

@kuba2k2 kuba2k2 added bug Something isn't working enhancement New feature or request LN882H Lightning LN882H family labels Jun 29, 2026
@kuba2k2 kuba2k2 merged commit 0bbb1bb into libretiny-eu:master Jun 29, 2026
2 checks passed
Bl00d-B0b added a commit to Bl00d-B0b/libretiny that referenced this pull request Jul 1, 2026
The merge of upstream master combined this file's static-IP block with the
merged libretiny-eu#375 (kuba2k2) simplification, declaring WiFiNetworkInfo &info twice in
wifiEventStaConnected. Restore the DNS-capable implementation (dns_setserver for
manual_ip dns1/dns2); the merged libretiny-eu#375 drops it, which breaks MQTT-by-hostname.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request LN882H Lightning LN882H family

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants