Add TCP/IP stack to STM32
This guide shows how to add a TCP/IP stack to an existing STM32 firmware project. The example uses STM32 Cube and CubeMX, which is the most common STM32 workflow.
The target board is Nucleo-H723ZG with built-in Ethernet, but the same integration approach applies to other STM32 devices and networking hardware.
Mongoose provides a built-in TCP/IP stack. In this guide, we integrate it into an STM32 firmware project and run a minimal HTTP server.
Configure peripherals
Open CubeMX and make sure to configure the following peripherals:
- In Pinout & Configuration → Security, enable RNG
- In Pinout & Configuration → Connectivity, enable USART in Asynchronous mode and configure the TX and RX pins. See board pinouts for correct pins
- In Pinout & Configuration → Connectivity, enable ETH in RMII mode and configure the Ethernet pins. Again, see board pinouts for correct pins
NOTE: Do not enable Ethernet IRQ handler. Mongoose has its own Ethernet driver
Once done, click "Generate Code" to regenerate UART/RNG/ETH setup code for your firmware.
Copy Mongoose files
Mongoose is distributed as two core source files. The easiest way to integrate it is to copy them directly into your project.
Create a mongoose/ directory in the project root:
project/
... your project files ...
mongoose/
mongoose_config.h
mongoose.c
mongoose.h
Download mongoose.c and
mongoose.h from the Mongoose repository and create mongoose_config.h:
#define MG_ARCH MG_ARCH_CUBE
MG_ARCH_CUBE enables the STM32 Cube environment.
Update the linker script
On STM32Fxx series, you can skip this section. On STM32Hxx series, read on.
In a STM32CubeMX project, a linker script is a .ld file that defines how your
firmware is placed in memory (Flash, RAM sections), and you can find it in your
project folder (typically Core/Startup/ or project root).
Update the linker script and define the .eth_ram section:
For STM32H7xx:
.eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > ROM
The Ethernet MAC uses a DMA controller to transfer data. DMA buffers may be placed in different memory regions. It is important to place them into the DMA-accessible region.
The Ethernet driver places DMA buffers into the .eth_ram
ELF section. Therefore, in the linker script, the .eth_ram
section must be placed in a memory region that is accessible
by the Ethernet DMA controller.
If this is not done, .eth_ram ends up in the same region as .bss,
which might not be accessible by the Ethernet DMA. The memory regions
and their accessibility are described in the reference manual.
Create a minimal HTTP server
Open Core/Src/main.c. Include Mongoose header at the top:
#include "mongoose.h"
Before the main() function, add the following two functions:
// Redirect `printf()` to UART so debug output is visible:
int _write(int fd, unsigned char *buf, int len) {
HAL_UART_Transmit(&huart3, buf, len, HAL_MAX_DELAY);
return len;
}
// HTTP server event handler
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/hello"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%d}\n", MG_ESC("status"), 1);
} else {
mg_http_reply(c, 200, "", "hello world\n");
}
}
}
Modify your main() function main loop - before the loop, add
Mongoose instance initialisation, and inside the loop, add mg_mgr_poll() call:
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
while (1) {
mg_mgr_poll(&mgr, 0);
The mg_mgr_poll() processes packets, parses protocols, and calls your event handler when something happens.
Build and run
Add the mongoose/ directory to the project include paths and build the firmware.
Open a serial console. Mongoose prints the assigned IP address during startup. Open that address in a browser and the board should reply with:
hello world
At this point your STM32 firmware has a working TCP/IP stack and HTTP server powered by Mongoose.
Using Web UI builder
Here is a quick step-by-step guide to add Mongoose with a web dashboard to an existing CubeMX-based project:
- In CubeMX, configure debug UART, Ethernet, and RNG (for TLS)
- Go to Web UI builder, start a new project, select destination directory where your project lives
- Choose the board, dashboard template, click finish
- Click generate - do not cleanup the destination. That should create
mongoosedirectory in the project - Modify CMakeLists.txt:
- Add mongoose/*.c files to source
- Add mongoose/ to includes
- Add this snippet to generate .bin:
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O binary
${PROJECT_NAME}.elf
${PROJECT_NAME}.bin
)
- Edit Core/Src/main.c:
- Add
#include "mongoose_glue.h - Add
_write()override as described above - Add
mongoose_init()andmongoose_poll()calls tomain()