Custom Firmware

Our flashing tool lowsync (see Getting Started) can be used to build custom firmware images, which include:

These firmware images can be bulk flashed on to devices with lowsync, or be used by a device to update itself. For updating, the firmware can be uploaded to the device in any way, for example via HTTPS.

Example project

You can find a fully featured example project on GitHub.

This example project shows how to

Create a firmware image

To create a firmware image, call:

lowsync build --firmware-config=path-to-configuration-file path-to-output-image

The configuration file must be a file in JSON format, with the following content (values are examples):

    "lowjs": {
        "version": "20200105",              <- "latest" or date in format YYMMDDDD for pinned version
        "pro": true,                        <- true for low.js Professional, false for low.js basic
        "system_flash_size": 8388608        <- how much Flash space to use for firmware, rest is used for file system
        "ide_support": false,               <- include the IDE?
        "ota_update_support": true          <- include Over-The-Air updating support?
    "static_files": "file_system/",         <- null for no static files or path to directory which included static files
    "factory_files": null,                  <- null for no factory files or path to directory which included files to prepopulate the file system on factory reset
    "modules": {                            <- npm modules, specified as in any package.json file
        "ws": "^7.2.1"
    "settings": {                           <- low.js settings to overwrite, see documentation for options
          "code": {
              "main": "/server/index.js",
                  "auto_restart_on_fatal": true,
                  "console_kb": 0,
                  "only_static_files": true
          "wifi": {
                  "ssid": "Custom Firmware Example",
                  "password": "customfirmware"
          "web": {
                  "http_enabled": false,
                  "https_enabled": false

After a build, lowsync outputs the flash usage by the image:

****** Used flash space: ******
low.js code       2097152 bytes
low.js data        791876 bytes
Static files       993970 bytes
Factory files           0 bytes
Modules             53810 bytes
Settings             1155 bytes
OTA support       3937963 bytes
Reserved           512682 bytes
Total             8388608 bytes

Static files vs factory files

Static files are interleaved with the editable file system for*() and fs.stat*() calls, however cannot be changed or deleted. If a file is created in the file system, this is used instead of the static file. Static files are not listed when calling fs.readdir*().

The setting code.main.only_static_files (only used for custom firmware and not documentated on the low.js Settings documentation page) makes low.js only look for code files (everything accessed via require()) in the static files. Static files can be accessed far faster than file system files, so this makes program loading faster. Also, by putting the user code into the static files, with a firmware update the user program gets updated too.

The factory files are placed into the newly formatted file system on every factory reset of the device. Thus, every factory file will need double the Flash space: for the firmware image and for the file system. When updating the firmware, the files in file system are not touched.

Flash firmware image

Flashing is done via:

lowsync flash --port=portname --firmware-file=page-to-firmware-image

portname is the name of the port under which lowsync can connect to the ESP32-WROVER board. Under Windows the port usually begins with COM, under other platforms it is a file beginning with /dev/tty. If you cannot find the port, chances are you need to install a device driver for the USB to UART adapter chip.

This call needs Internet access to sign the low.js code. This feature ensures that the firmware cannot be copied from one microcontroller to the other - the flashed firmware only works on the device it is flashed on with lowsync. None of the static or factory files (together the user files) are transferred to the neonious servers, however.

Over-The-Air updating

The firmware can be transferred to the device in any way.

All the user program has to do is pipe it to a stream created with lowsys.createFirmwareStream() to update the device, and then call lowsys.restart(true) to trigger a microcontroller restart. lowsys.createFirmwareStream() can throw Errors, also make sure to handle 'error' events on the stream (see example project highlighted above).

In this case low.js signs the new firmware itself automatically.

Do not save the file on the file system and then write it at once to the firmware stream, as typically the resources for this (Flash, RAM) are not available.