Zephyr: Device Dashboard
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.
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.h- Mongoose Library
src/net.h- part of the device dashboard example, contains the Web functionality
src/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:
- 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
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 zephyronce. It clones the Zephyr repo and populates the
zephyrprojectdirectory, then you can try other Zephyr examples by just running
make build. If you run
make zephyragain, 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-F746ZG nucleo_f746zg STM32 Nucleo-F429ZI nucleo_f429zi
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 inside
DOCKER_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
--runnerparameter, 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, run
minicom -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
IP_ADDRESSis 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
- You can run
make cleanto 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 pristineto clean up everything under the build directory.
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
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.
- 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_MINIMAL_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: