Zephyr: Device Dashboard
Overview
This tutorial demonstrates how Mongoose Library can be used over the Zephyr OS.
Features of this implementation include:
- 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
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.
Project structure
This example uses the Zephyr freestanding application format, the zephyrproject
directory will be cloned on a separate directory and the project structure inside the example directory will follow this format:
src/
- the directory that includes the application code- src/main.c - main application file, contains Mongoose logic and Zephyr network-ready detection code
src/mongoose.c
,src/mongoose.h
- Mongoose Library
src/net.c
,src/net.h
- part of the device dashboard example, contains the Web functionalitysrc/packed_fs.c
- part of device dashboard example, embeds the Web UI used by the dashboard- CMakeLists.txt - A cmake file that selects the source files and compilation options; a standard Zephyr project component
- prj.conf - a standard Zephyr project component, contains configuration values
- Makefile - mainly invokes Docker to call the Zephyr utilities
Build and run
It is assumed that you have Docker installed, and your user is able to run it. If in doubt, check it:
docker ps
Follow the Build Tools tutorial to setup your development environment.
Start a terminal in the project directory; clone the Mongoose Library repo, and run the
make zephyr
command:git clone https://github.com/cesanta/mongoose cd mongoose/examples/zephyr/device-dashboard make zephyr
- This takes several minutes, but you only need to run
make zephyr
once. It clones the Zephyr repo and populates thezephyrproject
directory, then you can try other Zephyr examples by just runningmake build
. If you runmake zephyr
again, it will update the repo, so call it from time to time to keep it up to date.
- This takes several minutes, but you only need to run
We need to specify for which board we are going to build. You can get a list of the supported boards and their names here, this is a list of the ones we tried:
Board Zephyr name and link to their info STM32 NUCLEO-F207ZG nucleo_f207zg STM32 NUCLEO-F429ZI nucleo_f429zi STM32 NUCLEO-F746ZG nucleo_f746zg STM32 NUCLEO-F756ZG nucleo_f756zg STM32 NUCLEO-F767ZI nucleo_f767zi STM32 NUCLEO-H563ZI nucleo_h563zi STM32 NUCLEO-H723ZG nucleo_h723zg STM32 NUCLEO-H743ZI nucleo_h743zi STM32 NUCLEO-H753ZI nucleo_h753zi STM32 NUCLEO-H755ZI-Q nucleo_h755zi_q/stm32h755xx/m7 STM32 STM32H573I-DK stm32h573i_dk STM32 STM32H735G-DK stm32h735g_disco STM32 STM32H745I-DISCO stm32h745i_disco/stm32h745xx/m7 STM32 STM32H747I-DISCO stm32h747i_disco/stm32h747xx/m7 NXP FRDM-MCXN947 frdm_mcxn947/mcxn947/cpu0 NXP MIMXRT1060-EVK mimxrt1060_evk NXP MIMXRT1064-EVK mimxrt1064_evk Wiznet W5500 Evaluation Pico w5500_evb_pico Run
make build BUILD_ARGS="-b yourboardname"
.make build BUILD_ARGS="-b nucleo_f746zg"
Once the build succeeds, run
make flash
. The Makefile shares the USB bus with the Docker container, this works well with ST-Link and J-Link devices. If this does not work for you, pass the necessary Docker parameters insideDOCKER_ARGS
(e.g.: make flash DOCKER_ARGS=something). If you are using the standard debugger for your board, it will just work. If you are using another option like for example reflashing your ST-Link to a J-Link, you might need to add a--runner
parameter, read the Zephyr documentation.The generated ELF file is at build/zephyr
Now open a second terminal, this will be our serial console where Zephyr and Mongoose will log their activities. Run a serial port software (we use minicom; make sure you configure it at the proper speed, many boards use 115200bps). Check your board/devtool's device name, for ST-Link and J-Link it is usually
/dev/ttyACM0
; in Linux, for example, runminicom -D /dev/ttyACM0
.Reset your board, you should see Zephyr and Mongoose logs:
[00:00:00.020,000] <inf> net_config: Running dhcpv4 client... 1f0e 2 main.c:33:main Mongoose version : v7.9 [00:00:07.930,000] <inf> net_dhcpv4: Received: 192.168.69.242 [00:00:07.940,000] <inf> net_config: IPv4 address: 192.168.69.242 [00:00:07.940,000] <inf> net_config: Lease time: 21600 seconds [00:00:07.940,000] <inf> net_config: Subnet: 255.255.255.0 [00:00:07.940,000] <inf> net_config: Router: 192.168.69.1 2828 2 mongoose.c:4060:sntp_cb 2 got time: 1678300553311 ms from epoch
Start a browser on
http://IP_ADDRESS:8000
whereIP_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
Cleaning up
- You can run
make clean
to clean up the build files but keep the configuration files, which speeds the next build - If you do major changes (e.g.: compiling for a different board), run
make pristine
to clean up everything under the build directory.
main.c overview
As the network is initialized by Zephyr (we configured it for that), the main()
function has two blocks:
- Wait until the netwok is ready (DHCP has assigned us an IP address)
- Run Mongoose
Zephyr will start calling our main() function right from the start, so we need to wait until we have a valid address before opening sockets.
We declare a semaphore and write a simple event handler that will be called by the Zephyr network manager. This function will release the semaphore.
On our main() function then we register that callback function and block on the semaphore:
Init and run Mongoose
Next goes Mongoose. As we commented, it should be run after network initialization is complete. The logic is standard; we initialize the event manager, start a listener on port 8000, and fall into an infinite event loop:
In this case, the listener is started by web_init()
, the device dashboard initialization function. The URL is configured by the macro HTTP_URL
.
For this example we've chosen to embed all the web files in a packed filesystem, more information on the embedded filesystem tutorial
We have covered those aspects that are specific to the Zephyr implementation, for the details on the application and the UI, please see the device dashboard tutorial.
Configuring Zephyr
- Mongoose needs a large stack, allocates memory to perform its tasks, and interacts with Zephyr through its socket API. The following is a set of configuration options that generally apply to most Mongoose examples:
CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POLL_MAX=32 CONFIG_POSIX_MAX_FDS=32 CONFIG_NET_CONNECTION_MANAGER=y CONFIG_ISR_STACK_SIZE=2048 CONFIG_MAIN_STACK_SIZE=8192 CONFIG_IDLE_STACK_SIZE=1024 CONFIG_MINIMAL_LIBC_RAND=y CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=32768
- Since in this example we'll be using several connections simultaneously, we need to tell Zephyr so. Even though we are using the socket interface, we need to set the configuration options that increase the number of connections (and contexts) available:
CONFIG_NET_MAX_CONN=10 CONFIG_NET_MAX_CONTEXTS=10
Configuring Mongoose
Network operations need a time base to calculate timeouts; this, as well as random number generation, will be provided by Zephyr, and Mongoose needs to know how to link to them. We define these and other compilation options in mongoose_config.h
#define MG_ARCH MG_ARCH_ZEPHYR
- configures Mongoose to work with Zephyr#define MG_ENABLE_PACKED_FS 1
- enables the embedded filesystem support