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:

  1. In CubeMX, configure debug UART, Ethernet, and RNG (for TLS)
  2. Go to Web UI builder, start a new project, select destination directory where your project lives
  3. Choose the board, dashboard template, click finish
  4. Click generate - do not cleanup the destination. That should create mongoose directory in the project
  5. 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
  )
  1. Edit Core/Src/main.c:
  • Add #include "mongoose_glue.h
  • Add _write() override as described above
  • Add mongoose_init() and mongoose_poll() calls to main()