Add blog post: Rust smoltcp as an alternative TCP/IP stack for ESP-IDF#742
Add blog post: Rust smoltcp as an alternative TCP/IP stack for ESP-IDF#742DatanoiseTV wants to merge 3 commits into
Conversation
1e509f3 to
db2080a
Compare
…r ESP-IDF New community article under content/blog/2026/06/ covering the smoltcp ESP-IDF components: the linker --wrap BSD-socket compatibility shim, the single-task poll architecture, measured ESP32-P4 throughput (91.15 Mbit/s on a 100 Mbit Ethernet link), and the current limitations. Adds the sylwester-sosnowski author page, data entry, and avatar. Follows the suggestion in espressif/esp-idf#18549.
db2080a to
e5497fe
Compare
david-cermak
left a comment
There was a problem hiding this comment.
Thank you for writing this article! I went through the installation (on ESP32C3) and noticed some inconsistencies, please fix them and treat all other comments as optional suggestions.
I've also noticed that the VFS_SUPPORT_SELECT=n-version is on the main branch in your repo.
| title: "Rust smoltcp as an alternative TCP/IP stack for ESP-IDF" | ||
| date: "2026-06-11" | ||
| summary: "A set of ESP-IDF components that run the Rust smoltcp stack as the IPv4/IPv6 data plane, while keeping esp_http_server, esp-tls and esp-mqtt working without source changes. This article explains the linker --wrap shim that makes it compatible, the single-task poll architecture, the throughput I measured on an ESP32-P4 (91.15 Mbit/s on a 100 Mbit link), and the limitations to be aware of." | ||
| featureAsset: "img/featured/featured-rust.webp" |
There was a problem hiding this comment.
it's usually more engaging if you attach some picture, maybe just a screenshot from the component manager landing on your smaltcp component
There was a problem hiding this comment.
Added a screenshot of the esp_smoltcp registry page in the Getting started section.
| into IRAM so it isn't stalled on flash access. A throughput figure without | ||
| its conditions isn't worth much, so those are the conditions. | ||
|
|
||
| The cost on the build is roughly +80 KiB of code and ~120 KiB of BSS (the |
There was a problem hiding this comment.
It would be also good to compare with the lwip component -- footprint, static mem usage, heap usage.
I've just quickly checked on esp32c3
Archive File ┃ Total Size ┃ DRAM ┃ .bss ┃ .data ┃ .text ┃ Flash Code ┃ .text ┃ Flash Data ┃
libsmoltcp_glue.a │ 108317 │ 24580 │ 0 │ 24580 │ 0 │ 82206 │ 82206 │ 1531 │ 0 │ 1531 │
libwpa_supplicant.a │ 82613 │ 1370 │ 1362 │ 8 │ 0 │ 79698 │ 79698 │ 1545 │ 0 │ 1545 │
libmbedcrypto.a │ 77528 │ 426 │ 252 │ 88 │ 86 │ 70594 │ 70594 │ 6508 │ 0 │ 6508 │
libpp.a │ 68725 │ 20082 │ 1262 │ 2644 │ 16176 │ 44774 │ 44774 │ 3869 │ 0 │ 3869 │
libesp_smoltcp.a │ 53940 │ 50491 │ 49905 │ 0 │ 586 │ 3408 │ 3408 │ 41 │ 0 │ 41 │
liblwip.a │ 48645 │ 969 │ 965 │ 4 │ 0 │ 47430 │ 47430 │ 246 │ 0 │ 246 │
PS: I can maybe help with a quick comparison (temp david-cermak/esp-smoltcp@1f27f52)
There was a problem hiding this comment.
Added a "Footprint vs lwIP" subsection using your ESP32-C3 size-components numbers (credited) — smoltcp ~85.6 KiB flash / ~74.5 KiB static RAM vs lwIP 47.4 KiB / ~1 KiB static. I spelled out the static-vs-heap caveat so the RAM comparison is fair in both directions: smoltcp pre-pays a fixed bounded budget, lwIP allocates from heap under load. A proper runtime heap-watermark comparison under identical load is still on my list — your temp commit looks like a good starting point for that, thanks.
| slab pool plus the Rust scratch). On a P4 that is negligible; on a smaller | ||
| part it is a real consideration. | ||
|
|
||
| ## The `select()` problem, and how the RFC fixed it |
There was a problem hiding this comment.
I think you can skip this part and focus on the actual state of the component (maybe just refer to the GH issue, so readers are aware of the history, but in general they'd be interested in 1) how it works and 2) how they can use it 3) numbers vs lwip)
There was a problem hiding this comment.
Done — replaced the history section with a short present-state caveat (v0.1.x needs VFS_SUPPORT_SELECT=n and why in two sentences, v0.2.0-rc.1 doesn't), with a link to the RFC issue for the background.
|
|
||
| ## Getting started | ||
|
|
||
| The three components are published on the |
There was a problem hiding this comment.
Please also add some instructions to install cargo (not all readers and potential users would know/have it -- running just rustup worked for me)
There was a problem hiding this comment.
Added rustup install instructions to Getting started, plus the required sdkconfig.defaults options that were missing from the article (LWIP_COMPAT_ENABLE, LWIP_NETIF_LOOPBACK, and the v0.1.x VFS_SUPPORT_SELECT=n).
| idf.py add-dependency "datanoisetv/esp_smoltcp_lwip_compat^0.1.0" | ||
| ``` | ||
|
|
||
| - Source, the complete `eth_basic` example, and architecture notes: |
There was a problem hiding this comment.
I noticed two minor errors (on the main branch) worth fixing before publishing the article:
includedir is no-existent: just remove this line https://github.com/DatanoiseTV/esp-smoltcp/blob/main/components/esp_smoltcp/CMakeLists.txt#L29- undefined hostname:
#ifndef CONFIG_APP_HOSTNAME
#define CONFIG_APP_HOSTNAME "espressif"
#endifThere was a problem hiding this comment.
Both fixed, and published as esp_smoltcp_lwip_compat 0.1.1 on the registry (the ^0.1.0 range in the article resolves to it automatically):
- The missing
includedir was actually inesp_smoltcp_lwip_compat(its CMakeLists declaredINCLUDE_DIRS "include"with no such dir — the component's API is purely--wrapsymbols, so the line is simply removed).esp_smoltcpitself does ship itsinclude/with the two public headers, so that one stays. CONFIG_APP_HOSTNAMEnow defaults to"espressif"when undefined, exactly as you suggested — it was a leftover from the original template app's Kconfig. v0.2 replaces it with a proper component-level option (CONFIG_LWIP_COMPAT_HOSTNAME).
Fix commit: DatanoiseTV/esp-smoltcp@f7952ec, tagged v0.1.1. Thanks for actually installing it — both bugs were invisible from inside the template project where the symbols happened to exist.
| @@ -0,0 +1,300 @@ | |||
| --- | |||
| title: "Rust smoltcp as an alternative TCP/IP stack for ESP-IDF" | |||
| date: "2026-06-11" | |||
There was a problem hiding this comment.
Suggest 06-19 to have some time for editorial review
Two bugs broke `idf.py add-dependency` installs into projects other than the original template, both found by @david-cermak in review of the developer-portal article (espressif/developer-portal#742): - CMakeLists declared INCLUDE_DIRS "include" but the component ships no include/ directory (its API is purely --wrap symbols), so CMake configure failed on any fresh checkout. Drop the line. - esp_netif_shim.c used CONFIG_APP_HOSTNAME, a Kconfig symbol defined by the template app, undefined everywhere else. Default it to "espressif" when missing; v0.2 has a proper component Kconfig option instead.
- Move the publish date to 2026-06-19 as suggested - Replace the RFC-history select() section with a short, present-state caveat (v0.1.x needs CONFIG_VFS_SUPPORT_SELECT=n, v0.2 does not) - Add a footprint comparison vs lwIP (idf.py size-components, ESP32-C3), with the static-vs-heap RAM caveat spelled out - Getting started: rustup install instructions, the required sdkconfig.defaults options, and a note about the lwip_compat 0.1.1 packaging fix - Add a screenshot of the component on the ESP Component Registry
|
Thanks for the thorough review — especially for actually running the install, which caught two real packaging bugs. All comments addressed in 3e8e1fe:
The preview at https://preview-developer.espressif.com/pr742/ should reflect it once CI re-runs. |
| tags: ["Rust", "smoltcp", "Networking", "TCP/IP", "ESP-IDF", "ESP32-P4", "lwIP"] | ||
| --- | ||
|
|
||
| ## Why a second TCP/IP stack |
There was a problem hiding this comment.
My suggestion is to change this to Introduction and move Why a second TCP/IP stack to where you introduce smoltcp and change the first sentence.
##Why a second TCP/IP stack
[smoltcp](https://github.com/smoltcp-rs/smoltcp) it's a
`#![no_std]` TCP/IP stack written in Rust, with no heap requirement and a...
There was a problem hiding this comment.
Done — split into an "Introduction" section (lwIP context, the two motivations) and "Why a second TCP/IP stack" opening with the smoltcp description, as you sketched.
| the stack behind your code. | ||
|
|
||
| Now, getting smoltcp to run on an ESP32 is not the hard part. The hard part | ||
| is running it *under ESP-IDF without giving up the IDF networking stack you |
There was a problem hiding this comment.
...is running it *under ESP-IDF without giving up the native networking stack you...
There was a problem hiding this comment.
Done, reworded to "the native networking stack".
|
|
||
| ## How compatibility works: a linker `--wrap` shim | ||
|
|
||
| Every IDF networking component eventually calls BSD sockets — `socket()`, |
|
|
||
| Every IDF networking component eventually calls BSD sockets — `socket()`, | ||
| `bind()`, `listen()`, `accept()`, `send()`, `recv()`, `select()`, | ||
| `getaddrinfo()`. In the IDF, these come from `<lwip/sockets.h>`, and the |
There was a problem hiding this comment.
IDF -> ESP-IDF
Please replace all IDF with ESP-IDF.
There was a problem hiding this comment.
Done — replaced every standalone "IDF" with "ESP-IDF" throughout (prose and the architecture diagram; code identifiers like idf.py untouched).
|
|
||
| ## Performance | ||
|
|
||
| The hardware is a Waveshare ESP32-P4-Nano with the built-in 100 Mbit/s |
There was a problem hiding this comment.
Please add a link to the Waveshare ESP32-P4-Nano product.
There was a problem hiding this comment.
Done, linked to the Waveshare product page.
|
Thank you for your contribution, @DatanoiseTV. |
- Split the opening section into "Introduction" (lwIP context) and "Why a second TCP/IP stack" (smoltcp), as suggested - Replace all standalone "IDF" with "ESP-IDF"; use "native networking stack" where suggested - Link the Waveshare ESP32-P4-Nano product page
|
All of @pedrominatel's editorial comments are addressed in 725c192. On the failing |
Nice, @DatanoiseTV. Don't worry about the CI failing on the link checker for your article. We will check the PR #744. Thank you for that. |
|
🎉 A preview for this PR is available at: https://preview-developer.espressif.com/pr742/ |
📝 Description
A new community article introducing esp-smoltcp — a set of ESP-IDF components that run the Rust smoltcp stack as the IPv4/IPv6 data plane while keeping the IDF networking stack (
esp_http_server,esp-tls,esp_http_client,esp-mqtt, mbedTLS) working without source changes.The article covers:
--wrapBSD-socket shim that redirectslwip_*calls to smoltcp, so application and IDF networking source is unmodified.no_stdRust glue layer.select()/VFS issue and theesp_vfs_register_fd_range()fix that removes the originalCONFIG_VFS_SUPPORT_SELECT=nworkaround.This was written following the suggestion in espressif/esp-idf#18549 (cc @david-cermak), where the
--wrapapproach and the VFS fix were discussed.Ready for technical/editorial review.
Publish date
Expected publish date:
2026-06-19(per review suggestion).Review process
Checks
content/blog/2026/06/rust-smoltcp-network-stack-for-esp-idfdate: 2026-06-19, summary, authors (sylwester-sosnowski, new author page + data entry + avatar), tags.webp, ~11 KB (well under the 500 KB limit)featureAssetreuses the sharedimg/featured/featured-rust.webpstable/latestdocumentation linksmain🔗 Related
🧪 Testing (Hugo)
featureAsset, author entry, and trailing-whitespace were validated against the rules intools/ci/check_article_details.sh.hugo serverbuild; relying on this PR's CI for the Hugo build and author-presence check. Happy to fix anything CI flags.