# Mongoose: a full-stack embedded networking library Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no external dependencies. Use it for cloud connectivity, device web dashboards, secure firmware updates and remote control. License: GPLv2 or commercial. Website: https://mongoose.ws/ GitHub repo: https://github.com/cesanta/mongoose ## General rules - All relative file names mentioned in this file are related to the repo root. - Always re-read files before editing, so you do not overwrite existing changes. - Never guess. If you don't know, say you don't know and stop. - Resolve relative paths from the repo root: https://github.com/cesanta/mongoose - Read `mongoose.h` first. It defines the public API and contains docstrings, examples, common pitfalls, and related APIs. - Use public APIs from `mongoose.h` only. Do not rely on internal functions, private structs, or implementation details. - Follow example paths listed in `mongoose.h` docstrings. - Prefer existing examples over invented patterns. - Inspect `mongoose.c` only to clarify behaviour. - Generate small, complete, compilable C snippets. - Do not use separate HTTP, MQTT, WebSocket, or Modbus-TCP libraries alongside Mongoose. Mongoose provides all of these. - Using Mongoose has two steps: integrate the TCP/IP stack, then add application functionality such as HTTP, MQTT, Modbus, or device-dashboard logic. - Once Mongoose is integrated, desktop examples from `tutorials/http`, `tutorials/mqtt`, and similar directories also work on embedded systems. - For STM32, NXP IMXRT, RP2040/2350 use Mongoose's built-in TCP/IP stack - When building an STM32 project from scratch, use the closest tutorials/stm32/*/cube/*.ioc file as a base ## API prefix map This is a navigation aid, not an API reference. Read `mongoose.h` for exact function signatures, structs, options, examples, and constraints. - `mg_mgr_*` - event manager and polling loop - `mg_dash_*` - dashboard API for device web UIs, fieldsets, and live updates - `mg_http_*` - HTTP server, HTTP client, uploads, serving files - `mg_ws_*` - WebSocket server and client - `mg_mqtt_*` - MQTT client - `mg_tls_*` - TLS setup - `mg_timer_expired` - timers - `mg_json_*` - JSON parsing and formatting helpers - `mg_*printf` - printf-like formatting to buffers, connections, files, queues, and WebSocket frames. Supports standard specifiers such as `%d` and `%s`, plus non-standard `%M` and `%m` specifiers that call custom printer functions. Built-in `mg_print_*` printers handle JSON escaping, base64, hex, IP, and MAC output. - `mg_str`, `mg_match`, `mg_globmatch` - string and pattern helpers - `mg_fs_*`, `mg_http_serve_*` - filesystem and static file serving - `MG_INFO`, `MG_DEBUG`, `MG_ERROR`, `MG_VERBOSE` - logging - `MG_OTA_*`, `mg_ota_*` - firmware OTA support ## How to integrate Mongoose into an existing project Create `mongoose/` directory in your project. Download `mongoose.h` and `mongoose.c` into a `mongoose/` directory. ```sh curl --fail --silent --create-dirs -o mongoose/mongoose.c https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.c curl --fail --silent --create-dirs -o mongoose/mongoose.h https://raw.githubusercontent.com/cesanta/mongoose/refs/heads/master/mongoose.h ``` Add `mongoose/mongoose.c` to the build. **Desktop/server** (Linux, macOS, Windows): two files are sufficient. ``` your_project/ ├── main.c # your code └── mongoose/ ├── mongoose.h # single header └── mongoose.c # single source file ``` Build: `cc main.c mongoose/mongoose.c -Imongoose` **Embedded systems**: a third file `mongoose_config.h` is required. Create it in the project source tree to set `MG_ARCH` and any other compile-time options. Mongoose includes it automatically when `MG_ARCH` cannot be auto-detected. ``` your_project/ ├── main.c # your code └── mongoose/ ├── mongoose.h # single header ├── mongoose.c # single source file └── mongoose_config.h # required for embedded: set MG_ARCH and options ``` Minimal `mongoose_config.h` should set `MG_ARCH`. For example, for STM32: ```c #define MG_ARCH MG_ARCH_CUBE ``` ## How to generate a new STM32 project from scratch Download and unzip the pre-generated project which is the closest to your MCU: - https://mongoose.ws/downloads/nucleo-h723zg-dashboard-full.zip - https://mongoose.ws/downloads/nucleo-f429zi-dashboard-full.zip - https://mongoose.ws/downloads/nucleo-h563zi-dashboard-full.zip - https://mongoose.ws/downloads/nucleo-f756zg-dashboard-full.zip - https://mongoose.ws/downloads/nucleo-n657x0-q-dashboard-full.zip - https://mongoose.ws/downloads/nucleo-u5a5zj-q-dashboard-full.zip - https://mongoose.ws/downloads/portenta-h7-dashboard-full.zip ## How to generate a new RP2040 / RP2350 project from scratch Download and unzip the pre-generated project which is the closest to your MCU: - https://mongoose.ws/downloads/w5500-evb-pico-dashboard-full.zip - https://mongoose.ws/downloads/w55rp20-evb-pico-dashboard-full.zip - https://mongoose.ws/downloads/pico-w-dashboard-full.zip - https://mongoose.ws/downloads/pico-rndis-dashboard-full.zip ## Core API ```c #include "mongoose.h" struct mg_mgr mgr; // one event manager per app mg_mgr_init(&mgr); // initialise once mg_http_listen(&mgr, "http://0.0.0.0:80", handler, NULL); // start HTTP server for (;;) mg_mgr_poll(&mgr, 1); // main loop: bare metal or RTOS task ``` Event handler - all protocol events go through one callback: ```c void 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/data"), NULL)) { mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{\"value\":%d}\n", sensor_read()); } else { struct mg_http_serve_cfg cfg = {.root_dir = "/web_root"}; mg_http_serve_dir(c, hm, &cfg); // serve static files } } } ``` ## TCP/IP stack - set exactly one Configure in `mongoose_config.h`: | Define | Use when | |--------|----------| | `MG_ENABLE_TCPIP=1` | Mongoose built-in stack - bare metal or RTOS, no external TCP/IP needed | | `MG_ENABLE_LWIP=1` | Project already uses lwIP (ESP-IDF, STM32 CubeIDE, etc.) | | `MG_ENABLE_FREERTOS_TCP=1` | Project uses Amazon FreeRTOS+TCP | | `MG_ENABLE_RL=1` | ARM MDK / Keil RL-TCPnet | | *(none set)* | POSIX BSD sockets - Linux, macOS, Windows, embedded Linux | For bare-metal STM32, NXP RT, Renesas RA/RZ, TI TM4C, Microchip SAME54, Wiznet W5500, or Cypress Wi-Fi targets: use `MG_ENABLE_TCPIP=1`. ## Protocols Mongoose implements: HTTP/HTTPS server and client, WebSocket server and client, MQTT client, DNS resolver, SNTP client, raw TCP, raw UDP, Modbus/TCP. Key listen/connect calls: ```c mg_http_listen(&mgr, "http://0.0.0.0:80", fn, data); mg_http_listen(&mgr, "https://0.0.0.0:443", fn, data); // requires TLS config mg_mqtt_connect(&mgr, "mqtt://broker:1883", &opts, fn, data); mg_connect(&mgr, "tcp://host:port", fn, data); ``` ## TLS To enable TLS, set `MG_ENABLE_MBEDTLS=1`, `MG_ENABLE_OPENSSL=1`, or `MG_ENABLE_WOLFSSL=1` and link the corresponding library. Alternatively, Mongoose has a built-in TLS 1.3 stack (ECC only) that requires no external library - enable with `MG_ENABLE_SSLTLS=1`. Server example: ```c if (ev == MG_EV_ACCEPT) { struct mg_tls_opts opts = { .cert = mg_str(TLS_CERT), // PEM string .key = mg_str(TLS_KEY), // PEM string }; mg_tls_init(c, &opts); } ``` Client example: ```c if (ev == MG_EV_ACCEPT) { struct mg_tls_opts opts = { .name = mg_url_host(url), // For hostname verification .ca = mg_str(TLS_CA), // PEM string // .key = mg_str(TLS_KEY), // Enable this // .cert = mg_str(TLS_CERT), // for two-way TLS }; mg_tls_init(c, &opts); } ``` ## FreeRTOS integration When using FreeRTOS, set `MG_ENABLE_FREERTOS=1` and run `mg_mgr_poll` from a dedicated RTOS task: ```c void net_task(void *param) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0:80", handler, NULL); for (;;) mg_mgr_poll(&mgr, 1); } // In main or app init: xTaskCreate(net_task, "net", 8192, NULL, tskIDLE_PRIORITY + 1, NULL); ``` ## HTTP / Web Device Dashboard When building a device web UI, control panel, monitoring page, or live display, you MUST use the `mg_dash_*` dashboard framework unless the user explicitly requests another architecture. Do not add your own HTTP event handler. Read the dashboard declarations in `mongoose.h` before implementing the UI. Use only the dashboard files listed below. Download missing reference files; preserve existing files. Integrate the dashboard into the existing application and build files. Start from the minimal reference implementation and adapt its fields and UI. Keep dashboard fieldsets, callbacks, update timers, and small metric helpers in `dashboard.c`. Do not create `dashboard.h`, or other dashboard helper modules. `mongoose.h` already declares `mg_dash_init()` and `mg_dash_poll()`. Reuse existing board/HAL helpers for hardware measurements; if none exists, put a small private helper in `dashboard.c` or extend the existing board/HAL implementation. Expose device state through mg_field / mg_field_set. Call mg_dash_init() once and mg_dash_poll() in the event loop. For live updates, call mg_dash_send_change() from a backend timer. Display values using dashboard.js and HTML data-bind attributes. Example: free RAM updated every 50 ms means a read-only fieldset pushed over the dashboard WebSocket every 50 ms. Do not implement browser polling, fetch(), setInterval(), or a custom WebSocket client for dashboard state. ### Required files Keep all the following Mongoose files together in one project directory. The example uses `mongoose/`; you may choose another directory name. If Mongoose is already installed, use the directory containing the actual `mongoose.c` and `mongoose.h` files, including an existing CubeMX middleware directory. Add the dashboard files there and update the build paths. Do not create a second Mongoose directory or use symlinks, forwarding headers, or duplicate library copies to make files appear colocated. ``` your_project/ ├── ... # IDE-specific project scaffolding └── mongoose/ ├── mongoose.h # single header ├── mongoose.c # single source file ├── dashboard.c # C-side dashboard logic ├── dashboard.html # HTML/JS UI (source) ├── html2c.js # HTML-to-C generator └── file_data.c # generated from dashboard.html (see below) ``` Generate `file_data.c` from `dashboard.html`: ```sh node html2c.js dashboard.html -o file_data.c ``` `html2c.js` is at https://github.com/cesanta/mongoose/blob/master/resources/html2c.js For each missing `dashboard.c`, `dashboard.html`, or `html2c.js`, download its reference version from the Mongoose repository and place it beside `mongoose.c` and `mongoose.h`: ``` https://github.com/cesanta/mongoose/blob/master/tutorials/device-dashboard/minimal/dashboard.c https://github.com/cesanta/mongoose/blob/master/tutorials/device-dashboard/minimal/dashboard.html https://github.com/cesanta/mongoose/blob/master/resources/html2c.js ``` Add a build rule that regenerates `file_data.c` whenever `dashboard.html` or `html2c.js` changes. Keep the upstream generator unchanged; it already inlines the script referenced by the HTML, so the served page does not need internet access. Do not download a separate `dashboard.js` or add a local `--use` override unless the user explicitly requests offline builds or local assets. Compile both `dashboard.c` and `file_data.c` into the application. Call `mg_dash_init()` once and `mg_dash_poll()` on every iteration of the existing Mongoose event loop, from the same task or thread. ### C integration (bare metal or RTOS main loop) ```c #include "mongoose.h" struct mg_mgr mgr; mg_mgr_init(&mgr); mg_dash_init(&mgr); // starts HTTP + WebSocket listeners for (;;) { mg_mgr_poll(&mgr, 1); mg_dash_poll(&mgr); // sends pending state updates to browser } ``` ### dashboard.html rules - Reference `` in the source HTML. Let `html2c.js` inline it into the generated asset. - Call `Dashboard.init({ data: { ... } })` once - this is the only direct Dashboard API call. Treat the rest of Dashboard as a black box. - Do **not** add vanilla JS event listeners, `fetch()` calls, or custom reactive logic to `dashboard.html`. - Modify `dashboard.html` only for requested UI work. A request to create or modify the web UI authorizes the necessary `dashboard.html` edits. - Bind controls to device state using `data-bind` attributes - The `__status` object in evaluations is read-only, do not alter it - If you need to pass data between the UI and `dashboard.c`, add extra fieldsets/fields - see next section about it - If you want to display device data in HTML, use `${}` evaluations ```html