Skip to content

Why Did a MicroPython Soft Reset Hang an STM32 with a W5500, and How Does PR #19800 Fix It?

Upstream MicroPython fix keeps an active W5500's SPI bus alive through soft reset on STM32 with lwIP, qualified with 60 resets and 20 upload cycles.

ElenaLee

Published October 24, 2026 · © MIT license (MIT)

Why Did a MicroPython Soft Reset Hang an STM32 with a W5500, and How Does PR #19800 Fix It?

Components

Hardware components

WIZnet parts

Project description

📌 Overview

PR #19800 is a pull request to the official MicroPython repository, opened on October 22, 2026 by GitHub user daniel-briscoe-qntm. It fixes a hang on STM32 boards that use a WIZnet W5500 for Ethernet: after an ordinary soft reset, MicroPython kept the network running but switched off the SPI bus the W5500 was on, and the next network poll stalled the board.

The fix is small (34 added lines across 5 files) and targeted. During soft reset cleanup, the STM32 port now keeps exactly one SPI bus alive: the one owned by the active W5500 network interface. The W5500 driver also stops touching SPI when the interface is inactive. The author qualified the change on real hardware with 20 test cycles, including 60 soft resets and 20 patterned 4 KiB file uploads with readback.

What happens to the W5500 bus on a soft reset, before and after the fix Generated technical diagram: the soft reset sequence before the fix (SPI shut down under an active NIC) and after PR #19800 (the active NIC's bus is kept).

Key takeaway: anyone running MicroPython with lwIP and a W5500 on STM32 could hit this hang during routine development. The fix comes with a reproducible test procedure and a maintainer-agreed design.

Background: Soft Reset, lwIP and the WIZNET5K Driver

A soft reset restarts the MicroPython interpreter without rebooting the chip. It happens all the time in normal work: pressing Ctrl+D in the REPL, or running mpremote to copy files or run a script. Python objects are cleared, but some hardware state is kept on purpose so the board does not have to start from zero.

On STM32, MicroPython can drive a W5500 through the network.WIZNET5K class. When the firmware is built with lwIP, a small open-source TCP/IP stack, the W5500 is used as an Ethernet interface: lwIP on the STM32 builds the TCP/IP packets, and the W5500 sends and receives Ethernet frames over SPI. A background handler called PendSV polls the W5500 for new frames. On STM32, the lwIP network state and this polling survive a soft reset.

The Problem: Two Lifecycles That Did Not Match

The bug report, issue #19790, was filed on October 20, 2026. The setup was a custom STM32F407VE board with a W5500 on hardware SPI1, MicroPython v1.27.0, and lwIP enabled. The author activated the W5500, left it active, and ran an ordinary mpremote operation that soft-resets the interpreter.

The board hung, and the host test hit its 30-second timeout. A debugger capture over SWD showed SPI1 disabled while the CPU was inside PendSV with interrupts masked. The cause was a lifecycle mismatch: the STM32 port kept lwIP and its NIC polling across the soft reset, but spi_deinit_all() shut down every non-static SPI bus, including the one the active W5500 still needed. The next poll tried to talk to a W5500 on a bus with no clock.

The issue also documented a working board-level workaround, declaring the bus static with MICROPY_HW_SPI_IS_STATIC, and asked the maintainers which upstream approach they preferred.

The Fix in PR #19800

MicroPython maintainer projectgus replied on October 21 with a cleaner version of the proposed approach and invited a pull request, noting that the change is safe because hardware SPI objects are statically allocated, so no heap objects are left invalid after the soft reset. The PR, opened the next day, has three parts:

  • network_wiznet5k.c: a new wiznet5k_get_active_spi() returns the SPI object of the active W5500, and wiznet5k_poll() returns early when the interface is inactive, so no poll touches SPI after teardown.
  • mpnetworkport.c: wiznet5k_spi_is_active() tells the STM32 port whether a given SPI index belongs to the active NIC.
  • spi.c: spi_deinit_all() skips that one bus and still shuts down every other non-static bus.

The core of the fix in ports/stm32/spi.c Five added lines in spi_deinit_all(), rendered from the PR #19800 diff. Source: micropython/micropython, MIT License.

⚙️ Role of the WIZnet W5500

