Overview

This tutorial demonstrates how Mongoose Library can be used to implement a device dashboard runnning on the Zephyr OS. The full source code is at https://github.com/cesanta/mongoose/tree/master/examples/zephyr/device-dashboard

This tutorial is an example following on the device dashboard tutorial. You should read and follow that tutorial first.

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 - Mongoose Library source code
    • src/mongoose.h - Mongoose Library header file
    • src/net.c - the common code for this example
    • src/packed_fs.c - a packed filesystem containing the web files to be served
  • CMakeLists.txt - a standard Zephyr project component, describes what files will be compiled
  • prj.conf - a standard Zephyr project component, contains configuration values
  • Makefile - mainly invokes Docker to call the Zephyr utilities

Build the project

It is assumed that 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 it:

$ docker ps

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 the zephyrproject directory, then you can try other Zephyr examples by just running make build. If you run make zephyr again, it will update the repo, so call it from time to time to keep it up to date.

The default board is an x86 machine that can be simulated with QEMU, see the code README for details on playing with it. To build for a real board we need to specify which board we are going to build for. 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
  • Run make build BOARD=yourboardname.
    $ make build BOARD=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 SHAREUSB (e.g.: make flash SHAREUSB=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
    $ minicom -D /dev/ttyACM0
    

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.

Try it out

Start a browser on http://IP_ADDRESS:8000 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

Main function

As the network is initialized by Zephyr (we configured it for that), the main() function has two blocks:

  1. Wait until the netwok is ready (DHCP has assigned us an IP address)
  2. Run Mongoose

Wait for the network to come up

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:

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:

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_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:
      CONFIG_NET_MAX_CONN=10
      CONFIG_NET_MAX_CONTEXTS=10