Apps and libraries

A Mongoose OS app is a firmware that does something specific. It could be built and flashed on a microcontroller. For example, a blynk app is a firmware that makes a device controllable by the Blynk mobile app.

Another example is a default app that gets flashed when you press a "Flash" button on a Web UI "device control" dialog, or type mos flash <arch> on a terminal. That default app blinks an LED periodically, can talk to an MQTT server, and allows user to extend the logic by editing JavaScript code directly on a device filesystem.

An app can use any number of libs. A lib is a reusable library. It cannot be built directly into a working firmware, because it only provides an API but does not actually use that API. An app can include a lib by listing it in the libs: section of the mos.yml file. mos build command generates code that calls library initialisation functions. Libraries are initialised in the order of their reference.

Local and remote builds

By default, a mos build command that builds an app's firmware, is using so-called remote build - it packs apps's sources and sends them over to the Mongoose OS build machine. This is the default behavior, cause it does not require a Docker installation on the workstation.

However, if a Docker is installed, then it is possible to build locally. This is done by adding an extra --local flag (see below). In this case, everything is done on the local machine. This is a preferrable option for the automated builds, and for those who do not want their sources leaving their workstations. Summary:

Build type Build command
Remote (default)
mos build --platform PLATFORM
Local (requires Docker)
mos build --platform PLATFORM --local --verbose

mos.yml file format reference

mos.yml file drives the way Mongoose apps are built. Below is a description of the sections (keys) in this file. Libraries also have mos.yml files, the only difference with apps is that they have type: lib key and they cannot be built into a firmware. So the following applies to both apps and libraries.

author

A string, FirstName SecondName <Email> of the author, example:

author: Joe Bloggs <joe@bloggs.net>

build_vars

