ESP32: Device Dashboard

Overview

This tutorial demonstrates how Mongoose Library can be used to implement a device dashboard on an ESP32.

device dashboard login

Features of this implementation include:

  • Uses the ESP-IDF framework
  • The Web dashboard provides:
    • User Authentication: login protection with multiple permission levels
    • The web UI is optimised for size and for TLS usage
    • Logged users can view/change device settings
    • The web UI is fully embedded into the firmware binary, and does not need a filesystem to serve it, making it resilient
interactive device dashboard

This example is a hardware adaptation of the Device Dashboard that can run on Mac/Linux/Windows. Mongoose Library, being cross-platform, allows to develop and run the same code on different platforms. That means: all functionality related to networking can be developed and debugged on a workstation, and then run as-is on an embedded device - and this example is a demonstration of that.

Take your time to navigate and study the Device Dashboard tutorial. Here, we concentrate on the features specific to this embedded platform.

This example uses the ESP-IDF 4.x/5.x toolchain, and therefore the project structure follows a standard ESP-IDF CMake-based format:

  • main/ - a "main" component directory which includes application code
    • main/CMakeLists.txt - a standard ESP-IDF component-level project CMake file
    • main/main.c - main application file, contains Mongoose logic
    • main/wifi.c - contains Wifi setup routine, wifi_init()
    • main/mongoose.c, main/mongoose.h - Mongoose Library
    • main/net.c, main/net.h - part of the device dashboard example, contains the Web functionality
    • main/packed_fs.c - part of device dashboard example, embeds the Web UI used by the dashboard
  • CMakeLists.txt - a standard ESP-IDF top-level project CMake file
  • partitions.csv - defines a partition table for the flash
  • sdkconfig.defaults - default build values
  • Makefile - mainly invokes Docker to call the IDF utilities

Build and run

Before building the project, open the main/main.c file and edit your WiFi network name and WiFi password settings:

The next step is to build the project. It is assumed you're using Linux or Mac as a workstation, you have Docker installed, and your user is able to run it. If in doubt, check

$ docker ps

Start a terminal in the project directory; clone the Mongoose Library repo, and run the make build command:

$ git clone https://github.com/cesanta/mongoose
$ cd mongoose/examples/esp32/device-dashboard
$ make build

In order to flash this recently built firmware to your ESP32 board, plug it in a USB port and execute (change /dev/ttyUSB0 to the actual serial port for your module, on your system):

$ make flash PORT=/dev/ttyUSB0

You can also download and unzip esputil, in that case run make flash2 PORT=/dev/ttyUSB0. When done, connect to the serial console. You can use a serial port application like minicom or you can also use esputil, just run esputil -p /dev/ttyUSB0 monitor.

Now start a browser on http://IP_ADDRESS where IP_ADDRESS is the board's IP address printed on the serial console. You should see a login screen.

From here on, please go to the device dashboard tutorial and repeat some of the steps depicted there

Build with TLS support

To build using MbedTLS, just configure Mongoose to enable it, the IDF already builds it for us:

  • In main/CMakeLists.txt, set -DMG_TLS=MG_TLS_MBED

Compilation options

Mongoose supports a number of well-known architectures, among them the ESP32 IDF, which detects automatically. Network operations need a time base to calculate timeouts; this, as well as random number generation, will be provided by the IDF, and Mongoose now knows how to link to them. We set other compilation options in the main/CmakeLists.txt file.

main.c overview

This example can be divided in the following blocks:

  1. Mount a filesystem and write a sample file
  2. Initialise WiFi
  3. Run Mongoose

Mount filesystem

The firmware that has been built does not have a filesystem image flashed, but it has SPIFFS filesystem support. So the following piece of code initialises a filesystem, and writes a file hello.txt using the mg_file_printf() API call.

For this example we've chosen to embed all the web files in a packed filesystem, more information on the embedded filesystem tutorial

Initialise wifi

There we call a wifi_init() function which is defined in main/wifi.c. That is a blocking function, i.e. it does not return until the board gets an IP address:

Run Mongoose

Next goes Mongoose. Note that it should be run after network initialisation is complete - which is true in our case. The logic is standard - initialise the event manager, start a listener, and fall into an infinite event loop:

We have covered those aspects that are specific to the ESP implementation, for the details on the application and the UI, please see the device dashboard tutorial.

Using SPIFFS to store web files

In case we'd like to hold our web files on a partition using the SPIFFS filesystem, here is an example on how to do it.

This event handler function shows a RESTful handler with the /api/state URI, which reports the amount of free RAM. Other URIs are treated as static server requests:

There is a caveat there - we redefine FS handler. This is done because SPIFFS is a flat file system without directory support. Therefore we create our own filesystem which is derived from a standard POSIX FS but has a stat() function redefined - it returns a "directory" flag for /:

Configure for your module

DevkitC boards can have different ESP32-WROOM module options with different ESP32 processors and memory sizes at different speeds.

This example is configured for a bare minimum in order to run in as many devices as possible, if you want to tweak the settings for your device, change the settings in the sdkconfig.defaults file.

M5 STAMP PICO

The M5 STAMP PICO has 4MB flash connected via SPI at 80MHz, with the ESP32-PICO-D4 processor clocked at 240MHz:

# These are specific for ESP32-PICO-D4
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ="80m"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240

XIAO-ESP32-C3

The XIAO-ESP32-C3 has 4MB flash connected via SPI at 80MHz, with the ESP32-C3 processor clocked at 160MHz. It uses a RISC V processor, so these changes are mandatory in order to compile for the right processor architecture:

# These are specific for ESP32-C3(Fx4)
CONFIG_IDF_TARGET_ARCH_RISCV=y
CONFIG_IDF_TARGET="esp32c3"
CONFIG_IDF_TARGET_ESP32C3=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHFREQ="80m"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"

In a Linux system, the usual port name is /dev/ttyACM0, as the module uses the ESP32-C3 built-in USB serial

You will need an antenna connected to the module