In this setup the W5500 is the board's Ethernet interface. It is wired to hardware SPI1 (PA5, PA6, PA7), with chip select on PC4 and reset on PA3, and it runs in polling mode without the optional interrupt line. MicroPython's lwIP stack on the STM32 handles TCP/IP, and the W5500 moves Ethernet frames between the SPI bus and the cable.

The fix ties the SPI bus lifetime to the W5500's lifetime: as long as the W5500 interface is active, its bus stays clocked through a soft reset; once the interface is deactivated, the bus is shut down as before. For W5500 users this removes a hang that could appear during routine mpremote file copies and script runs. Board makers no longer need a static-SPI override just to make soft reset safe.

WIZnet role in one line: the W5500 is the Ethernet port, and PR #19800 makes sure its SPI link survives the interpreter restarts that MicroPython developers trigger every day.

How the Fix Was Qualified

The PR includes a detailed validation section. On upstream master (b70ae1a), the author tested revision 6d07806 on an STM32F407VE with a W5500 on hardware SPI1, in polling mode, with no static-SPI board override:

  • 20 qualification cycles with 60 ordinary mpremote soft resets, NIC reconstruction, network down/up, nonblocking listener and socket checks, and 20 patterned 4096-byte uploads with readback.
  • Lifecycle checks: the active NIC's SPI clock is kept across soft reset; polling an inactive NIC is safe after explicit SPI teardown; inactive and unowned SPI clocks are still disabled on later resets.
  • Builds with and without WIZnet, SWD programming checks, and a byte-identical application filesystem before and after programming.

The same procedure also passed on the earlier v1.27.0-based candidate. MicroPython's CI code size report shows no size change on the standard boards it tracks.

How the fix was qualified on real hardware Generated summary of the PR #19800 validation section.

Status and Scope

As of October 23, 2026, PR #19800 is open with the port-stm32 label, after the maintainer agreed on the approach in the linked issue. The qualified setup is STM32F407 with a W5500 on hardware SPI. SoftSPI, deliberately deinitializing an active NIC's bus, and reattaching the optional interrupt are outside the qualified scope, and the hardware tests run outside the upstream test suite.

Related WIZnet Maker Projects

If you are new to the W5500 on MicroPython and STM32, MicroPython - STM32 W5500 Ethernet module socket communication shows the basic wiring and socket setup that this fix makes more robust.

For the same kind of driver hardening in another RTOS, Hardening Zephyr's W5500 Driver: An Open-Source Ethernet Stress Test stress-tests the W5500 path in Zephyr, a useful comparison of how each ecosystem validates its Ethernet driver.

For an application built on an STM32 and W5500 data path, How Does W5500 Fit into an STM32 Predictive Maintenance Datalogger? shows where a reliable Ethernet link matters in a real logging project.

❓ FAQ

What does the W5500 do in this project? It is the STM32 board's Ethernet interface. MicroPython's lwIP stack builds the TCP/IP traffic, and the W5500 sends and receives the Ethernet frames over SPI.

Who is affected by the bug? STM32 boards running MicroPython with lwIP and network.WIZNET5K on hardware SPI, without a static-SPI board override, that soft-reset while the W5500 is active, for example during mpremote file copies.

Is there a workaround before the fix is merged? Yes. The issue documents declaring the W5500's SPI bus static in the board definition with MICROPY_HW_SPI_IS_STATIC, which keeps the bus alive through soft reset.

Does the fix keep every SPI bus alive? No. It keeps only the bus owned by the active W5500 interface. Inactive or unused SPI buses are still shut down on soft reset, as before.

Has the fix been merged? As of October 23, 2026 it is an open pull request. A MicroPython maintainer proposed the approach in issue #19790 and asked for the PR, and the author's hardware qualification is posted in the PR.

Documents

  • PR #19800: stm32: Retain active lwIP WIZnet SPI across soft reset

    Original source: the fix and its hardware validation

  • Issue #19790: soft reset hangs when the SPI bus is deinitialized

    Bug report, SWD finding, workaround and maintainer discussion

  • MicroPython network.WIZNET5K documentation

    Driver class used on the STM32 port

  • MicroPython mpremote documentation

    The tool whose routine operations trigger soft reset

  • WIZnet W5500

    WIZnet chip page

Comments

Comments