List of Makefile variables that are passed to the architecture-specific Makefile when an app is getting built. See next section for a build process deep-dive. An example of arch-specific Makefile is: platforms/esp32/Makefile.build. The others are in the respective directories: fw/platforms/*/Makefile.build.

The example below changes ESP32 SDK configuration by disabling brownout detection:

build_vars:
  ESP_IDF_SDKCONFIG_OPTS: "${build_vars.ESP_IDF_SDKCONFIG_OPTS} CONFIG_BROWNOUT_DET="

Another example is the dns-sd library that enables DNS-SD:

build_vars:
  MGOS_ENABLE_MDNS: 1

binary_libs

A list of .a libs or directories with those. Do not put trailing slashes to directory names:

binary_libs:
  - mylib/mylib.a

cdefs

Additional preprocessor flags to pass to the compiler, example:

cdefs:
  FOO: BAR

That gets converted into the -DFOO=BAR compilation option, for both C and C++ sources.

cflags, cxxflags

Modify compilation flags for C (cflags) and C++ (cxxflags). For example, by default warnings are treated as errors. This setting ignores warnings when compiling C code:

cflags:
  - "-Wno-error"

If what you're after is defining preprocessor variables, cdefs makes it easier. This snippet:

cdefs:
  FOO: BAR

Is the same as:

cflags:
  - "-DFOO=BAR"
cxxflags:
  - "-DFOO=BAR"

config_schema

This can define a new configuration section for the device, and also override a previosly defined configuration entries defined elsewhere. For example, the following snippet defines a new section foo and overrides a default value of mqtt.server set by the mqtt library:

config_schema:
  - ["foo", "o", {title: "my app settings"}]
  - ["foo.enable", "b", true, {title: "Enable foo"}]
  - ["mqtt.server", "1.2.3.4:1883"]

description

A string, one-line short description, example:

description: Send BME280 temperature sensor readings via MQTT

filesystem

A list of files or directories with files to be copied to the device's filesystem, example:

filesystem:
  - fs
  - other_dir_with_files
  - foo/somepage.html

includes

A list of directories with C/C++ include files. Do not put trailing slash to the directory name. Example:

includes:
  - my_stuff/include

libs

Library dependencies. Each library should have an origin and optionally can have name and version. origin is a GitHub URL, like https://github.com/mongoose-os-libs/aws (note: it must be a repo with mos.yml in the repo root!).

Name is used to generate the code which calls library initialization function: e.g. if the lib name is mylib, it should have the function bool mgos_mylib_init(void). Also, for local builds, name is used as a directory name under deps: that's where mos clones libraries.

version is a git tag name, or branch name, or SHA of the library's repository. If omitted, it defaults to the libs_version in mos.yml, which, in turn, defaults to the mos tool version. So e.g. if the mos tool version is 1.21, then by default it will try to use libs with the tag 1.21. Latest mos will use the master branch.

Example:

libs:
    # Use aws lib on the default version
  - origin: https://github.com/mongoose-os-libs/aws

    # Use aws lib on the version 1.20
  - origin: https://github.com/mongoose-os-libs/aws
    version: 1.20

    # Use the lib "mylib" located at https://github.com/bob/mylib-test1
  - origin: https://github.com/bob/mylib-test1
    name: mylib

name

Override app or lib name. By default, the name is set equal to the directory name.

name: my_cool_app

sources

A list of C/C++ source files or directories with those. Do not put trailing slashes to directory names:

sources:
  - src
  - foo/bar.c

tags

A list of free-form string tags, used for Web UI search. Some tags are predefined, they place the app or library in a certain category. Those predefined tags are: cloud (cloud integrations), hardware (hardware peripherals or API), remote_management (remote management), core (core functionality). Example:

tags:
  - cloud
  - JavaScript
  - AWS

Build process deep dive

When mos build [FLAGS] command is executed in the app directory, the following happens:

  • mos scans libs: section of the mos.yml file and imports all libraries into the libs directory (~/.mos/libs, could be overridden by --libs-dir ANOTHER_DIR flag)

  • Each library also has mos.yml file, and a library could have a libs: section as well - this way the library can depend on other library. mos imports all dependent libraries too, recursively.

  • When all required libraries are imported, mos executes git pull in each of them, in order to update. That could be switched off by --no-libs-update flag.

  • At this point, all required libraries are imported and updated.

  • mos combines app's mos.yml file together with the mos.yml files of all dependent libraries, merging them into one file. The order of merging is this: if my-app depends on library lib1, and library lib1 depends on library lib2, then result_yml = lib2/mos.yml + lib1/mos.yml + my-app/mos.yml. Meaning, the application's mos.yml has the highest priority.

  • If --local --verbose --repo PATH/TO/MONGOOSE_OS_REPO flag is specified, then mos starts a local build by invoking docker.cesanta.com/ARCH-build docker image. That image encapsulates a native SDK for the given architecture together with Mongoose OS sources, https://github.com/cesanta/mongoose-os. mos tool invokes make -f fw/platforms/ARCH/Makefile.build for the given platform. The result of this docker invocation is a build/ directory with build artifacts and build/fw.zip firmware zip file which could be flashed to the device with mos flash command.

  • If --local flag is not specified, packs source and filesystem files and sends them to the Mongoose OS cloud build backend at http://mongoose.cloud, which performs an actual build as described in the previous step, and sends back a build/ directory with built build/fw.zip and artifacts.

  • Generated artifacts in the build/ directory is as follows:

build/fw.zip  - a built firmware
build/fs      - a filesystem directory that is put in the firmware
build/gen     - a generated header and source files

How to create a new library

  • The best way to develop a new library is as part of an app development. In your app, do a local build, which creates a deps/ directory. That is the directory where you should place your new library.

  • Clone an empty library, which is a skeleton for the new library, into the deps/mylib directory (change mylib to whatever name you want): git clone https://github.com/mongoose-os-libs/empty deps/mylib

  • Create include/mgos_mylib.h and src/mgos_mylib.c files in your library:

    mgos_mylib.c:

#include "mgos_mylib.h"

// NOTE: library init function must be called mgos_LIBNAME_init()
bool mgos_mylib_init(void) {
  return true;
}

mgos_mylib.h:

#include "mgos.h"
  • You can add your library-specific API to mgos_mylib.h and implementation in mgos_mylib.c.

  • In your app's mos.yml file, add a reference to the new library:

    libs:
    - name: mylib
    
  • Click on build button to build an app, and flash button to flash it

  • Edit library source files mylib/src, build myapp until a test app works as intented.

How to port an Arduino library

Contributing an app or library

If you would like to share your project with a community and publish it under the Apache 2.0 license, please follow these steps:

  • Build your app as described in the previous section, flash and test it.
  • Modify mos.yml, set author field as Your Name <your@email.address>.
  • Make sure you have a descriptive README.md file.
  • If this is a library:
  • Start a new discussion on forum with a subject New contribution: ..., show a link to your code on GitHub / Bitbucket / whatever, or attach a zip file with the app sources.
edit this doc